- 在多个组件的onDestroy方法中添加节点有效性检查,防止无效节点上解绑事件 - 修复MissionComp中任务启动逻辑,改为通过UI打开方式触发MissionStart事件 - 添加新的任务界面(UIID.Mission)及相关配置 - 修复MissionCardComp中Map未初始化导致的空引用问题 - 优化按钮事件绑定和解绑逻辑,增加空值检查
608 lines
23 KiB
TypeScript
608 lines
23 KiB
TypeScript
/**
|
||
* @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, Sprite, Animation, AnimationClip, resources, UITransform, Widget, ProgressBar } 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 { HeroInfo } from "../common/config/heroSet";
|
||
import { CKind } from "../common/config/CardSet";
|
||
import { ScoreWeights } from "../common/config/ScoreSet";
|
||
import { HighlightSet, HighlightType, HighlightLevel } from "../common/config/HighlightSet";
|
||
import { mLogger } from "../common/Logger";
|
||
import { UIID } from "../common/config/GameUIConfig";
|
||
|
||
const { ccclass, property } = _decorator;
|
||
|
||
/**
|
||
* VictoryComp —— 战斗结算弹窗视图组件
|
||
*
|
||
* 通过 oops.gui.open(UIID.Victory, args) 打开。
|
||
* 展示战斗结果,计算总分,并提供重开 / 退出操作。
|
||
*/
|
||
@ccclass('VictoryComp')
|
||
@ecs.register('Victory', false)
|
||
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;
|
||
/** 奖励等级(预留) */
|
||
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();
|
||
|
||
// 渲染分数UI和亮点标签
|
||
this.renderScores();
|
||
|
||
// 显示MVP英雄
|
||
const mvp = this.getMVPHero();
|
||
this.renderMVPHero(mvp);
|
||
}
|
||
|
||
// ======================== MVP 英雄 ========================
|
||
|
||
/**
|
||
* 获取战斗中最厉害的英雄(根据等级和攻击力)
|
||
*/
|
||
private getMVPHero(): HeroAttrsComp | null {
|
||
let mvp: HeroAttrsComp | null = null;
|
||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||
const model = entity.get(HeroAttrsComp);
|
||
if (!model || model.fac !== FacSet.HERO) return;
|
||
if (!mvp) {
|
||
mvp = model;
|
||
} else {
|
||
if (model.lv > mvp.lv) {
|
||
mvp = model;
|
||
} else if (model.lv === mvp.lv && model.ap > mvp.ap) {
|
||
mvp = model;
|
||
}
|
||
}
|
||
});
|
||
return mvp;
|
||
}
|
||
|
||
/**
|
||
* 渲染 MVP 英雄,逻辑参考 CardComp 长按放大的 UI 显示
|
||
*/
|
||
private renderMVPHero(mvp: HeroAttrsComp | null) {
|
||
if (!this.mvp_node) return;
|
||
|
||
if (!mvp) {
|
||
this.mvp_node.active = false;
|
||
return;
|
||
}
|
||
|
||
this.mvp_node.active = true;
|
||
|
||
const uuid = mvp.hero_uuid;
|
||
const hero = HeroInfo[uuid];
|
||
if (!hero) return;
|
||
|
||
const kindName = CKind[CKind.Hero];
|
||
|
||
// 节点查找
|
||
const BG_node = this.mvp_node.getChildByName("BG");
|
||
const HF_node = this.mvp_node.getChildByName("HF");
|
||
const NF_node = this.mvp_node.getChildByName("NF");
|
||
const lv_node = this.mvp_node.getChildByName("lv");
|
||
const name_node = this.mvp_node.getChildByName("name");
|
||
const ap_node = this.mvp_node.getChildByName("ap");
|
||
const hp_node = this.mvp_node.getChildByName("hp");
|
||
const oinfo_node = this.mvp_node.getChildByName("oinfo");
|
||
const icon_node = this.mvp_node.getChildByName("icon");
|
||
const hbNode = this.mvp_node.getChildByName("HB");
|
||
const cost_node = this.mvp_node.getChildByName("cost");
|
||
|
||
// ---- 背景与边框 ----
|
||
if (BG_node) {
|
||
BG_node.children.forEach(child => {
|
||
child.active = (child.name === kindName);
|
||
});
|
||
}
|
||
|
||
if (HF_node) {
|
||
HF_node.active = true;
|
||
// HF_node.children.forEach(child => {
|
||
// child.active = (child.name === kindName);
|
||
// });
|
||
}
|
||
|
||
if (NF_node) {
|
||
NF_node.active = false;
|
||
}
|
||
|
||
if (hbNode) hbNode.active = false;
|
||
|
||
// ---- 卡牌等级标识 ----
|
||
const cardLvStr = `lv${mvp.pool_lv || 1}`;
|
||
if (lv_node) {
|
||
lv_node.children.forEach(child => {
|
||
if (child.name === "light") {
|
||
child.active = false;
|
||
} else if (child.name === "bg") {
|
||
child.active = true;
|
||
} else {
|
||
child.active = (child.name === cardLvStr);
|
||
}
|
||
});
|
||
}
|
||
|
||
// ---- 调整尺寸 (模拟放大状态) ----
|
||
const isEnlarged = true; // 结算界面的卡牌默认处于放大显示状态
|
||
const uiTrans = this.mvp_node.getComponent(UITransform);
|
||
if (uiTrans) {
|
||
uiTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||
const widget = this.mvp_node.getComponent(Widget);
|
||
if (widget) widget.updateAlignment();
|
||
}
|
||
|
||
if (BG_node) {
|
||
const bgTrans = BG_node.getComponent(UITransform);
|
||
if (bgTrans) {
|
||
bgTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||
const widget = BG_node.getComponent(Widget);
|
||
if (widget) widget.updateAlignment();
|
||
}
|
||
BG_node.children.forEach(child => {
|
||
const childTrans = child.getComponent(UITransform);
|
||
if (childTrans) {
|
||
childTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||
const widget = child.getComponent(Widget);
|
||
if (widget) widget.updateAlignment();
|
||
}
|
||
});
|
||
}
|
||
|
||
if (HF_node) {
|
||
const hfTrans = HF_node.getComponent(UITransform);
|
||
if (hfTrans) {
|
||
hfTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||
const widget = HF_node.getComponent(Widget);
|
||
if (widget) widget.updateAlignment();
|
||
}
|
||
HF_node.children.forEach(child => {
|
||
const childTrans = child.getComponent(UITransform);
|
||
if (childTrans) {
|
||
childTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||
const widget = child.getComponent(Widget);
|
||
if (widget) widget.updateAlignment();
|
||
}
|
||
});
|
||
}
|
||
|
||
this.mvp_node.children.forEach(child => {
|
||
const widget = child.getComponent(Widget);
|
||
if (widget) widget.updateAlignment();
|
||
child.children.forEach(subChild => {
|
||
const subWidget = subChild.getComponent(Widget);
|
||
if (subWidget) subWidget.updateAlignment();
|
||
});
|
||
});
|
||
|
||
// ---- 文本信息 ----
|
||
const heroLv = mvp.lv || 1;
|
||
const suffix = heroLv >= 2 ? "★".repeat(heroLv - 1) : "";
|
||
if (name_node) {
|
||
const label = name_node.getComponent(Label);
|
||
if (label) label.string = `${suffix}${hero.name || ""}${suffix}`;
|
||
const currentPos = name_node.position;
|
||
name_node.setPosition(currentPos.x, isEnlarged ? 8 : -70, currentPos.z);
|
||
}
|
||
|
||
if (ap_node) {
|
||
ap_node.active = true;
|
||
const valNode = ap_node.getChildByName("val");
|
||
if (valNode) {
|
||
const label = valNode.getComponent(Label);
|
||
if (label) label.string = `${Math.floor(mvp.ap)}`;
|
||
}
|
||
}
|
||
|
||
if (hp_node) {
|
||
hp_node.active = true;
|
||
const valNode = hp_node.getChildByName("val");
|
||
if (valNode) {
|
||
const label = valNode.getComponent(Label);
|
||
if (label) label.string = `${Math.floor(mvp.hp_max)}`;
|
||
}
|
||
}
|
||
|
||
if (oinfo_node) {
|
||
oinfo_node.active = isEnlarged;
|
||
const infoLabel = oinfo_node.getChildByName("info")?.getComponent(Label);
|
||
if (infoLabel) infoLabel.string = `${hero.info || ""}`;
|
||
}
|
||
|
||
if (cost_node) {
|
||
cost_node.active = false; // 结算时不显示金币费用
|
||
}
|
||
|
||
// ---- 图标动画 ----
|
||
if (icon_node) {
|
||
this.updateHeroAnimation(icon_node, uuid);
|
||
}
|
||
}
|
||
|
||
private updateHeroAnimation(node: Node, uuid: number) {
|
||
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
||
if (sprite) sprite.spriteFrame = null;
|
||
|
||
const hero = HeroInfo[uuid];
|
||
if (!hero) return;
|
||
|
||
const anim = node.getComponent(Animation) || node.addComponent(Animation);
|
||
|
||
// clear animation clips
|
||
const clips = anim.clips;
|
||
for (let i = clips.length - 1; i >= 0; i--) {
|
||
const clip = clips[i];
|
||
if (clip) anim.removeClip(clip);
|
||
}
|
||
|
||
const path = `game/heros/hero/${hero.path}/idle`;
|
||
resources.load(path, AnimationClip, (err, clip) => {
|
||
if (err || !clip) {
|
||
mLogger.log(this.debugMode, "VictoryComp", `load hero animation failed ${uuid}`, err);
|
||
return;
|
||
}
|
||
// avoid state conflict
|
||
if (!this.node.isValid || !this.mvp_node || !this.mvp_node.active) return;
|
||
|
||
const currentClips = anim.clips;
|
||
for (let i = currentClips.length - 1; i >= 0; i--) {
|
||
const c = currentClips[i];
|
||
if (c) anim.removeClip(c);
|
||
}
|
||
|
||
anim.addClip(clip);
|
||
anim.play("idle");
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取满足条件的最高等级的亮点成就
|
||
* @param type 亮点类型
|
||
* @param value 玩家实际达成的值
|
||
* @returns 达成的最高等级配置,未达成返回 null
|
||
*/
|
||
private getHighestHighlightLevel(type: HighlightType, value: number): HighlightLevel | null {
|
||
const config = HighlightSet[type];
|
||
if (!config || !config.levels) return null;
|
||
|
||
let highest: HighlightLevel | null = null;
|
||
for (const levelConfig of config.levels) {
|
||
if (value >= levelConfig.threshold) {
|
||
highest = levelConfig;
|
||
}
|
||
}
|
||
return highest;
|
||
}
|
||
|
||
/**
|
||
* 计算并获取所有达成的最高亮点配置数组,用于加分和UI展示
|
||
*/
|
||
private getAchievedHighlights(s: any): { type: HighlightType, config: HighlightLevel, value: number }[] {
|
||
const achieved: { type: HighlightType, config: HighlightLevel, value: number }[] = [];
|
||
|
||
// 计算辅助比例
|
||
const refreshRatio = s.refresh_count > 0 ? (s.refresh_hit_count / s.refresh_count) : 0;
|
||
const goldRatio = s.gold_earned > 0 ? (s.gold_spent / s.gold_earned) : 0;
|
||
|
||
// 判定表:每个维度对应的值
|
||
const checkList: { type: HighlightType, value: number }[] = [
|
||
{ type: HighlightType.CritMaster, value: s.crt_count },
|
||
{ type: HighlightType.DeathExpert, value: s.dead_trigger_count },
|
||
{ type: HighlightType.IronWall, value: s.shield_block_count },
|
||
{ type: HighlightType.WindStorm, value: s.wf_count },
|
||
{ type: HighlightType.OneHitKill, value: Math.floor(s.highest_dmg) },
|
||
{ type: HighlightType.HealingLight, value: Math.floor(s.heal_total) },
|
||
{ type: HighlightType.PerfectClear, value: (s.passed_wave_20 && s.wave_all_alive_count >= 20) ? 1 : 0 },
|
||
{ type: HighlightType.LuckyKing, value: refreshRatio },
|
||
{ type: HighlightType.ThriftyPlayer, value: goldRatio }
|
||
];
|
||
|
||
for (const item of checkList) {
|
||
const levelConfig = this.getHighestHighlightLevel(item.type, item.value);
|
||
if (levelConfig) {
|
||
achieved.push({ type: item.type, config: levelConfig, value: item.value });
|
||
}
|
||
}
|
||
|
||
return achieved;
|
||
}
|
||
|
||
/**
|
||
* 计算单局总分并更新到 smc.vmdata.scores.score。
|
||
*/
|
||
private calculateTotalScore() {
|
||
const s = smc.vmdata.scores;
|
||
|
||
// 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);
|
||
|
||
// 2. 输出分:衡量伤害能力——团队火力如何。
|
||
s.score_output = Math.floor(s.total_dmg / 100) * 10
|
||
+ (s.crt_count * 5)
|
||
+ (s.wf_count * 5)
|
||
+ (s.highest_dmg * 2);
|
||
|
||
// 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(s.score_combat + s.score_output + s.score_defense + s.score_build + s.score_efficiency + highlightBonus);
|
||
|
||
// 判定是否打破历史最高分记录
|
||
let isNewRecord = false;
|
||
if (s.score > smc.data.score) {
|
||
smc.data.score = s.score;
|
||
isNewRecord = true;
|
||
// 更新云端/本地存储,保存新记录
|
||
if (typeof smc.updateCloudData === "function") {
|
||
smc.updateCloudData();
|
||
}
|
||
}
|
||
|
||
// 借用 scores 对象传递新记录标记,供 UI 渲染使用
|
||
(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,
|
||
isNewRecord: isNewRecord
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 渲染得分条与亮点标签
|
||
* 依赖各维度对应的UI节点(需要在Cocos Creator中拖入绑定)
|
||
*/
|
||
private renderScores() {
|
||
const s = smc.vmdata.scores;
|
||
|
||
// 渲染总分
|
||
if (this.total_score_label) {
|
||
this.total_score_label.string = `${s.score}`;
|
||
|
||
// 判定是否是新记录,如果是则激活 new 节点
|
||
const isNewRecord = (s as any).isNewRecord === true;
|
||
const newNode = this.total_score_label.node.getChildByName("new");
|
||
if (newNode) {
|
||
newNode.active = isNewRecord;
|
||
}
|
||
}
|
||
|
||
// 通用渲染单个维度的函数
|
||
const renderDim = (node: Node, score: number, maxScore: number) => {
|
||
if (!node) return;
|
||
const lab = node.getChildByName("score_label")?.getComponent(Label);
|
||
if (lab) lab.string = `${score}`;
|
||
|
||
const bar = node.getChildByName("progress_bar")?.getComponent(ProgressBar);
|
||
if (bar) {
|
||
// 根据该维度得分占“预期满分”的比例设置进度条(fillRange)
|
||
bar.progress = Math.min(1, Math.max(0, score / maxScore));
|
||
}
|
||
};
|
||
|
||
// TODO: 进度条的最大值可按设计期望自行调整,目前为占位预估值
|
||
renderDim(this.combat_node, s.score_combat, 3000);
|
||
renderDim(this.output_node, s.score_output, 3000);
|
||
renderDim(this.defense_node, s.score_defense, 1500);
|
||
renderDim(this.build_node, s.score_build, 1000);
|
||
renderDim(this.efficiency_node, s.score_efficiency, 200);
|
||
|
||
// 渲染成就亮点标签
|
||
this.renderHighlights();
|
||
}
|
||
|
||
/**
|
||
* 根据当前局数据匹配并生成对应的亮点标签(成就)
|
||
*/
|
||
private renderHighlights() {
|
||
if (!this.highlights_container || !this.highlight_prefab) return;
|
||
|
||
// 先清空原有的标签
|
||
this.highlights_container.removeAllChildren();
|
||
|
||
const s = smc.vmdata.scores;
|
||
// 获取所有已达成的亮点(包含对应等级的信息)
|
||
const achievedList = s.achieved_highlights || [];
|
||
|
||
// 最多显示前3个亮点(如有优先级需求,可在截取前对 achievedList 进行排序)
|
||
const displayTags = achievedList.slice(0, 3);
|
||
|
||
displayTags.forEach(item => {
|
||
const tagNode = instantiate(this.highlight_prefab);
|
||
const lab = tagNode.getComponent(Label) || tagNode.getChildByName("label")?.getComponent(Label);
|
||
|
||
if (lab) {
|
||
// 获取主配置和等级配置
|
||
const typeConfig = HighlightSet[item.type];
|
||
const levelConfig = item.config;
|
||
|
||
// 格式化描述(替换占位符 {0} 为实际所需达成的阈值或当前值)
|
||
let descStr = levelConfig.desc.replace("{0}", levelConfig.threshold.toString());
|
||
|
||
// 显示:[图标] 称号 (描述)
|
||
lab.string = `${typeConfig.icon} ${levelConfig.title} (${descStr})`;
|
||
}
|
||
this.highlights_container.addChild(tagNode);
|
||
});
|
||
}
|
||
|
||
// ======================== 操作入口 ========================
|
||
|
||
/** 退出战斗:清理数据 → 触发任务结束 → 关闭弹窗 */
|
||
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.gui.open(UIID.Mission)
|
||
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()
|
||
}
|
||
} |