refactor(map): 重构抽卡逻辑与卡牌渲染规则

1.  实现升级卡轮询机制,按英雄UUID升序循环切换必出升级卡
2.  重构buildDrawCards抽卡流程,分离必出升级卡与随机抽取逻辑
3.  移除抽卡池内的刷新功能卡,调整特殊卡渲染逻辑
4.  统一英雄卡与升级卡的交互与渲染表现
This commit is contained in:
panFD
2026-07-20 22:35:21 +08:00
parent cff7b4703f
commit 1612e02790
3 changed files with 1740 additions and 679 deletions

View File

@@ -151,7 +151,12 @@ export class MissionCardComp extends CCComp {
/** 卡牌面板收起态缩放scale=0 隐藏) */
private cardsHideScale: Vec3 = new Vec3(0, 0, 1);
/** 卡牌原始定位点 */
private cardsPos = [-240, 0, 240]
private cardsPos = [-220, 0, 220]
/**
* 必出升级卡的轮询索引:每次刷新按场上可升级英雄 UUID 升序轮询选 1 张。
* 每次 buildDrawCards 取模更新,保证多次刷新时升级卡依次切换。
*/
private upgradeRotationIndex: number = 0;
// ======================== 生命周期 ========================
@@ -728,17 +733,12 @@ export class MissionCardComp extends CCComp {
/**
* 构建本次抽卡结果,保证最终可分发 3 条数据。
*
* 抽卡池构成
* - 动态升级卡:扫描场上存活英雄,每个 UUID 至多生成一张升级卡
* (已达到 HERO_MAX_LV 的英雄不出卡)。同 UUID 只出一张,避免重复
* - 基础英雄卡:所有英雄的 lv1 卡
* - 刷新功能卡SpecialRefresh 卡。
* 抽卡规则
* 1. **必出规则**:当场上有可升级英雄,每次刷新必出 1 张升级卡,并放在返回数组首位(对应卡池最左侧槽位)。
* 多次刷新时,按场上英雄 target_hero_uuid 升序循环切换升级卡upgradeRotationIndex
* 2. 其余 2 张从混合池(剩余升级卡 + 基础英雄卡)按权重抽取
*
* 三类卡按 weight 权重混合抽 3 张,且升级卡之间 unique 去重
*
* 特殊规则:当场存活英雄数已达 HERO_MAX_NUM 时,
* 不再抽取基础英雄卡和刷新卡,只抽取场上已有英雄的升级卡
* (若全部已满级则降级回混合池,避免卡池为空)。
* 特殊规则:当场存活英雄数已达 HERO_MAX_NUM 时,全部出升级卡(若全部已满级则降级回混合池)
*/
private buildDrawCards(): CardConfig[] {
const upgradeCards = this.buildHeroUpgradeCards();
@@ -758,21 +758,42 @@ export class MissionCardComp extends CCComp {
}
const heroCards = CardPoolList.filter(c => c.type === CardType.Hero);
const refreshCards: CardConfig[] = Object.values(SpecialRefreshCardList);
const mixedPool: CardConfig[] = [...upgradeCards, ...heroCards, ...refreshCards];
// 英雄池只刷英雄卡(含动态升级卡),不再混入刷新功能卡
const mixedPool: CardConfig[] = [...upgradeCards, ...heroCards];
if (mixedPool.length === 0) return [];
const picked = this.pickMixedCards(mixedPool, 3, upgradeCards);
if (picked.length >= 3) return picked.slice(0, 3);
const result: CardConfig[] = [];
const usedUpgradeTargets = new Set<number>();
/** 兜底:不足 3 张时循环补齐 */
const filled = [...picked];
while (filled.length < 3) {
if (mixedPool.length === 0) break;
filled.push(mixedPool[filled.length % mixedPool.length]);
// 必出 1 张升级卡,按 UUID 升序轮询,放在返回数组首位(对应最左侧卡槽)
if (upgradeCards.length > 0) {
const idx = this.upgradeRotationIndex % upgradeCards.length;
this.upgradeRotationIndex = (this.upgradeRotationIndex + 1) % upgradeCards.length;
const mandatory = upgradeCards[idx];
result.push(mandatory);
usedUpgradeTargets.add(mandatory.target_hero_uuid ?? 0);
}
return filled;
// 剩余张数从混合池抽取,排除已选升级卡 target_hero_uuid
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);
}
return true;
});
const rest = this.pickMixedCards(remainingPool, remainingCount, upgradeCards);
result.push(...rest);
}
// 兜底:不足 3 张时循环补齐
while (result.length < 3) {
if (mixedPool.length === 0) break;
result.push(mixedPool[result.length % mixedPool.length]);
}
return result.slice(0, 3);
}
/**
@@ -850,6 +871,8 @@ export class MissionCardComp extends CCComp {
hero_lv: lv + 1,
});
});
// 按 target_hero_uuid 升序排序,保证返回顺序稳定(便于 buildDrawCards 按顺序轮询)
result.sort((a, b) => (a.target_hero_uuid ?? 0) - (b.target_hero_uuid ?? 0));
return result;
}