refactor(missionCard): 重构抽卡逻辑,新增无重复抽取工具方法
1. 抽卡逻辑抽离为pickUniqueHeroCards私有方法,统一处理无重复抽取规则 2. 优化buildDrawCards和tryRefreshHeroCards的抽卡实现,确保同组内和场上英雄不重复 3. 调整刷新次数UI的文本显示格式,移除固定前后缀文案 4. 移除废弃的drawCardsByRule调用,统一使用自有抽卡逻辑
This commit is contained in:
@@ -19,7 +19,8 @@
|
|||||||
* 关键设计:
|
* 关键设计:
|
||||||
* - 3 个 CHeroComp 由 cardPrefab 动态实例化生成,通过 cacheCardComps() 映射为有序数组 cardComps[],
|
* - 3 个 CHeroComp 由 cardPrefab 动态实例化生成,通过 cacheCardComps() 映射为有序数组 cardComps[],
|
||||||
* 之后所有分发、清空操作均通过此数组进行。
|
* 之后所有分发、清空操作均通过此数组进行。
|
||||||
* - buildDrawCards() 从基础英雄卡池按权重抽取 3 张,不足时循环补齐。
|
* - buildDrawCards() 从基础英雄卡池按权重抽取 3 张,同组内英雄绝不重复,
|
||||||
|
* 也不与场上存活英雄撞车(池不足时回退全量池,仍不重复补位)。
|
||||||
* - 英雄上限校验(onUseHeroCard)采用 guard/cancel 模式:
|
* - 英雄上限校验(onUseHeroCard)采用 guard/cancel 模式:
|
||||||
* CHeroComp 发出 UseHeroCard 事件并传入 guard 对象,
|
* CHeroComp 发出 UseHeroCard 事件并传入 guard 对象,
|
||||||
* 本组件可通过 guard.cancel=true 阻止使用。
|
* 本组件可通过 guard.cancel=true 阻止使用。
|
||||||
@@ -41,7 +42,7 @@ import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, Tween, twe
|
|||||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||||
import { GameEvent } from "../common/config/GameEvent";
|
import { GameEvent } from "../common/config/GameEvent";
|
||||||
import { CardConfig, CardPoolList, CardType, drawCardsByRule, SpecialRefreshCardList, SpecialRefreshHeroType } from "../common/config/CardSet";
|
import { CardConfig, CardPoolList, CardType, SpecialRefreshCardList, SpecialRefreshHeroType } from "../common/config/CardSet";
|
||||||
import { drawEquipCards } from "../common/config/EquipSet";
|
import { drawEquipCards } from "../common/config/EquipSet";
|
||||||
import { drawItemCards } from "../common/config/ICardSet";
|
import { drawItemCards } from "../common/config/ICardSet";
|
||||||
import { drawSkillCards } from "../common/config/SCardSet";
|
import { drawSkillCards } from "../common/config/SCardSet";
|
||||||
@@ -1802,9 +1803,9 @@ export class MissionCardComp extends CCComp {
|
|||||||
* 构建本次抽卡结果,返回 3 张英雄卡。
|
* 构建本次抽卡结果,返回 3 张英雄卡。
|
||||||
*
|
*
|
||||||
* 规则:
|
* 规则:
|
||||||
* 1. 从基础英雄卡池按权重抽取,同一次抽卡内英雄不重复。
|
* 1. 从基础英雄卡池按权重抽取,同一次抽卡内英雄不重复(三选一硬性规则)。
|
||||||
* 2. 过滤已上场英雄(卡池不足 3 张时自动回退到未过滤池,保证能抽满)。
|
* 2. 过滤已上场英雄(卡池不足 3 张时自动回退到未过滤池,保证能抽满)。
|
||||||
* 3. 不足 3 张时循环补齐(允许重复)。
|
* 3. 候选池唯一英雄不足 3 个时返回实际数量,也绝不重复补位。
|
||||||
*
|
*
|
||||||
* 注:升级卡机制已移除,英雄卡池只出普通英雄卡。
|
* 注:升级卡机制已移除,英雄卡池只出普通英雄卡。
|
||||||
*/
|
*/
|
||||||
@@ -1817,24 +1818,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
const filtered = allHeroCards.filter(c => !onFieldUuids.has(c.uuid));
|
const filtered = allHeroCards.filter(c => !onFieldUuids.has(c.uuid));
|
||||||
const availableHeroCards = filtered.length >= 3 ? filtered : allHeroCards;
|
const availableHeroCards = filtered.length >= 3 ? filtered : allHeroCards;
|
||||||
|
|
||||||
const result: CardConfig[] = [];
|
return this.pickUniqueHeroCards(availableHeroCards, 3);
|
||||||
const usedUuids = new Set<number>();
|
|
||||||
|
|
||||||
// 按权重抽取,同一次抽卡内英雄不重复
|
|
||||||
while (result.length < 3) {
|
|
||||||
const pool = availableHeroCards.filter(c => !usedUuids.has(c.uuid));
|
|
||||||
if (pool.length === 0) break;
|
|
||||||
const pick = this.weightedPick(pool);
|
|
||||||
if (!pick) break;
|
|
||||||
result.push(pick);
|
|
||||||
usedUuids.add(pick.uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兜底:不足 3 张时循环补齐(允许重复)
|
|
||||||
while (result.length < 3) {
|
|
||||||
result.push(availableHeroCards[result.length % availableHeroCards.length]);
|
|
||||||
}
|
|
||||||
return result.slice(0, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 单次按权重抽取一张卡 */
|
/** 单次按权重抽取一张卡 */
|
||||||
@@ -1849,20 +1833,48 @@ export class MissionCardComp extends CCComp {
|
|||||||
return cards[cards.length - 1];
|
return cards[cards.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从候选池按权重抽取 count 张互不重复的英雄卡。
|
||||||
|
* Why: 三选一不允许出现一样的英雄——池内唯一英雄不足 count 时
|
||||||
|
* 返回实际抽到的数量,也绝不以重复卡补位。
|
||||||
|
* @param pool 已完成上场过滤/类型过滤的候选池
|
||||||
|
* @param count 目标张数
|
||||||
|
* @returns 不重复的卡配置数组(0 ~ count 张)
|
||||||
|
*/
|
||||||
|
private pickUniqueHeroCards(pool: CardConfig[], count: number): CardConfig[] {
|
||||||
|
const result: CardConfig[] = [];
|
||||||
|
const usedUuids = new Set<number>();
|
||||||
|
while (result.length < count) {
|
||||||
|
const rest = pool.filter(c => !usedUuids.has(c.uuid));
|
||||||
|
if (rest.length === 0) break;
|
||||||
|
const pick = this.weightedPick(rest);
|
||||||
|
if (!pick) break;
|
||||||
|
result.push(pick);
|
||||||
|
usedUuids.add(pick.uuid);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新卡效果:按英雄类型重新抽取 3 张英雄卡。
|
||||||
|
* 先过滤(类型 + 已上场)再抽取,保证三选一内不重复且尽量不与场上英雄撞车;
|
||||||
|
* 子池不足 3 张时回退到该类型的全量池,仍抽不齐则如实返回(不重复补位)。
|
||||||
|
*/
|
||||||
private tryRefreshHeroCards(heroType?: HType): boolean {
|
private tryRefreshHeroCards(heroType?: HType): boolean {
|
||||||
let cards = drawCardsByRule({
|
let pool = CardPoolList.filter(c => c.type === CardType.Hero);
|
||||||
count: 3,
|
if (heroType !== undefined) {
|
||||||
type: CardType.Hero,
|
pool = pool.filter(c => HeroInfo[c.uuid]?.type === heroType);
|
||||||
heroType,
|
}
|
||||||
unique: true, // 保证一次刷新内的英雄卡不重复
|
if (pool.length === 0) return false;
|
||||||
});
|
|
||||||
// 过滤已上场英雄;若过滤后为空则回退到未过滤池,避免无可抽卡
|
|
||||||
const onFieldUuids = this.getAliveHeroUuidSet();
|
const onFieldUuids = this.getAliveHeroUuidSet();
|
||||||
const filtered = cards.filter(c => !onFieldUuids.has(c.uuid));
|
const filtered = pool.filter(c => !onFieldUuids.has(c.uuid));
|
||||||
if (filtered.length > 0) cards = filtered;
|
const available = filtered.length >= 3 ? filtered : pool;
|
||||||
|
|
||||||
|
const cards = this.pickUniqueHeroCards(available, 3);
|
||||||
if (cards.length <= 0) return false;
|
if (cards.length <= 0) return false;
|
||||||
this.layoutCardSlots();
|
this.layoutCardSlots();
|
||||||
this.dispatchCardsToSlots(cards.slice(0, 3));
|
this.dispatchCardsToSlots(cards);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2048,10 +2060,10 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.updateDrawCostUI();
|
this.updateDrawCostUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 刷新选择英雄次数提示 Label("第X次"),未绑定或节点失效时静默跳过 */
|
/** 刷新选择英雄次数提示 Label(仅数字,前后缀文字由 UI 提供),未绑定或节点失效时静默跳过 */
|
||||||
private updatePickCountLabel() {
|
private updatePickCountLabel() {
|
||||||
if (this.pickCountLabel && this.pickCountLabel.isValid) {
|
if (this.pickCountLabel && this.pickCountLabel.isValid) {
|
||||||
this.pickCountLabel.string = `第${this.currentPickIndex}次`;
|
this.pickCountLabel.string = `${this.currentPickIndex}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user