diff --git a/assets/script/game/common/config/CardSet.ts b/assets/script/game/common/config/CardSet.ts index eea05e21..05b90b07 100644 --- a/assets/script/game/common/config/CardSet.ts +++ b/assets/script/game/common/config/CardSet.ts @@ -110,11 +110,11 @@ export interface CardConfig { trigger_limit?: number; /** - * 动态升级卡专属:目标英雄 UUID。 + * 动态升级卡专属:目标英雄实体 eid(运行时唯一)。 * 仅当 type === CardType.SpecialUpgrade 且该卡由 buildDrawCards 动态生成时使用。 - * 使用该卡时精确升级场上对应 UUID 的英雄。 + * 使用该卡时精确升级场上对应 eid 的英雄实体。 */ - target_hero_uuid?: number; + target_hero_eid?: number; } /** 升级卡折扣表(已废弃,保留以兼容历史引用) */ @@ -274,7 +274,7 @@ export enum SpecialRefreshHeroType { export interface SpecialUpgradeCardConfig extends CardConfig { name: string info: string - /** 模板字段:currentLv=0 表示动态卡(实际值由 target_hero_uuid 对应的英雄决定) */ + /** 模板字段:currentLv=0 表示动态卡(实际值由 target_hero_eid 对应的英雄决定) */ currentLv: number /** 模板字段:targetLv=0 表示"+1 级"(实际目标等级 = 当前等级 + 1) */ targetLv: number @@ -292,7 +292,7 @@ export interface SpecialRefreshCardConfig extends CardConfig { * 升级卡模板。 * 仅有 7001 一条记录,作为动态升级卡的基础配置(cost/weight/name/info)。 * 实际使用时由 MissionCardComp.buildDrawCards 根据场上英雄动态生成 CardConfig, - * 通过 target_hero_uuid 字段绑定具体英雄。 + * 通过 target_hero_eid 字段绑定具体英雄实体。 */ export const SpecialUpgradeCardList: Record = { 7001: { diff --git a/assets/script/game/map/CardComp.ts b/assets/script/game/map/CardComp.ts index 31b4f1a6..f01bf0f9 100644 --- a/assets/script/game/map/CardComp.ts +++ b/assets/script/game/map/CardComp.ts @@ -526,9 +526,12 @@ export class CardComp extends CCComp { this.showCallBtn(); oops.message.dispatchEvent(GameEvent.CardSelected, this); // 英雄卡 / 升级卡打开信息面板(点击替代长按) - const isUpgradeCard = this.card_type === CardType.SpecialUpgrade && !!this.cardData?.target_hero_uuid; + const isUpgradeCard = this.card_type === CardType.SpecialUpgrade && !!this.cardData?.target_hero_eid; if (this.card_type === CardType.Hero || isUpgradeCard) { - const heroUuid = isUpgradeCard ? (this.cardData!.target_hero_uuid as number) : this.card_uuid; + // 升级卡通过 eid 反查实体得到 hero_uuid;若实体已不存在则降级为本卡 uuid + const heroUuid = isUpgradeCard + ? (this.queryHeroUuidByEid(this.cardData!.target_hero_eid as number) ?? this.card_uuid) + : this.card_uuid; const heroLv = Math.max(1, this.cardData?.hero_lv ?? 1); const poolLv = Math.max(1, this.cardData?.base_pool_lv ?? this.cardData?.pool_lv ?? 1); oops.gui.remove(UIID.HInfo); @@ -536,6 +539,20 @@ export class CardComp extends CCComp { } } + /** + * 通过 eid 反查场上英雄实体的 hero_uuid。 + * 用于动态升级卡渲染:升级卡绑定的是 eid,但显示需要 HeroInfo[uuid]。 + * @returns 找到则返回 hero_uuid,找不到(实体已销毁等)返回 null + */ + private queryHeroUuidByEid(eid: number): number | null { + if (!eid) return null; + const entity = ecs.getEntityByEid(eid); + if (!entity) return null; + const model = entity.get(HeroAttrsComp); + if (!model) return null; + return model.hero_uuid ?? null; + } + /** * 其他卡牌被点击时联动隐藏本卡 call_btn。 * @param event 事件名(ListenerFunc 约定的第一个参数) @@ -639,11 +656,14 @@ export class CardComp extends CCComp { this.node.setPosition(this.restPosition.x, this.restPosition.y, this.restPosition.z); - // 渲染参数:动态升级卡(SpecialUpgrade + target_hero_uuid)按英雄卡样式渲染, - // 使用 target_hero_uuid 对应的英雄信息(名称/AP/HP/动画),等级显示为目标等级。 - const isUpgradeCard = this.card_type === CardType.SpecialUpgrade && !!this.cardData.target_hero_uuid; + // 渲染参数:动态升级卡(SpecialUpgrade + target_hero_eid)按英雄卡样式渲染, + // 通过 target_hero_eid 反查实体得到 hero_uuid,再用 HeroInfo 渲染名称/AP/HP/动画。 + const isUpgradeCard = this.card_type === CardType.SpecialUpgrade && !!this.cardData.target_hero_eid; const renderType: CardType = isUpgradeCard ? CardType.Hero : this.card_type; - const renderUuid: number = isUpgradeCard ? (this.cardData.target_hero_uuid as number) : this.card_uuid; + // 升级卡通过 eid 反查实体得到 hero_uuid;若实体已不存在则降级为本卡 uuid + const renderUuid: number = isUpgradeCard + ? (this.queryHeroUuidByEid(this.cardData.target_hero_eid as number) ?? this.card_uuid) + : this.card_uuid; const renderKind: CKind = isUpgradeCard ? CKind.Hero : this.cardData.kind; // ---- 卡牌种类标识(近战 / 远程 / 辅助等) ---- diff --git a/assets/script/game/map/CardLiteComp.ts b/assets/script/game/map/CardLiteComp.ts index 51f753a6..f8efe70e 100644 --- a/assets/script/game/map/CardLiteComp.ts +++ b/assets/script/game/map/CardLiteComp.ts @@ -246,9 +246,10 @@ export class CardLiteComp extends CCComp { this.setLabel(this.name_node, `${spSuffix}${skillCard?.name || skill?.name || ""}${spSuffix}`); } else { if (this.lvl_node) this.lvl_node.node.active = false; - // 动态升级卡:显示对应英雄名 + "升级"后缀 - if (this.card_type === CardType.SpecialUpgrade && this.cardData.target_hero_uuid) { - const targetHero = HeroInfo[this.cardData.target_hero_uuid]; + // 动态升级卡:显示对应英雄名 + "升级"后缀(注意:升级卡绑定的是 target_hero_eid 而非 uuid, + // 此轻量列表场景通常不展示升级卡;为保持渲染健壮性,回退到本卡 uuid 查 HeroInfo) + if (this.card_type === CardType.SpecialUpgrade && this.cardData.target_hero_eid) { + const targetHero = HeroInfo[this.card_uuid]; this.setLabel(this.name_node, `${targetHero?.name || ""} 升级`); } else { const specialCard = this.card_type === CardType.SpecialUpgrade diff --git a/assets/script/game/map/MissionCardComp.ts b/assets/script/game/map/MissionCardComp.ts index 301aa622..e3b4ea78 100644 --- a/assets/script/game/map/MissionCardComp.ts +++ b/assets/script/game/map/MissionCardComp.ts @@ -540,8 +540,8 @@ export class MissionCardComp extends CCComp { /** * 使用特殊卡事件回调: - * - SpecialUpgrade:按卡牌携带的 target_hero_uuid 精确升级场上对应英雄。 - * target_hero_uuid 缺失时回退为随机升级一个可升级的英雄。 + * - SpecialUpgrade:按卡牌携带的 target_hero_eid 精确升级场上对应英雄实体。 + * target_hero_eid 缺失或实体不存在时升级失败。 * - SpecialRefresh:按英雄类型重新抽取英雄卡。 */ private onUseSpecialCard(event: string, args: any) { @@ -553,8 +553,8 @@ export class MissionCardComp extends CCComp { if (type === CardType.SpecialUpgrade) { const template = SpecialUpgradeCardList[uuid]; if (!template) return; - const targetHeroUuid = Number(payload?.target_hero_uuid ?? 0); - success = this.tryUpgradeHeroByUuid(targetHeroUuid); + const targetHeroEid = Number(payload?.target_hero_eid ?? 0); + success = this.tryUpgradeHeroByEid(targetHeroEid); if (!success) { oops.gui.toast(`场上没有可升级的英雄`); } @@ -735,7 +735,7 @@ export class MissionCardComp extends CCComp { * * 抽卡规则: * 1. **必出规则**:当场上有可升级英雄时,每次刷新必出 1 张升级卡,并放在返回数组首位(对应卡池最左侧槽位)。 - * 多次刷新时,按场上英雄 target_hero_uuid 升序循环切换升级卡(upgradeRotationIndex)。 + * 多次刷新时,按场上英雄 target_hero_eid 升序循环切换升级卡(upgradeRotationIndex)。 * 2. 其余 2 张从混合池(剩余升级卡 + 基础英雄卡)按权重抽取。 * * 特殊规则:当场存活英雄数已达 HERO_MAX_NUM 时,全部出升级卡(若全部已满级则降级回混合池)。 @@ -772,15 +772,15 @@ export class MissionCardComp extends CCComp { this.upgradeRotationIndex = (this.upgradeRotationIndex + 1) % upgradeCards.length; const mandatory = upgradeCards[idx]; result.push(mandatory); - usedUpgradeTargets.add(mandatory.target_hero_uuid ?? 0); + usedUpgradeTargets.add(mandatory.target_hero_eid ?? 0); } - // 剩余张数从混合池抽取,排除已选升级卡 target_hero_uuid + // 剩余张数从混合池抽取,排除已选升级卡 target_hero_eid const remainingCount = 3 - result.length; if (remainingCount > 0) { const remainingPool = mixedPool.filter(c => { - if (c.type === CardType.SpecialUpgrade && c.target_hero_uuid) { - return !usedUpgradeTargets.has(c.target_hero_uuid); + if (c.type === CardType.SpecialUpgrade && c.target_hero_eid) { + return !usedUpgradeTargets.has(c.target_hero_eid); } return true; }); @@ -798,7 +798,7 @@ export class MissionCardComp extends CCComp { /** * 从混合池中按权重抽取 n 张卡。 - * 升级卡之间强制 unique(同一个 target_hero_uuid 只能出现一次)。 + * 升级卡之间强制 unique(同一个 target_hero_eid 只能出现一次)。 */ private pickMixedCards(pool: CardConfig[], count: number, upgradeCards: CardConfig[]): CardConfig[] { if (pool.length === 0 || count <= 0) return []; @@ -807,8 +807,8 @@ export class MissionCardComp extends CCComp { while (selected.length < count) { const available = pool.filter(c => { - if (c.type === CardType.SpecialUpgrade && c.target_hero_uuid) { - return !usedUpgradeTargets.has(c.target_hero_uuid); + if (c.type === CardType.SpecialUpgrade && c.target_hero_eid) { + return !usedUpgradeTargets.has(c.target_hero_eid); } return true; }); @@ -816,8 +816,8 @@ export class MissionCardComp extends CCComp { const pick = this.weightedPick(available); if (!pick) break; selected.push(pick); - if (pick.type === CardType.SpecialUpgrade && pick.target_hero_uuid) { - usedUpgradeTargets.add(pick.target_hero_uuid); + if (pick.type === CardType.SpecialUpgrade && pick.target_hero_eid) { + usedUpgradeTargets.add(pick.target_hero_eid); } } return selected; @@ -836,9 +836,9 @@ export class MissionCardComp extends CCComp { } /** - * 扫描场上存活英雄,为每个 UUID 至多生成一张升级卡。 + * 扫描场上存活英雄,为每个英雄实体生成一张升级卡(按 eid 绑定)。 * - 已达 HERO_MAX_LV 的英雄不出卡。 - * - 同 UUID 多个英雄只生成一张(取等级最低的那个作为升级目标)。 + * - 同 eid 只生成一张(eid 本就唯一,不会重复)。 * - cost = 模板 cost + (当前等级 - 1) * BASE_COST,等级越高升级越贵。 */ private buildHeroUpgradeCards(): CardConfig[] { @@ -848,31 +848,22 @@ export class MissionCardComp extends CCComp { const actors = this.queryAliveHeroActors(); if (actors.length === 0) return []; - // 同 UUID 取等级最低的英雄 - const heroMap = new Map(); - for (const actor of actors) { - const uuid = actor.model.hero_uuid; - const lv = actor.model.lv; - const existing = heroMap.get(uuid); - if (!existing || lv < existing.lv) { - heroMap.set(uuid, { uuid, lv }); - } - } - const result: CardConfig[] = []; - heroMap.forEach(({ uuid, lv }) => { - if (lv >= FightSet.HERO_MAX_LV) return; // 已达上限不再出升级卡 + for (const actor of actors) { + const eid = actor.eid; + const lv = actor.model.lv; + if (lv >= FightSet.HERO_MAX_LV) continue; // 已达上限不再出升级卡 const dynamicCost = template.cost + (lv - 1) * FightSet.BASE_COST; result.push({ ...template, cost: dynamicCost, weight: template.weight, - target_hero_uuid: uuid, + target_hero_eid: eid, hero_lv: lv + 1, }); - }); - // 按 target_hero_uuid 升序排序,保证返回顺序稳定(便于 buildDrawCards 按顺序轮询) - result.sort((a, b) => (a.target_hero_uuid ?? 0) - (b.target_hero_uuid ?? 0)); + } + // 按 target_hero_eid 升序排序,保证返回顺序稳定(便于 buildDrawCards 按顺序轮询) + result.sort((a, b) => (a.target_hero_eid ?? 0) - (b.target_hero_eid ?? 0)); return result; } @@ -1077,39 +1068,34 @@ export class MissionCardComp extends CCComp { return count; } - private queryAliveHeroActors(): Array<{ model: HeroAttrsComp, view: HeroViewComp | null }> { - const actors: Array<{ model: HeroAttrsComp, view: HeroViewComp | null }> = []; + private queryAliveHeroActors(): Array<{ eid: number, model: HeroAttrsComp, view: HeroViewComp | null }> { + const actors: Array<{ eid: number, model: HeroAttrsComp, view: HeroViewComp | null }> = []; ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => { const model = entity.get(HeroAttrsComp); if (!model) return; if (model.fac !== FacSet.HERO) return; if (model.is_dead) return; const view = entity.get(HeroViewComp); - actors.push({ model, view }); + actors.push({ eid: entity.eid, model, view }); }); return actors; } /** - * 按英雄 UUID 精确升级一个存活英雄。 - * 若同 UUID 多个英雄,取等级最低且未达上限的;若都达上限则失败。 + * 按 eid 精确升级场上对应英雄实体。 * - * @param heroUuid 要升级的英雄 UUID + * @param heroEid 要升级的英雄实体 eid * @returns true = 升级成功 */ - private tryUpgradeHeroByUuid(heroUuid: number): boolean { - if (!heroUuid) return false; - const candidates = this.queryAliveHeroActors().filter(item => - item.model.hero_uuid === heroUuid && item.model.lv < FightSet.HERO_MAX_LV - ); - if (candidates.length === 0) return false; - // 取等级最低的(先升级低等级英雄) - candidates.sort((a, b) => a.model.lv - b.model.lv); - const target = candidates[0]; - const nextLv = Math.min(FightSet.HERO_MAX_LV, target.model.lv + 1); - this.applyHeroLevel(target.model, nextLv); - if (target.view) { - target.view.playBuff("buff_lvup"); + private tryUpgradeHeroByEid(heroEid: number): boolean { + if (!heroEid) return false; + const actor = this.queryAliveHeroActors().find(item => item.eid === heroEid); + if (!actor) return false; + if (actor.model.lv >= FightSet.HERO_MAX_LV) return false; + const nextLv = Math.min(FightSet.HERO_MAX_LV, actor.model.lv + 1); + this.applyHeroLevel(actor.model, nextLv); + if (actor.view) { + actor.view.playBuff("buff_lvup"); } return true; }