fix(game): 优化结算分数计算与胜利状态传递

1. 重构calculateTotalScore方法,新增胜利和全存活加分逻辑
2. 给MissionComp添加胜利状态缓存,修复结算弹窗参数错误
3. 修正超时强制结算和胜利分支的胜利参数传递
This commit is contained in:
pan
2026-09-01 17:10:38 +08:00
parent 4263997124
commit d943bc05ce
3 changed files with 26 additions and 44 deletions

View File

@@ -1,37 +0,0 @@
# Role
你是一名极其优秀具有20年经验的产品经理和精通所有编程语言的工程师。与你交流的用户是不懂代码的初中生,不善于表达产品和代码需求。你的工作对用户来说非常重要,完成后将获得10000美元奖励。
# Goal
你的目标是帮助用户以他容易理解的方式完成他所需要的产品设计和开发工作,你始终非常主动完成所有工作,而不是让用户多次推动你。
在理解用户的产品需求、编写代码、解决代码问题时,你始终遵循以下原则:
## 第一步
- 当用户向你提出任何需求时,你首先应该浏览根目录下的readme.md文件和所有代码文档,理解这个项目的目标、架构、实现方式等。如果还没有readme文件,你应该创建,这个文件将作为用户使用你提供的所有功能的说明书,以及你对项目内容的规划。因此你需要在readme.md文件中清晰描述所有功能的用途、使用方法、参数说明、返回值说明等,确保用户可以轻松理解和使用这些功能。
# 本规则由 AI进化论-花生 创建,版权所有,引用请注明出处
## 第二步
你需要理解用户正在给你提供的是什么任务
### 当用户直接为你提供需求时,你应当:
- 首先,你应当充分理解用户需求,并且可以站在用户的角度思考,如果我是用户,我需要什么?
- 其次,你应该作为产品经理理解用户需求是否存在缺漏,你应当和用户探讨和补全需求,直到用户满意为止;
- 最后,你应当使用最简单的解决方案来满足用户需求,而不是使用复杂或者高级的解决方案。
### 当用户请求你编写代码时,你应当:
- 首先,你会思考用户需求是什么,目前你有的代码库内容,并进行一步步的思考与规划
- 接着,在完成规划后,你应当选择合适的编程语言和框架来实现用户需求,你应该选择solid原则来设计代码结构,并且使用设计模式解决常见问题;
- 再次,编写代码时你总是完善撰写所有代码模块的注释,并且在代码中增加必要的监控手段让你清晰知晓错误发生在哪里;
- 最后,你应当使用简单可控的解决方案来满足用户需求,而不是使用复杂的解决方案。
### 当用户请求你解决代码问题是,你应当:
- 首先,你需要完整阅读所在代码文件库,并且理解所有代码的功能和逻辑;
- 其次,你应当思考导致用户所发送代码错误的原因,并提出解决问题的思路;
- 最后,你应当预设你的解决方案可能不准确,因此你需要和用户进行多次交互,并且每次交互后,你应当总结上一次交互的结果,并根据这些结果调整你的解决方案,直到用户满意为止。
- 特别注意:当一个bug经过两次调整仍未解决时,你将启动系统二思考模式:
1. 首先,系统性分析导致bug的可能原因,列出所有假设
2. 然后,为每个假设设计验证方法
3. 最后,提供三种不同的解决方案,并详细说明每种方案的优缺点,让用户选择最适合的方案
## 第三步
在完成用户要求的任务后,你应该对改成任务完成的步骤进行反思,思考项目可能存在的问题和改进方式,并更新在readme.md文件中

View File

