diff --git a/assets/script/game/common/config/GameEvent.ts b/assets/script/game/common/config/GameEvent.ts index 030aae79..96a71bdb 100644 --- a/assets/script/game/common/config/GameEvent.ts +++ b/assets/script/game/common/config/GameEvent.ts @@ -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", diff --git a/assets/script/game/map/CardComp.ts b/assets/script/game/map/CardComp.ts index bfdf00ce..c89267d0 100644 --- a/assets/script/game/map/CardComp.ts +++ b/assets/script/game/map/CardComp.ts @@ -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; } diff --git a/assets/script/game/map/MissionCardComp.ts b/assets/script/game/map/MissionCardComp.ts index ddb07478..ddad41dd 100644 --- a/assets/script/game/map/MissionCardComp.ts +++ b/assets/script/game/map/MissionCardComp.ts @@ -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 diff --git a/assets/script/game/map/SCardComp.ts b/assets/script/game/map/SCardComp.ts index 7ba7850f..7a1e4a81 100644 --- a/assets/script/game/map/SCardComp.ts +++ b/assets/script/game/map/SCardComp.ts @@ -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; }