refactor(card): 升级动态升级卡的英雄绑定逻辑

将动态升级卡从绑定英雄UUID改为绑定实体EID,适配ECS实体管理模式:
1. 更新CardConfig定义,替换target_hero_uuid为target_hero_eid
2. 新增EID反查UUID的工具方法,适配渲染和交互逻辑
3. 修改抽卡、卡牌点击和使用的全流程逻辑
4. 优化升级卡生成和匹配的稳定性
This commit is contained in:
panFD
2026-07-21 00:03:44 +08:00
parent 48e0ab06c5
commit 8818ec9c3f
4 changed files with 73 additions and 66 deletions

View File

@@ -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<number, { uuid: number, lv: number }>();
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;
}