feat(卡牌): 添加金币消耗机制

- 在 CardComp 中检查使用卡牌所需金币,不足时播放反弹动画并提示
- 在 MissionCardComp 中检查刷新卡牌所需金币,不足时提示
- 添加金币获取和设置方法,统一处理金币数值的取整和边界
- 更新 UI 显示逻辑,同时显示金币余额和刷新/升级消耗
- 使用卡牌或刷新时扣除相应金币并发送金币变更事件
This commit is contained in:
panw
2026-03-27 10:38:42 +08:00
parent ad5758c6e7
commit da83c89427
2 changed files with 83 additions and 7 deletions

View File

@@ -8,6 +8,7 @@ import { HeroInfo } from "../common/config/heroSet";
import { SkillSet } from "../common/config/SkillSet";
import { GameEvent } from "../common/config/GameEvent";
import { oops } from "db://oops-framework/core/Oops";
import { smc } from "../common/SingletonModuleComp";
@@ -161,6 +162,19 @@ export class CardComp extends CCComp {
useCard(): CardConfig | null {
if (!this.cardData || this.isUsing) return null;
const cardCost = Math.max(0, Math.floor(this.cardData.cost ?? 0));
const currentCoin = this.getMissionCoin();
if (currentCoin < cardCost) {
oops.gui.toast(`金币不足,召唤需要${cardCost}`);
this.playReboundAnim();
mLogger.log(this.debugMode, "CardComp", "use card coin not enough", {
uuid: this.cardData.uuid,
type: this.cardData.type,
cardCost,
currentCoin
});
return null;
}
if (this.cardData.type === CardType.Hero) {
const guard = {
cancel: false,
@@ -174,11 +188,18 @@ export class CardComp extends CCComp {
return null;
}
}
this.setMissionCoin(currentCoin - cardCost);
oops.message.dispatchEvent(GameEvent.CoinAdd, {
syncOnly: true,
delta: -cardCost
});
this.isUsing = true;
const used = this.cardData;
mLogger.log(this.debugMode, "CardComp", "use card", {
uuid: used.uuid,
type: used.type
type: used.type,
cost: cardCost,
leftCoin: this.getMissionCoin()
});
this.playUseDisappearAnim(() => {
this.cardUseComp?.onCardUsed(used);
@@ -481,6 +502,17 @@ export class CardComp extends CCComp {
[...clips].forEach(clip => anim.removeClip(clip, true));
}
private getMissionCoin(): number {
const missionData = smc?.vmdata?.mission_data;
return Math.max(0, Math.floor(missionData?.coin ?? 0));
}
private setMissionCoin(value: number) {
const missionData = smc?.vmdata?.mission_data;
if (!missionData) return;
missionData.coin = Math.max(0, Math.floor(value));
}
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() {
this.node.destroy();

View File

@@ -22,6 +22,8 @@ export class MissionCardComp extends CCComp {
private readonly buttonNormalScale: number = 1;
private readonly buttonPressScale: number = 0.94;
private readonly buttonClickScale: number = 1.06;
@property({ tooltip: "每次刷新卡牌消耗金币" })
refreshCost: number = 1;
/** 四个插卡槽位固定顺序分发1~4 */
@property(Node)
cards_node:Node = null!
@@ -106,7 +108,7 @@ export class MissionCardComp extends CCComp {
}
this.resetButtonScale(this.cards_chou);
this.resetButtonScale(this.cards_up);
this.updatePoolLvUI();
this.updateCoinAndCostUI();
this.updateHeroNumUI(false, false);
this.node.active = true;
const cards = this.buildDrawCards();
@@ -161,14 +163,14 @@ export class MissionCardComp extends CCComp {
}
private onCoinAdd(args:any){
if (args?.syncOnly) {
this.updatePoolLvUI();
this.updateCoinAndCostUI();
this.playCoinChangeAnim((args?.delta ?? 0) > 0);
return;
}
const v = typeof args === 'number' ? args : (args?.delta ?? args?.value ?? 0);
if (v === 0) return;
this.setMissionCoin(this.getMissionCoin() + v);
this.updatePoolLvUI();
this.updateCoinAndCostUI();
this.playCoinChangeAnim(v > 0);
}
@@ -260,8 +262,24 @@ export class MissionCardComp extends CCComp {
/** 抽卡按钮每次固定抽4张然后顺序分发给4个单卡脚本 */
private onClickDraw() {
const cost = this.getRefreshCost();
const currentCoin = this.getMissionCoin();
if (currentCoin < cost) {
oops.gui.toast(`金币不足,刷新需要${cost}`);
this.updateCoinAndCostUI();
mLogger.log(this.debugMode, "MissionCardComp", "draw coin not enough", {
currentCoin,
cost
});
return;
}
this.setMissionCoin(currentCoin - cost);
this.playCoinChangeAnim(false);
this.updateCoinAndCostUI();
mLogger.log(this.debugMode, "MissionCardComp", "click draw", {
poolLv: this.poolLv
poolLv: this.poolLv,
cost,
leftCoin: this.getMissionCoin()
});
this.layoutCardSlots();
const cards = this.buildDrawCards();
@@ -278,7 +296,7 @@ export class MissionCardComp extends CCComp {
const currentCoin = this.getMissionCoin();
if (currentCoin < cost) {
oops.gui.toast(`金币不足,升级需要${cost}`);
this.updatePoolLvUI();
this.updateCoinAndCostUI();
mLogger.log(this.debugMode, "MissionCardComp", "pool upgrade coin not enough", {
poolLv: this.poolLv,
currentCoin,
@@ -289,7 +307,7 @@ export class MissionCardComp extends CCComp {
this.setMissionCoin(currentCoin - cost);
this.poolLv += 1;
this.playCoinChangeAnim(false);
this.updatePoolLvUI();
this.updateCoinAndCostUI();
mLogger.log(this.debugMode, "MissionCardComp", "pool level up", {
poolLv: this.poolLv,
cost,
@@ -423,6 +441,10 @@ export class MissionCardComp extends CCComp {
const currentCoin = this.getMissionCoin();
return currentCoin >= this.getUpgradeCost(this.poolLv);
}
private canDrawCards() {
return this.getMissionCoin() >= this.getRefreshCost();
}
/** 更新升级按钮上的等级文案,反馈当前卡池层级 */
private updatePoolLvUI() {
if (!this.cards_up) return;
@@ -449,6 +471,24 @@ 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();
}
const coinNode = this.cards_chou.getChildByName("coin");
const numLabel = coinNode?.getChildByName("num")?.getComponent(Label);
if (numLabel) {
numLabel.string = `${this.getRefreshCost()}`;
}
}
private updateCoinAndCostUI() {
this.updatePoolLvUI();
this.updateDrawCostUI();
}
private playCoinChangeAnim(isIncrease: boolean) {
if (!this.coins_node || !this.coins_node.isValid) return;
const target = this.coins_node.getChildByName("num") || this.coins_node;
@@ -466,6 +506,10 @@ export class MissionCardComp extends CCComp {
return CardsUpSet[lv] ?? 0;
}
private getRefreshCost(): number {
return Math.max(0, Math.floor(this.refreshCost));
}
private ensureHeroInfoPanel(eid: number, model: HeroAttrsComp) {
if (!this.hero_info_node || !this.hero_info_prefab) return;
this.hero_info_node.active = true;