feat: 新增技能卡系统,优化卡牌操作逻辑
1. 调整任务开始按钮显示逻辑,新增nobg节点控制 2. 重构卡牌拖拽逻辑,技能卡改为点击使用,英雄卡保留上划使用 3. 修改技能卡牌初始消耗为0 4. 新增技能卡槽面板,在特定波次开放技能卡抽取 5. 新增技能卡刷新按钮与相关回调逻辑 6. 优化抽卡UI显示与费用更新逻辑
This commit is contained in:
@@ -137,6 +137,8 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
/** 四个槽位对应的 CardComp 控制器缓存(有序数组) */
|
||||
private cardComps: CardComp[] = [];
|
||||
/** 技能卡槽控制器缓存 */
|
||||
private skillCardComps: CardComp[] = [];
|
||||
/** 当前卡池等级(仅影响抽卡来源,不直接改卡槽现有内容) */
|
||||
private poolLv: number = CARD_POOL_INIT_LEVEL;
|
||||
/** 是否已缓存卡牌面板基准缩放 */
|
||||
@@ -228,6 +230,12 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
const cards = this.buildDrawCards();
|
||||
this.dispatchCardsToSlots(cards);
|
||||
|
||||
const wave = this.getCurrentWave();
|
||||
if ([1, 5, 10, 15, 20].includes(wave)) {
|
||||
this.showSkillCardPopup();
|
||||
}
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "mission start", {
|
||||
poolLv: this.poolLv
|
||||
});
|
||||
@@ -279,6 +287,7 @@ export class MissionCardComp extends CCComp {
|
||||
oops.message.on(GameEvent.HeroDead, this.onHeroDead, this);
|
||||
oops.message.on(GameEvent.HeroSell, this.onHeroSell, this);
|
||||
oops.message.on(GameEvent.UseHeroCard, this.onUseHeroCard, this);
|
||||
oops.message.on(GameEvent.UseSkillCard, this.onUseSkillCard, this);
|
||||
oops.message.on(GameEvent.UseSpecialCard, this.onUseSpecialCard, this);
|
||||
oops.message.on(GameEvent.CardPoolUpgrade, this.onCardPoolUpgrade, this);
|
||||
|
||||
@@ -286,6 +295,14 @@ export class MissionCardComp extends CCComp {
|
||||
this.cards_chou?.on(NodeEventType.TOUCH_START, this.onDrawTouchStart, this);
|
||||
this.cards_chou?.on(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this);
|
||||
this.cards_chou?.on(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, this);
|
||||
|
||||
/** 技能卡刷新按钮 */
|
||||
this.skill_refresh?.on(NodeEventType.TOUCH_START, this.onSkillDrawTouchStart, this);
|
||||
this.skill_refresh?.on(NodeEventType.TOUCH_END, this.onSkillDrawTouchEnd, this);
|
||||
this.skill_refresh?.on(NodeEventType.TOUCH_CANCEL, this.onSkillDrawTouchCancel, this);
|
||||
this.skill_ad_refresh?.on(NodeEventType.TOUCH_START, this.onSkillAdDrawTouchStart, this);
|
||||
this.skill_ad_refresh?.on(NodeEventType.TOUCH_END, this.onSkillAdDrawTouchEnd, this);
|
||||
this.skill_ad_refresh?.on(NodeEventType.TOUCH_CANCEL, this.onSkillAdDrawTouchCancel, this);
|
||||
// this.cards_up?.on(NodeEventType.TOUCH_START, this.onUpgradeTouchStart, this);
|
||||
// this.cards_up?.on(NodeEventType.TOUCH_END, this.onUpgradeTouchEnd, this);
|
||||
// this.cards_up?.on(NodeEventType.TOUCH_CANCEL, this.onUpgradeTouchCancel, this);
|
||||
@@ -351,6 +368,34 @@ export class MissionCardComp extends CCComp {
|
||||
this.layoutCardSlots();
|
||||
const cards = this.buildDrawCards();
|
||||
this.dispatchCardsToSlots(cards);
|
||||
|
||||
const wave = this.getCurrentWave();
|
||||
if ([1, 5, 10, 15, 20].includes(wave)) {
|
||||
this.showSkillCardPopup();
|
||||
}
|
||||
}
|
||||
|
||||
private showSkillCardPopup() {
|
||||
if (!this.skill_card_node) return;
|
||||
this.skill_card_node.active = true;
|
||||
const cards = this.buildSkillDrawCards();
|
||||
this.dispatchCardsToSkillSlots(cards);
|
||||
}
|
||||
|
||||
private dispatchCardsToSkillSlots(cards: CardConfig[]) {
|
||||
if (!this.skillCardComps) return;
|
||||
for (let i = 0; i < this.skillCardComps.length; i++) {
|
||||
if (this.skillCardComps[i]) {
|
||||
this.skillCardComps[i].applyDrawCard(cards[i] ?? null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onUseSkillCard(event: string, args: any) {
|
||||
// 选择技能后关闭弹窗
|
||||
if (this.skill_card_node && this.skill_card_node.isValid) {
|
||||
this.skill_card_node.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 解除按钮监听,避免节点销毁后回调泄漏 */
|
||||
@@ -360,6 +405,7 @@ export class MissionCardComp extends CCComp {
|
||||
oops.message.off(GameEvent.HeroDead, this.onHeroDead, this);
|
||||
oops.message.off(GameEvent.HeroSell, this.onHeroSell, this);
|
||||
oops.message.off(GameEvent.UseHeroCard, this.onUseHeroCard, this);
|
||||
oops.message.off(GameEvent.UseSkillCard, this.onUseSkillCard, this);
|
||||
oops.message.off(GameEvent.UseSpecialCard, this.onUseSpecialCard, this);
|
||||
oops.message.off(GameEvent.CardPoolUpgrade, this.onCardPoolUpgrade, this);
|
||||
if (this.cards_chou && this.cards_chou.isValid) {
|
||||
@@ -367,6 +413,16 @@ export class MissionCardComp extends CCComp {
|
||||
this.cards_chou.off(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this);
|
||||
this.cards_chou.off(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, this);
|
||||
}
|
||||
if (this.skill_refresh && this.skill_refresh.isValid) {
|
||||
this.skill_refresh.off(NodeEventType.TOUCH_START, this.onSkillDrawTouchStart, this);
|
||||
this.skill_refresh.off(NodeEventType.TOUCH_END, this.onSkillDrawTouchEnd, this);
|
||||
this.skill_refresh.off(NodeEventType.TOUCH_CANCEL, this.onSkillDrawTouchCancel, this);
|
||||
}
|
||||
if (this.skill_ad_refresh && this.skill_ad_refresh.isValid) {
|
||||
this.skill_ad_refresh.off(NodeEventType.TOUCH_START, this.onSkillAdDrawTouchStart, this);
|
||||
this.skill_ad_refresh.off(NodeEventType.TOUCH_END, this.onSkillAdDrawTouchEnd, this);
|
||||
this.skill_ad_refresh.off(NodeEventType.TOUCH_CANCEL, this.onSkillAdDrawTouchCancel, this);
|
||||
}
|
||||
// this.cards_up?.off(NodeEventType.TOUCH_START, this.onUpgradeTouchStart, this);
|
||||
// this.cards_up?.off(NodeEventType.TOUCH_END, this.onUpgradeTouchEnd, this);
|
||||
// this.cards_up?.off(NodeEventType.TOUCH_CANCEL, this.onUpgradeTouchCancel, this);
|
||||
@@ -522,6 +578,44 @@ export class MissionCardComp extends CCComp {
|
||||
private onDrawTouchCancel() {
|
||||
this.playButtonResetAnim(this.cards_chou);
|
||||
}
|
||||
|
||||
// ======================== 技能抽卡按钮回调 ========================
|
||||
private onSkillDrawTouchStart() {
|
||||
this.playButtonPressAnim(this.skill_refresh);
|
||||
}
|
||||
private onSkillDrawTouchEnd() {
|
||||
this.playButtonClickAnim(this.skill_refresh, () => this.onClickSkillRefresh());
|
||||
}
|
||||
private onSkillDrawTouchCancel() {
|
||||
this.playButtonResetAnim(this.skill_refresh);
|
||||
}
|
||||
|
||||
private onSkillAdDrawTouchStart() {
|
||||
this.playButtonPressAnim(this.skill_ad_refresh);
|
||||
}
|
||||
private onSkillAdDrawTouchEnd() {
|
||||
this.playButtonClickAnim(this.skill_ad_refresh, () => this.onClickSkillAdRefresh());
|
||||
}
|
||||
private onSkillAdDrawTouchCancel() {
|
||||
this.playButtonResetAnim(this.skill_ad_refresh);
|
||||
}
|
||||
|
||||
private onClickSkillRefresh() {
|
||||
const cost = MissionEconomy.getRefreshCost(this.refreshCost);
|
||||
const success = MissionEconomy.executeRefresh(this.refreshCost);
|
||||
if (!success) {
|
||||
oops.gui.toast(`金币不足,刷新需要${cost}`);
|
||||
return;
|
||||
}
|
||||
const cards = this.buildSkillDrawCards();
|
||||
this.dispatchCardsToSkillSlots(cards);
|
||||
}
|
||||
|
||||
private onClickSkillAdRefresh() {
|
||||
// TODO: 接入广告 SDK 逻辑,目前先直接刷新
|
||||
const cards = this.buildSkillDrawCards();
|
||||
this.dispatchCardsToSkillSlots(cards);
|
||||
}
|
||||
// /** 升级按钮按下反馈 */
|
||||
// private onUpgradeTouchStart() {
|
||||
// this.playButtonPressAnim(this.cards_up);
|
||||
@@ -544,6 +638,11 @@ export class MissionCardComp extends CCComp {
|
||||
this.cardComps = nodes
|
||||
.map(node => node?.getComponent(CardComp))
|
||||
.filter((comp): comp is CardComp => !!comp);
|
||||
|
||||
const skillNodes = [this.skill_card1, this.skill_card2, this.skill_card3];
|
||||
this.skillCardComps = skillNodes
|
||||
.map(node => node?.getComponent(CardComp))
|
||||
.filter((comp): comp is CardComp => !!comp);
|
||||
}
|
||||
|
||||
// ======================== 核心业务:抽卡 & 升级 ========================
|
||||
@@ -555,10 +654,10 @@ export class MissionCardComp extends CCComp {
|
||||
* 3. 重新布局槽位 → 从卡池构建 4 张卡 → 分发到槽位。
|
||||
*/
|
||||
private onClickDraw() {
|
||||
// if (this.isBattlePhase) {
|
||||
// oops.gui.toast("战斗阶段无法抽卡");
|
||||
// return;
|
||||
// }
|
||||
if (this.isBattlePhase) {
|
||||
oops.gui.toast("战斗阶段无法抽卡");
|
||||
return;
|
||||
}
|
||||
const cost = MissionEconomy.getRefreshCost(this.refreshCost);
|
||||
const success = MissionEconomy.executeRefresh(this.refreshCost);
|
||||
if (!success) {
|
||||
@@ -631,11 +730,23 @@ export class MissionCardComp extends CCComp {
|
||||
this.cards_node.active = true;
|
||||
Tween.stopAllByTarget(this.cards_node);
|
||||
this.cards_node.setScale(this.cardsShowScale);
|
||||
if (this.cards_chou && this.cards_chou.isValid) {
|
||||
const nobg = this.cards_chou.getChildByName("nobg");
|
||||
if (nobg) {
|
||||
nobg.active = !this.canDrawCards();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enterBattlePhase() {
|
||||
if (!this.cards_node || !this.cards_node.isValid) return;
|
||||
this.initCardsPanelPos();
|
||||
if (this.cards_chou && this.cards_chou.isValid) {
|
||||
const nobg = this.cards_chou.getChildByName("nobg");
|
||||
if (nobg) {
|
||||
nobg.active = true;
|
||||
}
|
||||
}
|
||||
// 战斗阶段不再隐藏抽卡面板
|
||||
// Tween.stopAllByTarget(this.cards_node);
|
||||
// tween(this.cards_node)
|
||||
@@ -650,12 +761,7 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
/** 构建本次抽卡结果,保证最终可分发3条数据 */
|
||||
private buildDrawCards(): CardConfig[] {
|
||||
let targetType: CardType | CardType[] | undefined = undefined;
|
||||
if (this.isBattlePhase) {
|
||||
targetType = CardType.Skill;
|
||||
} else {
|
||||
targetType = [CardType.Hero, CardType.SpecialRefresh];
|
||||
}
|
||||
const targetType = [CardType.Hero, CardType.SpecialRefresh];
|
||||
const cards = getCardsByLv(this.poolLv, targetType);
|
||||
|
||||
/** 正常情况下直接取前3 */
|
||||
@@ -670,6 +776,20 @@ export class MissionCardComp extends CCComp {
|
||||
return filled;
|
||||
}
|
||||
|
||||
private buildSkillDrawCards(): CardConfig[] {
|
||||
const targetType = CardType.Skill;
|
||||
const cards = getCardsByLv(this.poolLv, targetType);
|
||||
|
||||
if (cards.length >= 3) return cards.slice(0, 3);
|
||||
const filled = [...cards];
|
||||
while (filled.length < 3) {
|
||||
const fallback = getCardsByLv(this.poolLv, targetType);
|
||||
if (fallback.length === 0) break;
|
||||
filled.push(fallback[filled.length % fallback.length]);
|
||||
}
|
||||
return filled;
|
||||
}
|
||||
|
||||
private tryRefreshHeroCards(heroType?: HType, targetPoolLv?: number): boolean {
|
||||
const cards = drawCardsByRule(this.poolLv, {
|
||||
count: 3,
|
||||
@@ -716,6 +836,11 @@ export class MissionCardComp extends CCComp {
|
||||
this.cardComps.forEach(comp => {
|
||||
if (comp) comp.clearBySystem();
|
||||
});
|
||||
if (this.skillCardComps) {
|
||||
this.skillCardComps.forEach(comp => {
|
||||
if (comp) comp.clearBySystem();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private layoutCardSlots() {
|
||||
@@ -808,15 +933,28 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
private updateDrawCostUI() {
|
||||
if (!this.cards_chou) return;
|
||||
const nobg = this.cards_chou.getChildByName("nobg");
|
||||
if (nobg) {
|
||||
nobg.active = !this.canDrawCards();
|
||||
if (this.cards_chou) {
|
||||
const nobg = this.cards_chou.getChildByName("nobg");
|
||||
if (nobg) {
|
||||
nobg.active = this.isBattlePhase ? true : !this.canDrawCards();
|
||||
}
|
||||
const coinNode = this.cards_chou.getChildByName("coin");
|
||||
const numLabel = coinNode?.getChildByName("num")?.getComponent(Label);
|
||||
if (numLabel) {
|
||||
numLabel.string = `${MissionEconomy.getRefreshCost(this.refreshCost)}`;
|
||||
}
|
||||
}
|
||||
const coinNode = this.cards_chou.getChildByName("coin");
|
||||
const numLabel = coinNode?.getChildByName("num")?.getComponent(Label);
|
||||
if (numLabel) {
|
||||
numLabel.string = `${MissionEconomy.getRefreshCost(this.refreshCost)}`;
|
||||
|
||||
if (this.skill_refresh) {
|
||||
const nobg = this.skill_refresh.getChildByName("nobg");
|
||||
if (nobg) {
|
||||
nobg.active = !this.canDrawCards();
|
||||
}
|
||||
const coinNode = this.skill_refresh.getChildByName("coin");
|
||||
const numLabel = coinNode?.getChildByName("num")?.getComponent(Label);
|
||||
if (numLabel) {
|
||||
numLabel.string = `${MissionEconomy.getRefreshCost(this.refreshCost)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,6 +1152,7 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
// 关键:在 reset/销毁 时将 Map 置空,彻底切断引用
|
||||
this.cardComps = [] as any;
|
||||
this.skillCardComps = [] as any;
|
||||
|
||||
if (this.node && this.node.isValid) {
|
||||
this.node.destroy();
|
||||
|
||||
Reference in New Issue
Block a user