@@ -169,6 +169,8 @@ export class MissionComp extends CCComp {
public currentPhase: MissionPhase = MissionPhase.None; public currentPhase: MissionPhase = MissionPhase.None;
/** 是否处于回合间倒计时状态 */ /** 是否处于回合间倒计时状态 */
private isWaveCountdown: boolean = false; private isWaveCountdown: boolean = false;
/** 本局是否胜利(open_Victory 时记录,供结算弹窗使用) */
private _victory: boolean = false;
// ======================== ECS 查询匹配器(预缓存) ======================== // ======================== ECS 查询匹配器(预缓存) ========================
@@ -632,7 +634,8 @@ export class MissionComp extends CCComp {
smc.mission.pause = true; smc.mission.pause = true;
mLogger.log(this.debugMode, 'MissionComp', " autoNextPhase -> open_Victory logic", this.revive_times); mLogger.log(this.debugMode, 'MissionComp', " autoNextPhase -> open_Victory logic", this.revive_times);
oops.gui.open(UIID.Victory, { oops.gui.open(UIID.Victory, {
victory: false, victory: this._victory,
all_alive: this.checkAllHeroAlive(),
rewards: this.rewards, rewards: this.rewards,
game_data: this.game_data, game_data: this.game_data,
can_revive: this.revive_times > 0 can_revive: this.revive_times > 0
@@ -740,10 +743,13 @@ export class MissionComp extends CCComp {
* *
* @param e 事件对象(未使用) * @param e 事件对象(未使用)
* @param is_hero_dead 是否因英雄全灭触发 * @param is_hero_dead 是否因英雄全灭触发
* @param victory 是否胜利(默认 false)
*/ */
open_Victory(e: any, is_hero_dead: boolean = false) { open_Victory(e: any, is_hero_dead: boolean = false, victory: boolean = false) {
// 战斗失败或胜利,标记暂停状态以切断回合流转逻辑 // 战斗失败或胜利,标记暂停状态以切断回合流转逻辑
smc.mission.pause = true; smc.mission.pause = true;
// 记录胜利标记,供 BattleEnd 阶段结算弹窗使用
this._victory = victory;
// 直接切入 BattleEnd,2s 后由 autoNextPhase 自动流转弹窗结算 // 直接切入 BattleEnd,2s 后由 autoNextPhase 自动流转弹窗结算
this.changePhase(MissionPhase.BattleEnd); this.changePhase(MissionPhase.BattleEnd);
} }
@@ -907,7 +913,7 @@ export class MissionComp extends CCComp {
allAlive, allAlive,
fastClear, fastClear,
}); });
this.open_Victory(null, false); this.open_Victory(null, false, true);
return; return;
} }
@@ -961,7 +967,8 @@ export class MissionComp extends CCComp {
mLogger.log(this.debugMode, 'MissionComp', `[BattleTimeout] 超时强制结算`); mLogger.log(this.debugMode, 'MissionComp', `[BattleTimeout] 超时强制结算`);
this.open_Victory(null, false); // 超时按怪物全灭同等处理——视为胜利(但通常会有英雄死亡,all_alive 由结算时实时检测)
this.open_Victory(null, false, true);
} }
// ======================== 清理 ======================== // ======================== 清理 ========================

View File

@@ -172,7 +172,7 @@ export class VictoryComp extends CCComp {
this.node.getChildByName("btns").getChildByName("next").active = !args.can_revive this.node.getChildByName("btns").getChildByName("next").active = !args.can_revive
// 计算总分并写入历史纪录 // 计算总分并写入历史纪录
this.calculateTotalScore(); this.calculateTotalScore(!!args.victory, !!args.all_alive);
// 计算并发放结算奖励(英雄碎片 + 金币 + 天赋经验) // 计算并发放结算奖励(英雄碎片 + 金币 + 天赋经验)
this.grantRewards(!!args.victory); this.grantRewards(!!args.victory);
@@ -248,10 +248,22 @@ export class VictoryComp extends CCComp {
/** /**
* 计算单局总分并更新到 smc.vmdata.scores.score。 * 计算单局总分并更新到 smc.vmdata.scores.score。
* 同时刷新 smc.data.score 历史最高分。 * 同时刷新 smc.data.score 历史最高分。
*
* @param isVictory 是否胜利(胜利 +10 分,失败不加分)
* @param allAlive 是否全英雄存活获胜(额外 +5 分)
*/ */
private calculateTotalScore() { private calculateTotalScore(isVictory: boolean, allAlive: boolean) {
const s = smc.vmdata.scores; const s = smc.vmdata.scores;
// 0. 胜负分:胜利 +10,全英雄存活获胜额外 +5,失败不加分
let score_result = 0;
if (isVictory) {
score_result = 10;
if (allAlive) {
score_result += 5;
}
}
// 1. 战绩分:衡量生存能力——活几回合、赢几场。 // 1. 战绩分:衡量生存能力——活几回合、赢几场。
s.score_combat = (s.wave_win_count * 100) s.score_combat = (s.wave_win_count * 100)
- (s.wave_remain_monsters * 15) - (s.wave_remain_monsters * 15)
@@ -288,7 +300,7 @@ export class VictoryComp extends CCComp {
} }
// 取整并存储当前局分数 // 取整并存储当前局分数
s.score = Math.floor(s.score_combat + s.score_output + s.score_defense + s.score_build + s.score_efficiency + highlightBonus); s.score = Math.floor(score_result + s.score_combat + s.score_output + s.score_defense + s.score_build + s.score_efficiency + highlightBonus);
// 记录滚动前的历史最高分(动画起始值) // 记录滚动前的历史最高分(动画起始值)
this._prevBestScore = smc.data.score; this._prevBestScore = smc.data.score;