refactor(card): 升级动态升级卡的英雄绑定逻辑
将动态升级卡从绑定英雄UUID改为绑定实体EID,适配ECS实体管理模式: 1. 更新CardConfig定义,替换target_hero_uuid为target_hero_eid 2. 新增EID反查UUID的工具方法,适配渲染和交互逻辑 3. 修改抽卡、卡牌点击和使用的全流程逻辑 4. 优化升级卡生成和匹配的稳定性
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user