Files
pixelheros/assets/script/game/map/MissEquipComp.ts
pan 0b579f695c refactor(map): 重构装备卡槽管理与节点挂载逻辑
1.  移除HeroBoxComp中未使用的ebars_node属性
2.  重构EBox.load方法,支持自定义父节点并更新注释
3.  重构MissEquipComp:
    - 移除无用的BoxSet导入和硬编码槽位坐标
    - 使用ebars_node子节点动态生成槽位
    - 修改装备挂载逻辑,将装备挂载到对应ebar子节点而非直接设置位置
    - 更新相关注释与日志提示
4.  调整mission.prefab的UI布局参数与绑定节点
2026-08-06 16:52:32 +08:00

204 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file MissEquipComp.ts
* @description 场上装备卡槽位管理器组件UI 视图层)
*
* 职责:
* 1. 管理场上已使用的 **装备卡** 的可视化槽位(最多 10 个)。
* 2. 监听 UseEquipCard 事件,当玩家使用装备卡时实例化 EquipBoxComp 并放入空闲槽位。
* 3. 监听 RemoveEquipBox 事件,当装备生效完毕后回收槽位并重新排列。
*
* 关键设计:
* - slots 数组预定义了 10 个固定坐标位2 行 × 5 列s
* 每个槽位记录是否占用及对应节点引用。
* - 当某个 EquipBox 销毁时,触发 rearrangeSlots 将剩余节点
* 紧凑地重排到前置槽位,避免视觉空洞。
* - EquipBox 的实例化使用 equip_box Prefab在编辑器中绑定。
*
* 依赖:
* - EquipBoxCompEquipBoxComp.ts—— 单个装备卡的效果控制组件
* - GameEvent.UseEquipCard —— 装备卡使用事件
* - GameEvent.RemoveEquipBox —— 装备卡移除事件
* - smc.map.MapView.scene.entityLayer —— 装备节点的父容器EQUIP 节点)
*/
import { mLogger } from "../common/Logger";
import { _decorator, Node, Prefab, Vec3 } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { EBox } from "./EBox";
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
const { ccclass, property } = _decorator;
/** 装备槽位占用状态 */
interface EquipSlot {
/** 是否已被占用 */
used: boolean;
/** 占用该槽位的节点引用 */
node: Node | null;
}
/**
* MissEquipComp —— 场上装备卡槽位管理器
*
* 在战斗场景中管理已激活的装备卡显示位置。
* 最多 6 个槽位(由外部传入的 ebars_node 子节点 ebar 提供),不足时提示已满。
*/
@ccclass('MissEquipComp')
@ecs.register('MissEquipComp', false)
export class MissEquipComp extends CCComp {
/** 调试日志开关 */
private debugMode: boolean = true;
/** 当前活跃实例(供其他系统查询槽位占用,避免直接 find 强引用) */
private static instance: MissEquipComp | null = null;
/**
* 查询场上装备槽是否已全部占用。
* @returns 无空闲槽位时返回 true实例不存在时返回 false
*/
public static isFull(): boolean {
const inst = MissEquipComp.instance;
if (!inst) return false;
return inst.slots.every(slot => slot.used);
}
/** 装备卡 Prefab在编辑器中赋值 */
@property({ type: Prefab })
private equip_box: Prefab = null;
/** 装备槽位容器(子节点为 6 个 ebar每个装备实例化为一个 ebar 的子节点) */
@property({ type: Node })
private ebars_node: Node = null;
/** 槽位占用状态(与 ebars_node 子节点一一对应,最多 6 个) */
private slots: EquipSlot[] = [];
/** 注册事件监听 */
onLoad() {
MissEquipComp.instance = this;
// 初始化槽位占用状态(与 ebars_node 子节点一一对应)
if (this.ebars_node) {
this.slots = this.ebars_node.children.map(() => ({ used: false, node: null }));
}
oops.message.on(GameEvent.UseEquipCard, this.onUseEquipCard, this);
oops.message.on(GameEvent.RemoveEquipBox, this.onRemoveEquipBox, this);
}
/** 移除事件监听 */
onDestroy() {
super.onDestroy();
if (MissEquipComp.instance === this) {
MissEquipComp.instance = null;
}
oops.message.off(GameEvent.UseEquipCard, this.onUseEquipCard, this);
oops.message.off(GameEvent.RemoveEquipBox, this.onRemoveEquipBox, this);
}
/**
* 处理装备卡移除事件:
* 1. 在 slots 中找到对应节点并释放。
* 2. 调用 rearrangeSlots 紧凑重排。
*
* @param event 事件名
* @param args 要移除的节点引用
*/
private onRemoveEquipBox(event: string, args: any) {
const node = args as Node;
let removed = false;
for (let i = 0; i < this.slots.length; i++) {
if (this.slots[i].node === node) {
this.slots[i].used = false;
this.slots[i].node = null;
removed = true;
break;
}
}
if (removed) {
this.rearrangeSlots();
}
}
/**
* 紧凑重排:将所有有效节点按顺序移到前置 ebar 槽位。
* 确保视觉上不会出现中间空洞。
*/
private rearrangeSlots() {
if (!this.ebars_node || !this.ebars_node.isValid) return;
// 收集所有有效节点
const validNodes: Node[] = [];
for (let i = 0; i < this.slots.length; i++) {
if (this.slots[i].used && this.slots[i].node && this.slots[i].node.isValid) {
validNodes.push(this.slots[i].node);
}
this.slots[i].used = false;
this.slots[i].node = null;
}
// 按顺序重新挂载到前置 ebar
for (let i = 0; i < validNodes.length && i < this.slots.length; i++) {
this.slots[i].used = true;
this.slots[i].node = validNodes[i];
validNodes[i].parent = this.ebars_node.children[i];
validNodes[i].setPosition(Vec3.ZERO);
}
}
/**
* 处理使用装备卡事件:提取 uuid 和 card_lv 后调用 addEquip。
* @param event 事件名
* @param args 卡牌数据(含 uuid、card_lv
*/
private onUseEquipCard(event: string, args: any) {
const payload = args ?? event;
const uuid = Number(payload?.uuid ?? 0);
const card_lv = Math.max(1, Math.floor(Number(payload?.card_lv ?? 1)));
if (!uuid) return;
this.addSkill(uuid, card_lv);
}
start() {
}
/**
* 在场上添加一个装备卡:
* 1. 在 slots 中查找空闲位。
* 2. 实例化 equip_box Prefab 并挂载到对应 ebar 子节点下。
* 3. 获取或添加 EquipBoxComp 并初始化。
*
* @param uuid 装备 UUID
* @param card_lv 装备卡等级
*/
addSkill(uuid: number, card_lv: number) {
if (!this.equip_box) {
mLogger.error(this.debugMode, "MissEquipComp", "equip_box prefab not set");
return;
}
if (!this.ebars_node || !this.ebars_node.isValid || this.slots.length === 0) {
mLogger.error(this.debugMode, "MissEquipComp", "ebars node not bound");
return;
}
// 查找空闲槽位
const emptyIndex = this.slots.findIndex(slot => !slot.used);
if (emptyIndex === -1) {
mLogger.warn(this.debugMode, "MissEquipComp", "equip_box slots are full");
oops.gui.toast("装备槽位已满");
return;
}
// 使用 ECS 实体创建装备节点,挂载到对应 ebar 子节点下
let ebox = ecs.getEntity<EBox>(EBox);
let node = ebox.load(uuid, card_lv, Vec3.ZERO, this.equip_box, this.ebars_node.children[emptyIndex]);
this.slots[emptyIndex].used = true;
this.slots[emptyIndex].node = node;
}
/** ECS 组件移除时销毁节点 */
reset() {
this.node.destroy();
}
}