refactor(map): 重构局内经济系统,统一封装金币操作逻辑
新建MissionEconomy类作为局内经济统一管理入口,整合原分散在HInfoComp、MissionComp、CardComp、MissionCardComp中的金币计算、消费、收益统计逻辑,移除各组件中重复的getMissionCoin、setMissionCoin、getRefreshCost等工具方法,统一维护评分系统的金币统计,提升代码可维护性。
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
// ======================== 怪物数量管理 ========================
|
||||
|
||||
112
assets/script/game/map/MissionEconomy.ts
Normal file
112
assets/script/game/map/MissionEconomy.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/MissionEconomy.ts.meta
Normal file
9
assets/script/game/map/MissionEconomy.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "49695a42-7cc6-457d-8370-0aaa398fb0f4",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user