refactor(map): 重构局内经济系统,统一封装金币操作逻辑

新建MissionEconomy类作为局内经济统一管理入口,整合原分散在HInfoComp、MissionComp、CardComp、MissionCardComp中的金币计算、消费、收益统计逻辑,移除各组件中重复的getMissionCoin、setMissionCoin、getRefreshCost等工具方法,统一维护评分系统的金币统计,提升代码可维护性。
This commit is contained in:
panw
2026-05-21 09:51:32 +08:00
parent 5cdb50b760
commit bbd3e01ae5
6 changed files with 164 additions and 103 deletions

View File

@@ -33,6 +33,7 @@ import { smc } from "../common/SingletonModuleComp";
import { UIID } from "../common/config/GameUIConfig";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { TalentType } from "../common/config/TalentSet";
import { MissionEconomy } from "./MissionEconomy";
@@ -291,19 +292,7 @@ export class CardComp extends CCComp {
useCard(): CardConfig | null {
if (!this.cardData || this.isUsing) return null;
const cardCost = this.card_cost;
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;
}
// 英雄卡特殊校验:通过 guard 对象实现"可取消"模式
if (this.cardData.type === CardType.Hero) {
const guard = {
@@ -319,16 +308,24 @@ export class CardComp extends CCComp {
return null;
}
}
// 扣除金币
this.setMissionCoin(currentCoin - cardCost);
// 【评分系统 - 效率分】记录购卡消耗的金币,以及刷新后的选中卡次数(命中率分子)
smc.vmdata.scores.gold_spent += cardCost;
// 使用统一经济管理入口消费金币
const success = MissionEconomy.spendCoin(cardCost);
if (!success) {
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: MissionEconomy.getCoin()
});
return null;
}
// 【评分系统 - 效率分】记录刷新后的选中卡次数(命中率分子)
smc.vmdata.scores.refresh_hit_count++;
oops.message.dispatchEvent(GameEvent.CoinAdd, {
syncOnly: true,
delta: -cardCost
});
// 标记使用中,阻止并发操作
this.isUsing = true;
const used = this.cardData;
@@ -336,7 +333,7 @@ export class CardComp extends CCComp {
uuid: used.uuid,
type: used.type,
cost: cardCost,
leftCoin: this.getMissionCoin()
leftCoin: MissionEconomy.getCoin()
});
// 播放消失动画 → 动画结束后清槽并分发效果
this.playUseDisappearAnim(() => {
@@ -1194,18 +1191,7 @@ export class CardComp extends CCComp {
// ======================== 数据访问 ========================
/** 从全局单例获取当前局内金币数量 */
private getMissionCoin(): number {
const missionData = smc?.vmdata?.mission_data;
return Math.max(0, Math.floor(missionData?.coin ?? 0));
}
/** 设置当前局内金币数量(自动向下取整并 clamp 至 >= 0 */
private setMissionCoin(value: number) {
const missionData = smc?.vmdata?.mission_data;
if (!missionData) return;
missionData.coin = Math.max(0, Math.floor(value));
}
/** ECS 组件移除时的释放钩子:销毁节点 */
reset() {

View File

@@ -35,6 +35,7 @@ import { mLogger } from "../common/Logger";
import { MissionHeroCompComp } from "./MissionHeroComp";
import { MoveComp } from "../hero/MoveComp";
import { FacSet } from "../common/config/GameSet";
import { MissionEconomy } from "./MissionEconomy";
const {property, ccclass } = _decorator;
@@ -311,19 +312,8 @@ export class HInfoComp extends CCComp {
});
if (!removed) return;
// 卖出英雄金币收益
const baseSellGold = 1; // 基础卖出金币
const goldBoost = HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.SellGold);
let totalSellGold = baseSellGold + goldBoost;
// 应用天赋 SellBonus (增加数值)
const bonusGold = HeroAttrsComp.getTalentValue(TalentType.SellBonus);
if (bonusGold > 0) {
totalSellGold += bonusGold;
}
totalSellGold = Math.floor(totalSellGold);
oops.message.dispatchEvent(GameEvent.CoinAdd, { delta: totalSellGold });
// 使用统一经济管理入口出售英雄
MissionEconomy.executeSellHero();
oops.gui.remove(UIID.IBox);
}

View File

@@ -49,6 +49,7 @@ import { FacSet, FightSet } from "../common/config/GameSet";
import { MoveComp } from "../hero/MoveComp";
import { MissionHeroCompComp } from "./MissionHeroComp";
import { TalentType } from "../common/config/TalentSet";
import { MissionEconomy } from "./MissionEconomy";
const { ccclass, property } = _decorator;
@@ -315,24 +316,15 @@ export class MissionCardComp extends CCComp {
/**
* 金币变化事件回调:
* - syncOnly=true仅同步 UI 显示(金币已被外部修改过)
* - 否则:累加 delta 到 mission_data.coin 后刷新 UI。
* 仅负责 UI 更新和动画表现。数据更新已由 MissionEconomy 统一处理
*/
private onCoinAdd(event: string, args: any){
const payload = args ?? event;
if (payload?.syncOnly) {
this.updateCoinAndCostUI();
this.playCoinChangeAnim((payload?.delta ?? 0) > 0);
return;
}
const v = typeof payload === 'number' ? payload : (payload?.delta ?? payload?.value ?? 0);
if (v === 0) return;
this.setMissionCoin(this.getMissionCoin() + v);
// 【评分系统 - 效率分】精确统计整局游戏的总收入与额外支出(如卖卡、技能扣费等)
if (v > 0) smc.vmdata.scores.gold_earned += v;
else smc.vmdata.scores.gold_spent -= v;
this.updateCoinAndCostUI();
this.playCoinChangeAnim(v > 0);
if (v !== 0) {
this.playCoinChangeAnim(v > 0);
}
}
/** 战斗开始:不收起面板,不再强制清空卡牌 */
@@ -583,27 +575,22 @@ export class MissionCardComp extends CCComp {
// oops.gui.toast("战斗阶段无法抽卡");
// return;
// }
const cost = this.getRefreshCost();
const currentCoin = this.getMissionCoin();
if (currentCoin < cost) {
const cost = MissionEconomy.getRefreshCost(this.refreshCost);
const success = MissionEconomy.executeRefresh(this.refreshCost);
if (!success) {
oops.gui.toast(`金币不足,刷新需要${cost}`);
this.updateCoinAndCostUI();
mLogger.log(this.debugMode, "MissionCardComp", "draw coin not enough", {
currentCoin,
currentCoin: MissionEconomy.getCoin(),
cost
});
return;
}
this.setMissionCoin(currentCoin - cost);
// 【评分系统 - 效率分】记录因刷新卡池消耗的金币,以及刷新次数
smc.vmdata.scores.gold_spent += cost;
smc.vmdata.scores.refresh_count++;
this.playCoinChangeAnim(false);
this.updateCoinAndCostUI();
mLogger.log(this.debugMode, "MissionCardComp", "click draw", {
poolLv: this.poolLv,
cost,
leftCoin: this.getMissionCoin()
leftCoin: MissionEconomy.getCoin()
});
this.layoutCardSlots();
const cards = this.buildDrawCards();
@@ -797,12 +784,12 @@ export class MissionCardComp extends CCComp {
}
private canUpPool() {
if (this.poolLv >= CARD_POOL_MAX_LEVEL) return false;
const currentCoin = this.getMissionCoin();
const currentCoin = MissionEconomy.getCoin();
return currentCoin >= this.getUpgradeCost(this.poolLv);
}
private canDrawCards() {
return this.getMissionCoin() >= this.getRefreshCost();
return MissionEconomy.getCoin() >= MissionEconomy.getRefreshCost(this.refreshCost);
}
/** 更新升级按钮上的等级文案,反馈当前卡池层级 */
private updatePoolLvUI() {
@@ -860,7 +847,7 @@ export class MissionCardComp extends CCComp {
const coinNode = this.cards_chou.getChildByName("coin");
const numLabel = coinNode?.getChildByName("num")?.getComponent(Label);
if (numLabel) {
numLabel.string = `${this.getRefreshCost()}`;
numLabel.string = `${MissionEconomy.getRefreshCost(this.refreshCost)}`;
}
}
@@ -887,12 +874,7 @@ export class MissionCardComp extends CCComp {
return Math.max(0, baseCost - discount);
}
private getRefreshCost(): number {
let cost = this.refreshCost;
const discount = HeroAttrsComp.getTalentValue(TalentType.RefreshDiscount);
cost = Math.max(0, cost - discount);
return Math.floor(cost);
}
private clearHeroInfoPanels() {
if (this.cachedHInfoComps) {
@@ -1050,21 +1032,14 @@ export class MissionCardComp extends CCComp {
return Math.max(0, Math.floor(missionData?.hero_num ?? 0));
}
private getMissionCoin(): number {
const missionData = this.getMissionData();
return Math.max(0, Math.floor(missionData?.coin ?? 0));
}
private getCurrentWave(): number {
const missionData = this.getMissionData();
return Math.max(1, Math.floor(missionData?.level ?? 1));
}
private setMissionCoin(value: number) {
const missionData = this.getMissionData();
if (!missionData) return;
missionData.coin = Math.max(0, Math.floor(value));
}
private getMissionHeroMaxNum(): number {
const missionData = this.getMissionData();

View File

@@ -47,6 +47,7 @@ import { CardInitCoins } from "../common/config/CardSet";
import { Timer } from "db://oops-framework/core/common/timer/Timer";
import { FieldSkillType } from "../common/config/SkillSet";
import { spawningEngine } from "./RogueConfig";
import { MissionEconomy } from "./MissionEconomy";
const { ccclass, property } = _decorator;
/** 任务(关卡)生命周期阶段 */
@@ -795,23 +796,11 @@ export class MissionComp extends CCComp {
if (wave <= 1) return;
if (wave <= this.lastPrepareCoinWave) return;
let reward = Math.max(0, Math.floor(this.prepareBaseCoinReward));
// 使用统一经济管理入口发放每波金币
const reward = MissionEconomy.executeWaveGold(this.prepareBaseCoinReward);
// 增加驻场技能金币收益
const goldBoost = HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.WaveGold);
reward += goldBoost;
if (reward <= 0) {
this.lastPrepareCoinWave = wave;
return;
}
smc.vmdata.mission_data.coin = Math.max(0, Math.floor((smc.vmdata.mission_data.coin ?? 0) + reward));
// 【评分系统 - 效率分】记录每波次发放的金币奖励收入
smc.vmdata.scores.gold_earned += reward;
this.lastPrepareCoinWave = wave;
oops.message.dispatchEvent(GameEvent.CoinAdd, { delta: reward, syncOnly: true });
mLogger.log(this.debugMode, "MissionComp", "grantPrepareCoinByWave", { wave, reward, boost: goldBoost, coin: smc.vmdata.mission_data.coin });
mLogger.log(this.debugMode, "MissionComp", "grantPrepareCoinByWave", { wave, reward, coin: smc.vmdata.mission_data.coin });
}
// ======================== 怪物数量管理 ========================

View File

@@ -0,0 +1,112 @@
import { smc } from "../common/SingletonModuleComp";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { FieldSkillType } from "../common/config/SkillSet";
import { TalentType } from "../common/config/TalentSet";
import { FightSet } from "../common/config/GameSet";
/**
* 局内经济统一管理类
* 负责统一处理金币的增减、各种费用的计算以及战绩评分中的金币统计。
*/
export class MissionEconomy {
/** 获取当前金币 */
static getCoin(): number {
return Math.max(0, Math.floor(smc.vmdata.mission_data.coin ?? 0));
}
/** 增加金币并更新统计 */
static addCoin(amount: number) {
if (amount <= 0) return;
amount = Math.floor(amount);
smc.vmdata.mission_data.coin = this.getCoin() + amount;
smc.vmdata.scores.gold_earned += amount;
// 发送 UI 更新事件,这里我们统一使用 syncOnly 通知 UI 刷新即可
oops.message.dispatchEvent(GameEvent.CoinAdd, { delta: amount, syncOnly: true });
}
/** 消费金币并更新统计 */
static spendCoin(amount: number): boolean {
if (amount <= 0) return true;
amount = Math.floor(amount);
const current = this.getCoin();
if (current < amount) return false;
smc.vmdata.mission_data.coin = current - amount;
smc.vmdata.scores.gold_spent += amount;
// 发送 UI 更新事件
oops.message.dispatchEvent(GameEvent.CoinAdd, { delta: -amount, syncOnly: true });
return true;
}
/**
* 计算卡牌刷新费用
* @param baseCost 基础费用默认为1
*/
static getRefreshCost(baseCost: number = 1): number {
let cost = baseCost;
const discount = HeroAttrsComp.getTalentValue(TalentType.RefreshDiscount);
cost = Math.max(0, cost - discount);
return Math.floor(cost);
}
/**
* 执行刷新卡牌并扣除金币
*/
static executeRefresh(baseCost: number = 1): boolean {
const cost = this.getRefreshCost(baseCost);
const success = this.spendCoin(cost);
if (success) {
smc.vmdata.scores.refresh_count++;
}
return success;
}
/**
* 计算英雄出售金币
*/
static getSellGold(): number {
const baseSellGold = 1; // 基础卖出金币
const goldBoost = HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.SellGold);
let totalSellGold = baseSellGold + goldBoost;
// 应用天赋 SellBonus (增加数值)
const bonusGold = HeroAttrsComp.getTalentValue(TalentType.SellBonus);
if (bonusGold > 0) {
totalSellGold += bonusGold;
}
return Math.floor(totalSellGold);
}
/**
* 执行出售英雄并增加金币
*/
static executeSellHero(): number {
const gold = this.getSellGold();
this.addCoin(gold);
return gold;
}
/**
* 计算每波基础金币收益
*/
static getWaveGold(baseReward: number): number {
let reward = Math.max(0, Math.floor(baseReward));
const goldBoost = HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.WaveGold);
reward += goldBoost;
return reward;
}
/**
* 执行发放每波金币
*/
static executeWaveGold(baseReward: number): number {
const reward = this.getWaveGold(baseReward);
if (reward > 0) {
this.addCoin(reward);
}
return reward;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "49695a42-7cc6-457d-8370-0aaa398fb0f4",
"files": [],
"subMetas": {},
"userData": {}
}