/** * @file VictoryComp.ts * @description 战斗结算弹窗组件(UI 视图层) * * 职责: * 1. 在战斗结束时弹出,展示结算信息(得分、奖励)。 * 2. 根据传入参数判断是否可复活,切换"下一步"或"复活"按钮。 * 3. 计算单局总分并存储到 smc.vmdata.scores.score。 * 4. 提供"重新开始"和"退出"两个操作入口。 * * 关键设计: * - onAdded(args) 接收战斗结果参数(victory / rewards / game_data / can_revive)。 * - calculateTotalScore() 根据 ScoreWeights 配置加权计算各项得分。 * - restart() 和 victory_end() 通过分发 MissionEnd / MissionStart 事件驱动游戏状态切换。 * * 依赖: * - smc.vmdata.scores —— 全局战斗统计数据 * - ScoreWeights(ScoreSet)—— 得分权重配置 * - GameEvent.MissionEnd / MissionStart —— 游戏生命周期事件 */ import { _decorator, instantiate, Label ,Prefab,Node} from "cc"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { smc } from "../common/SingletonModuleComp"; import { GameEvent } from "../common/config/GameEvent"; import { it } from "node:test"; import { HeroAttrsComp } from "../hero/HeroAttrsComp"; import { HeroViewComp } from "../hero/HeroViewComp"; import { FacSet } from "../common/config/GameSet"; import { Attrs } from "../common/config/HeroAttrs"; import { ScoreWeights } from "../common/config/ScoreSet"; import { mLogger } from "../common/Logger"; const { ccclass, property } = _decorator; /** * VictoryComp —— 战斗结算弹窗视图组件 * * 通过 oops.gui.open(UIID.Victory, args) 打开。 * 展示战斗结果,计算总分,并提供重开 / 退出操作。 */ @ccclass('VictoryComp') @ecs.register('Victory', false) export class VictoryComp extends CCComp { /** 调试日志开关 */ debugMode: boolean = false; /** 奖励等级(预留) */ reward_lv:number=1 /** 奖励数量(预留) */ reward_num:number=2 /** 掉落奖励列表 */ rewards:any[]=[] /** 累计游戏数据(经验 / 金币 / 钻石) */ game_data:any={ exp:0, gold:0, diamond:0 } // ======================== 复活相关 ======================== /** 是否可以复活(由 MissionComp 传入,取决于剩余复活次数) */ private canRevive: boolean = false; /** 加载时隐藏 loading 遮罩 */ protected onLoad(): void { this.node.getChildByName("loading").active=false } /** * 弹窗打开时的回调:接收战斗结果参数。 * * @param args.victory 是否胜利(当前仅用于标识) * @param args.rewards 掉落奖励列表 * @param args.game_data 累计数据 { exp, gold, diamond } * @param args.can_revive 是否可复活 */ onAdded(args: any) { this.node.getChildByName("loading").active=false mLogger.log(this.debugMode, 'VictoryComp', "[VictoryComp] onAdded",args) if(args.game_data){ this.game_data=args.game_data } // 根据是否可复活决定按钮显示 this.node.getChildByName("btns").getChildByName("next").active=!args.can_revive // 计算总分 this.calculateTotalScore(); } // ======================== 得分计算 ======================== /** * 计算单局总分并更新到 smc.vmdata.scores.score。 * * 得分维度: * 1. 战斗行为分 —— 暴击次数、连击触发、闪避、格挡、眩晕、冻结 * 2. 伤害转化分 —— 总伤害、平均伤害、反伤、暴击伤害 * 3. 击杀得分 —— 近战击杀、远程击杀、精英击杀、Boss 击杀 * 4. 生存得分 —— 治疗量、吸血量 * 5. 资源得分 —— 经验获取、金币获取 */ private calculateTotalScore() { const s = smc.vmdata.scores; let totalScore = 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. 伤害转化分 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. 击杀得分 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. 生存得分 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; // 取整并存储 s.score = Math.floor(totalScore); mLogger.log(this.debugMode, 'VictoryComp', `[VictoryComp] 结算总分: ${s.score}`); } // ======================== 操作入口 ======================== /** 退出战斗:清理数据 → 触发任务结束 → 关闭弹窗 */ victory_end(){ this.clear_data() oops.message.dispatchEvent(GameEvent.MissionEnd) oops.gui.removeByNode(this.node) } /** 清理运行时数据:解除暂停标志 */ clear_data(){ smc.mission.pause=false } /** 看广告双倍奖励(预留) */ watch_ad(){ return true } /** 双倍奖励发放(预留) */ double_reward(){ } /** * 重新开始: * 1. 清理数据。 * 2. 触发 MissionEnd 事件重置状态。 * 3. 显示 loading 遮罩,延迟 0.5 秒后触发 MissionStart。 * 4. 关闭弹窗。 */ restart(){ this.clear_data() oops.message.dispatchEvent(GameEvent.MissionEnd) this.node.getChildByName("loading").active=true this.scheduleOnce(()=>{ oops.message.dispatchEvent(GameEvent.MissionStart) this.node.getChildByName("loading").active=false oops.gui.removeByNode(this.node) },0.5) } /** 物品展示回调(预留) */ item_show(e:any,val:any){ mLogger.log(this.debugMode, 'VictoryComp', "item_show",val) } protected onDestroy(): void { mLogger.log(this.debugMode, 'VictoryComp', "释放胜利界面"); } /** ECS 组件移除时销毁节点 */ reset() { this.node.destroy() } }