refactor(game): 重构结算积分与奖励逻辑

1. 移除HeroAttrs中废弃的分数字段
2. 重构积分计算规则,简化为基础分+行为分
3. 调整胜利奖励计算逻辑,改用怪物等级作为成长参数替换原wave计数
4. 同步更新结算配置注释与日志输出
This commit is contained in:
pan
2026-09-11 16:25:28 +08:00
parent 5fe961846f
commit ea15b464ad
5 changed files with 42 additions and 78 deletions

View File

@@ -49,6 +49,7 @@ import { spawningEngine, WAVE_DURATION, BATTLE_TIMEOUT } from "./RogueConfig";
import { MissionMonCompComp } from "./MissionMonComp";
import {
getProgressText,
getMonsterLv,
} from "../common/config/StageSet";
const { ccclass, property } = _decorator;
@@ -638,6 +639,8 @@ export class MissionComp extends CCComp {
// 结算类型:小节胜利显示"下一节",末关通关显示"确认退出" + 通关标题
settle_type: this._settleType,
settle_title: this._settleTitle,
// 关卡难度度量(= 大关号,驱动胜利奖励随进度增长)
mon_lv: getMonsterLv(this.currentStage),
// 失败奖励判定数据:本节坚持时间 + 击杀统计(kills=已刷出-存活)
defeat_stats: {
clear_time: this.clearTime,

View File

@@ -180,6 +180,9 @@ export class VictoryComp extends CCComp {
/** 失败奖励判定数据(MissionComp 传入:本节坚持时间 + 击杀统计) */
private defeatStats: { clear_time: number; kills: number; total: number } | null = null;
/** 本节怪物等级(= 大关号,驱动胜利奖励随关卡进度增长) */
private monLv: number = 1;
/** 加载时隐藏 loading 遮罩并绑定按钮事件 */
protected onLoad(): void {
this.node.getChildByName("loading").active = false
@@ -225,6 +228,7 @@ export class VictoryComp extends CCComp {
this.settleType = args.settle_type ?? "defeat";
this.isVictory = !!args.victory;
this.defeatStats = args.defeat_stats ?? null;
this.monLv = Math.max(1, Number(args.mon_lv) || 1);
// 根据是否可复活决定按钮显示
this.node.getChildByName("btns").getChildByName("next").active = !args.can_revive
@@ -312,58 +316,38 @@ export class VictoryComp extends CCComp {
* 计算单局总分并更新到 smc.vmdata.scores.score。
* 同时刷新 smc.data.score 历史最高分。
*
* @param isVictory 是否胜利(胜利 +10 分,失败不加分)
* @param allAlive 是否全英雄存活获胜(额外 +5 分)
* 积分规则:
* - 胜利 +10 分,全队存活 +5 分;失败 -10 分
* - 暴击/闪避/击晕/冰冻每次 +1 分,单项封顶 5 分(不分胜负均计入)
*
* @param isVictory 是否胜利
* @param allAlive 是否全英雄存活
*/
private calculateTotalScore(isVictory: boolean, allAlive: boolean) {
const s = smc.vmdata.scores;
// 0. 胜负分:胜利 +10,全英雄存活获胜额外 +5,失败不加分
let score_result = 0;
// 0. 胜负分:胜利 +10(全队存活再 +5),失败 -10
let base = 0;
if (isVictory) {
score_result = 10;
base = 10;
if (allAlive) {
score_result += 5;
base += 5;
}
} else {
base = -10;
}
// 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. 战斗行为分:暴击/闪避/击晕/冰冻每次 +1,单项封顶 5
const critPts = Math.min(5, s.crt_count);
const dodgePts = Math.min(5, s.dod_count);
const stunPts = Math.min(5, s.stun_count);
const freezePts = Math.min(5, s.freeze_count);
// 2. 输出分:衡量伤害能力——团队火力如何。
s.score_output = Math.floor(s.total_dmg / 100) * 10
+ (s.crt_count * 5)
// 风怒机制已停用: 风怒次数不再计入输出分
// + (s.wf_count * 5)
+ (s.highest_dmg * 2);
// 存储当前局分数
s.score = Math.floor(base + critPts + dodgePts + stunPts + freezePts);
// 3. 防御分:衡量生存能力——团队多能扛。
s.score_defense = (s.shield_block_count * 5)
+ Math.floor(s.heal_total / 50) * 10
+ (s.dead_trigger_count * 8);
// 4. 构建分:衡量阵容构建质量——团队配合程度。
s.score_build = 0; // 待完善
// 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);
// 6. 亮点成就额外加分 (按等级叠加)
const achieved = this.getAchievedHighlights(s);
s.achieved_highlights = achieved; // 记录已达成的亮点信息
let highlightBonus = 0;
for (const item of achieved) {
highlightBonus += item.config.scoreBonus;
}
// 取整并存储当前局分数
s.score = Math.floor(score_result + s.score_combat + s.score_output + s.score_defense + s.score_build + s.score_efficiency + highlightBonus);
// 亮点成就判定:仅作结算页亮点标签展示,不计入总分
s.achieved_highlights = this.getAchievedHighlights(s);
// 记录滚动前的历史最高分(动画起始值)
this._prevBestScore = smc.data.score;
@@ -384,13 +368,8 @@ export class VictoryComp extends CCComp {
(s as any).isNewRecord = isNewRecord;
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,
highlightBonus: highlightBonus,
achievedHighlights: achieved,
base,
critPts, dodgePts, stunPts, freezePts,
isNewRecord: isNewRecord,
prevBest: this._prevBestScore
});
@@ -608,7 +587,9 @@ export class VictoryComp extends CCComp {
/**
* 计算并发放结算奖励,结果存入 _rewardResult。
* 胜利:原公式不变(base + wave × per_wave,整体 ×1.2)。
* 胜利:随关卡进度增长(base + monLv × per_lv,整体 ×1.2)。
* Why: 无回合概念后 wave_win_count 每节被 resetScores 清零恒为 1,
* 旧的 per_wave 增长失效,改挂怪物等级(= 大关号)随关卡推进线性增长。
* 失败:受"坚持时间 + 击杀比例"限制——
* - 坚持 < 15s 或本节一只怪未杀 → 无任何奖励;
* - 达标 → 坚持奖励(固定)+ 击杀比例奖励(基数 × 本节击杀比例),
@@ -616,9 +597,7 @@ export class VictoryComp extends CCComp {
* @param isVictory 是否胜利
*/
private grantRewards(isVictory: boolean) {
const s = smc.vmdata.scores;
const cfg = VICTORY_REWARD_SET;
const wave = Math.max(0, s.wave_win_count || 0);
// 失败奖励资格判定:坚持时间与击杀数不达标则整场无奖励
let defeatQualified = false;
@@ -637,8 +616,8 @@ export class VictoryComp extends CCComp {
let talentExp: number;
if (isVictory) {
const mult = cfg.victory_multiplier;
gold = Math.floor((cfg.gold.base + wave * cfg.gold.per_wave) * mult);
talentExp = Math.floor((cfg.talent_exp.base + wave * cfg.talent_exp.per_wave) * mult);
gold = Math.floor((cfg.gold.base + this.monLv * cfg.gold.per_lv) * mult);
talentExp = Math.floor((cfg.talent_exp.base + this.monLv * cfg.talent_exp.per_lv) * mult);
} else if (defeatQualified) {
// 失败达标 = 坚持奖励(固定)+ 击杀比例奖励
gold = Math.floor(cfg.defeat.survive.gold + cfg.defeat.kill.gold * killRatio);
@@ -690,7 +669,7 @@ export class VictoryComp extends CCComp {
this._rewardResult = { gold, talentExp, silver, diamond, fragments };
mLogger.log(this.debugMode, 'VictoryComp', '[VictoryComp] 结算奖励发放', {
isVictory, wave, gold, talentExp, silver, diamond, fragments,
isVictory, monLv: this.monLv, gold, talentExp, silver, diamond, fragments,
defeatStats: this.defeatStats, defeatQualified, killRatio,
});
}