feat(评分系统): 实现多维度游戏评分统计与结算
- 扩展 GameScoreStats 数据结构,新增战绩、输出、防御、构建和效率五个维度的统计字段 - 在战斗、治疗、购卡、刷新等关键节点实时采集评分数据 - 实现评分数据重置机制,确保每局数据独立 - 重构总分计算逻辑,采用五维加权评分模型 - 新增初始金币收入统计,完善资源利用效率评估
This commit is contained in:
@@ -331,6 +331,10 @@ export class CardComp extends CCComp {
|
||||
}
|
||||
// 扣除金币
|
||||
this.setMissionCoin(currentCoin - cardCost);
|
||||
// 【评分系统 - 效率分】记录购卡消耗的金币,以及刷新后的选中卡次数(命中率分子)
|
||||
smc.vmdata.scores.gold_spent += cardCost;
|
||||
smc.vmdata.scores.refresh_hit_count++;
|
||||
|
||||
oops.message.dispatchEvent(GameEvent.CoinAdd, {
|
||||
syncOnly: true,
|
||||
delta: -cardCost
|
||||
|
||||
@@ -298,6 +298,9 @@ export class MissionCardComp extends CCComp {
|
||||
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);
|
||||
}
|
||||
@@ -554,6 +557,9 @@ export class MissionCardComp extends CCComp {
|
||||
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", {
|
||||
|
||||
@@ -445,6 +445,33 @@ export class MissionComp extends CCComp {
|
||||
smc.mission.in_fight = false;
|
||||
smc.vmdata.mission_data.in_fight = false;
|
||||
smc.mission.stop_spawn_mon = true;
|
||||
|
||||
if (smc.mission.play && !smc.mission.pause) {
|
||||
// 【评分系统 - 战绩分】每回合胜利加分
|
||||
smc.vmdata.scores.wave_win_count++;
|
||||
// 【评分系统 - 战绩分】记录每回合结束时场上留存的敌人数量(扣分项)
|
||||
smc.vmdata.scores.wave_remain_monsters += smc.vmdata.mission_data.mon_num;
|
||||
|
||||
let allAlive = true;
|
||||
let hasHero = false;
|
||||
ecs.query(this.heroAttrsMatcher).forEach(entity => {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (attrs && attrs.fac === FacSet.HERO) {
|
||||
hasHero = true;
|
||||
if (attrs.is_dead) allAlive = false;
|
||||
}
|
||||
});
|
||||
// 【评分系统 - 战绩分】记录全员存活的胜利回合数(额外加分)
|
||||
if (hasHero && allAlive) {
|
||||
smc.vmdata.scores.wave_all_alive_count++;
|
||||
}
|
||||
|
||||
// 【评分系统 - 战绩分】判断是否通过最后一关(第20回合)
|
||||
if (this.currentWave === 20) {
|
||||
smc.vmdata.scores.passed_wave_20 = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 触发战斗结束技能(fend)
|
||||
this.triggerHeroBattleSkills(false);
|
||||
|
||||
@@ -682,7 +709,13 @@ export class MissionComp extends CCComp {
|
||||
this.heapTrendBaseMB = -1;
|
||||
this.monsterCountSyncTimer = 0;
|
||||
this.lastPrepareCoinWave = 0;
|
||||
|
||||
// 重置所有的战局得分数据,防止上一局的数据污染
|
||||
smc.resetScores();
|
||||
|
||||
smc.vmdata.mission_data.coin = Math.max(0, Math.floor(CardInitCoins));
|
||||
// 【评分系统 - 效率分】记录初始获得的金币收入
|
||||
smc.vmdata.scores.gold_earned += smc.vmdata.mission_data.coin;
|
||||
}
|
||||
|
||||
// ======================== 波次管理 ========================
|
||||
@@ -761,6 +794,8 @@ export class MissionComp extends CCComp {
|
||||
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 });
|
||||
|
||||
|
||||
@@ -48,6 +48,32 @@ export class VictoryComp extends CCComp {
|
||||
|
||||
@property(Node)
|
||||
mvp_node=null!
|
||||
|
||||
// ======================== 结算 UI 绑定 ========================
|
||||
@property({ type: Label, tooltip: "总分文本" })
|
||||
total_score_label: Label = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "战绩分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||
combat_node: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "输出分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||
output_node: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "防御分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||
defense_node: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "构建分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||
build_node: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "效率分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||
efficiency_node: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "亮点成就标签的容器" })
|
||||
highlights_container: Node = null!;
|
||||
|
||||
@property({ type: Prefab, tooltip: "亮点成就标签预制体" })
|
||||
highlight_prefab: Prefab = null!;
|
||||
|
||||
/** 调试日志开关 */
|
||||
debugMode: boolean = false;
|
||||
/** 奖励等级(预留) */
|
||||
@@ -334,39 +360,41 @@ export class VictoryComp extends CCComp {
|
||||
*/
|
||||
private calculateTotalScore() {
|
||||
const s = smc.vmdata.scores;
|
||||
let totalScore = 0;
|
||||
|
||||
// 1. 战绩分:衡量生存能力——活几回合、赢几场。
|
||||
s.score_combat = (s.wave_win_count * 100)
|
||||
- (s.wave_remain_monsters * 15)
|
||||
+ (s.wave_all_alive_count * 50)
|
||||
+ (s.passed_wave_20 ? 500 : 0);
|
||||
|
||||
// 1. 战斗行为分
|
||||
totalScore += s.crt_count * ScoreWeights.CRT_KILL;
|
||||
totalScore += s.wf_count * ScoreWeights.WF_TRIGGER;
|
||||
totalScore += s.dod_count * ScoreWeights.DODGE_SUCCESS;
|
||||
totalScore += s.back_count * ScoreWeights.BACK_SUCCESS;
|
||||
totalScore += s.stun_count * ScoreWeights.STUN_SUCCESS;
|
||||
totalScore += s.freeze_count * ScoreWeights.FREEZE_SUCCESS;
|
||||
// 2. 输出分:衡量伤害能力——团队火力如何。
|
||||
s.score_output = Math.floor(s.total_dmg / 100) * 10
|
||||
+ (s.crt_count * 5)
|
||||
+ (s.wf_count * 5)
|
||||
+ (s.highest_dmg * 2);
|
||||
|
||||
// 2. 伤害转化分
|
||||
totalScore += s.total_dmg * ScoreWeights.DMG_FACTOR;
|
||||
totalScore += s.avg_dmg * ScoreWeights.AVG_DMG_FACTOR;
|
||||
totalScore += s.thorns_dmg * ScoreWeights.THORNS_DMG_FACTOR;
|
||||
totalScore += s.crit_dmg_total * ScoreWeights.CRIT_DMG_FACTOR;
|
||||
// 3. 防御分:衡量生存能力——团队多能扛。
|
||||
s.score_defense = (s.shield_block_count * 5)
|
||||
+ Math.floor(s.heal_total / 50) * 10
|
||||
+ (s.dead_trigger_count * 8);
|
||||
|
||||
// 3. 击杀得分
|
||||
totalScore += s.melee_kill_count * ScoreWeights.KILL_MELEE;
|
||||
totalScore += s.remote_kill_count * ScoreWeights.KILL_REMOTE;
|
||||
totalScore += s.elite_kill_count * ScoreWeights.KILL_ELITE;
|
||||
totalScore += s.boss_kill_count * ScoreWeights.KILL_BOSS;
|
||||
// 4. 构建分:衡量阵容构建质量——团队配合程度。
|
||||
s.score_build = 0; // 待完善
|
||||
|
||||
// 4. 生存得分
|
||||
totalScore += s.heal_total * ScoreWeights.HEAL_FACTOR;
|
||||
totalScore += s.lifesteal_total * ScoreWeights.LIFESTEAL_FACTOR;
|
||||
|
||||
// 5. 资源得分
|
||||
totalScore += s.exp_total * ScoreWeights.EXP_FACTOR;
|
||||
totalScore += s.gold_total * ScoreWeights.GOLD_FACTOR;
|
||||
// 5. 效率分:衡量资源利用——金币花得值不值。
|
||||
const goldRatio = s.gold_earned > 0 ? (s.gold_spent / s.gold_earned) : 0;
|
||||
const refreshRatio = s.refresh_count > 0 ? (s.refresh_hit_count / s.refresh_count) : 0;
|
||||
s.score_efficiency = Math.floor(goldRatio * 100) + Math.floor(refreshRatio * 50);
|
||||
|
||||
// 取整并存储
|
||||
s.score = Math.floor(totalScore);
|
||||
mLogger.log(this.debugMode, 'VictoryComp', `[VictoryComp] 结算总分: ${s.score}`);
|
||||
s.score = Math.floor(s.score_combat + s.score_output + s.score_defense + s.score_build + s.score_efficiency);
|
||||
mLogger.log(this.debugMode, 'VictoryComp', `[VictoryComp] 结算总分: ${s.score}`, {
|
||||
combat: s.score_combat,
|
||||
output: s.score_output,
|
||||
defense: s.score_defense,
|
||||
build: s.score_build,
|
||||
efficiency: s.score_efficiency
|
||||
});
|
||||
}
|
||||
|
||||
// ======================== 操作入口 ========================
|
||||
|
||||
Reference in New Issue
Block a user