feat(missionCard): 实现卡牌使用后自动补卡功能
1. 新增GameEvent.CardUsed事件用于通知卡牌使用完成 2. 在各类卡牌使用逻辑中派发CardUsed事件 3. 为MissionCardComp添加CardUsed事件监听与补卡回调 4. 新增buildSingleHeroCard和buildSingleSkillCard方法用于生成单张补卡 5. 调整技能卡使用弹窗逻辑,改为由外部控制显隐
This commit is contained in:
@@ -22,6 +22,8 @@ export enum GameEvent {
|
||||
CardRefresh = "CardRefresh",
|
||||
/** 单张卡牌被点击选中(payload 为被点击的 CardComp 实例,用于其他卡牌联动隐藏 call_btn) */
|
||||
CardSelected = "CardSelected",
|
||||
/** 单张卡牌被成功使用(payload 为该卡槽组件实例 CardComp / SCardComp,用于 MissionCardComp 补卡) */
|
||||
CardUsed = "CardUsed",
|
||||
UseHeroCard = "UseHeroCard",
|
||||
UseSkillCard = "UseSkillCard",
|
||||
UseSpecialCard = "UseSpecialCard",
|
||||
|
||||
@@ -347,18 +347,26 @@ export class CardComp extends CCComp {
|
||||
/**
|
||||
* 根据卡牌类型分发对应的游戏效果事件。
|
||||
* - 英雄卡 → CallHero
|
||||
* - 技能卡 → UseSkillCard
|
||||
* - 特殊升级 / 特殊刷新 → UseSpecialCard
|
||||
*
|
||||
* 卡牌使用成功后会派发 CardUsed 事件(携带本卡槽组件),
|
||||
* 由 MissionCardComp 接收并立即补充一张新卡到该槽位。
|
||||
* SpecialRefresh 例外:其效果本身会重填所有槽位,无需重复补卡。
|
||||
*
|
||||
* @param payload 被使用的卡牌数据
|
||||
*/
|
||||
private executeCardEffectEntry(payload: CardConfig) {
|
||||
switch (payload.type) {
|
||||
case CardType.Hero:
|
||||
oops.message.dispatchEvent(GameEvent.CallHero, payload);
|
||||
oops.message.dispatchEvent(GameEvent.CardUsed, this);
|
||||
break;
|
||||
case CardType.SpecialUpgrade:
|
||||
oops.message.dispatchEvent(GameEvent.UseSpecialCard, payload);
|
||||
oops.message.dispatchEvent(GameEvent.CardUsed, this);
|
||||
break;
|
||||
case CardType.SpecialRefresh:
|
||||
// 刷新卡效果(tryRefreshHeroCards)会重填全部 3 槽,此处不再补卡
|
||||
oops.message.dispatchEvent(GameEvent.UseSpecialCard, payload);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -287,6 +287,7 @@ export class MissionCardComp extends CCComp {
|
||||
oops.message.on(GameEvent.UseSkillCard, this.onUseSkillCard, this);
|
||||
oops.message.on(GameEvent.UseSpecialCard, this.onUseSpecialCard, this);
|
||||
oops.message.on(GameEvent.ShowSmallTip, this.onShowSmallTip, this);
|
||||
oops.message.on(GameEvent.CardUsed, this.onCardUsed, this);
|
||||
|
||||
/** 按钮触控事件:抽卡 */
|
||||
this.cards_chou?.on(NodeEventType.TOUCH_START, this.onDrawTouchStart, this);
|
||||
@@ -431,10 +432,9 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
private onUseSkillCard(event: string, args: any) {
|
||||
// 选择技能后关闭弹窗
|
||||
if (this.skill_card_node && this.skill_card_node.isValid) {
|
||||
this.skill_card_node.active = false;
|
||||
}
|
||||
// 购买技能卡后不再关闭弹窗:技能卡池与英雄卡池保持一致,
|
||||
// 由 onCardUsed 立即向被使用的槽位补充一张新技能卡。
|
||||
// 弹窗由外部(按钮/阶段切换)控制显隐。
|
||||
|
||||
// 修复:独立判断 guide2 的关闭 和 guide3 的开启
|
||||
if (!smc.finish_guides.includes(2)) {
|
||||
@@ -460,6 +460,7 @@ export class MissionCardComp extends CCComp {
|
||||
oops.message.off(GameEvent.UseSkillCard, this.onUseSkillCard, this);
|
||||
oops.message.off(GameEvent.UseSpecialCard, this.onUseSpecialCard, this);
|
||||
oops.message.off(GameEvent.ShowSmallTip, this.onShowSmallTip, this);
|
||||
oops.message.off(GameEvent.CardUsed, this.onCardUsed, this);
|
||||
if (this.cards_chou && this.cards_chou.isValid) {
|
||||
this.cards_chou.off(NodeEventType.TOUCH_START, this.onDrawTouchStart, this);
|
||||
this.cards_chou.off(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this);
|
||||
@@ -572,6 +573,47 @@ export class MissionCardComp extends CCComp {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 单张卡牌被成功使用后的补卡回调:
|
||||
* - payload 为该卡槽组件实例(CardComp 或 SCardComp)。
|
||||
* - 命中英雄卡槽 → 调用 buildSingleHeroCard 补一张英雄/升级卡。
|
||||
* - 命中技能卡槽 → 调用 buildSingleSkillCard 补一张技能卡。
|
||||
* - 未命中任何槽位(如已销毁/未缓存)→ 忽略。
|
||||
*
|
||||
* Why: 购买一张卡后立即补充新卡,避免出现空槽,提升抽卡体验;
|
||||
* 英雄卡池与技能卡池遵循同一逻辑。
|
||||
*/
|
||||
private onCardUsed(event: string, args: any) {
|
||||
const source = args;
|
||||
if (!source) return;
|
||||
|
||||
const heroIdx = this.cardComps.findIndex(c => c === source);
|
||||
if (heroIdx >= 0) {
|
||||
const card = this.buildSingleHeroCard();
|
||||
if (card) {
|
||||
this.cardComps[heroIdx].applyDrawCard(card);
|
||||
}
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "refill hero slot", {
|
||||
index: heroIdx,
|
||||
uuid: card?.uuid ?? 0
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const skillIdx = this.skillCardComps.findIndex(c => c === source);
|
||||
if (skillIdx >= 0) {
|
||||
const card = this.buildSingleSkillCard();
|
||||
if (card) {
|
||||
this.skillCardComps[skillIdx].applyDrawCard(card);
|
||||
}
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "refill skill slot", {
|
||||
index: skillIdx,
|
||||
uuid: card?.uuid ?? 0
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 按钮触控回调 ========================
|
||||
|
||||
/** 抽卡按钮按下反馈 */
|
||||
@@ -957,6 +999,58 @@ export class MissionCardComp extends CCComp {
|
||||
return filled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建单张英雄池卡牌,用于购买后补卡。
|
||||
*
|
||||
* 规则与 buildDrawCards 保持一致(不含"必出轮询"逻辑):
|
||||
* 1. 满员且有可升级英雄 → 仅从升级卡池按权重抽 1 张。
|
||||
* 2. 否则从 升级卡 + 未召唤英雄卡 的混合池按权重抽 1 张。
|
||||
*
|
||||
* @returns 卡牌数据;池为空时返回 null
|
||||
*/
|
||||
private buildSingleHeroCard(): CardConfig | null {
|
||||
const upgradeCards = this.buildHeroUpgradeCards();
|
||||
const aliveHeroCount = this.getAliveHeroCount();
|
||||
const heroMax = this.getMissionHeroMaxNum();
|
||||
|
||||
if (aliveHeroCount >= heroMax && upgradeCards.length > 0) {
|
||||
return this.weightedPick(upgradeCards);
|
||||
}
|
||||
|
||||
const heroCards = CardPoolList.filter(c => c.type === CardType.Hero);
|
||||
const aliveHeroUuids = this.getAliveHeroUuids();
|
||||
const availableHeroCards = heroCards.filter(c => !aliveHeroUuids.has(c.uuid));
|
||||
|
||||
const mixedPool: CardConfig[] = [...upgradeCards, ...availableHeroCards];
|
||||
if (mixedPool.length === 0) return null;
|
||||
return this.weightedPick(mixedPool);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建单张技能卡,用于购买后补卡。
|
||||
* 规则与 buildSkillDrawCards 对齐(按当前 wave 过滤)。
|
||||
*
|
||||
* @returns 卡牌数据;池为空时返回 null
|
||||
*/
|
||||
private buildSingleSkillCard(): CardConfig | null {
|
||||
const currentWave = this.getCurrentWave();
|
||||
const cards = drawCardsByRule(1, {
|
||||
count: 1,
|
||||
type: CardType.Skill,
|
||||
wave: currentWave,
|
||||
unique: true
|
||||
});
|
||||
if (cards.length > 0) return cards[0];
|
||||
|
||||
// 兜底:放宽 wave 限制再尝试
|
||||
const fallback = drawCardsByRule(1, {
|
||||
count: 1,
|
||||
type: CardType.Skill,
|
||||
unique: true
|
||||
});
|
||||
return fallback[0] ?? null;
|
||||
}
|
||||
|
||||
private tryRefreshHeroCards(heroType?: HType): boolean {
|
||||
const cards = drawCardsByRule(1, {
|
||||
count: 3,
|
||||
@@ -1199,6 +1293,7 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
model.updateSkillDistanceCache();
|
||||
model.dirty_hp = true;
|
||||
model.dirty_lv = true;
|
||||
oops.message.dispatchEvent(GameEvent.HeroLvUp, {
|
||||
uuid: model.hero_uuid,
|
||||
lv: nextLv
|
||||
|
||||
@@ -109,6 +109,8 @@ export class SCardComp extends CCComp {
|
||||
this.clearAfterUse();
|
||||
this.isUsing = false;
|
||||
oops.message.dispatchEvent(GameEvent.UseSkillCard, used);
|
||||
// 派发 CardUsed 让 MissionCardComp 立即补一张新技能卡到本槽位
|
||||
oops.message.dispatchEvent(GameEvent.CardUsed, this);
|
||||
});
|
||||
return used;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user