import { sys, resources, SpriteAtlas } from "cc"; import { VM } from "../../../../extensions/oops-plugin-framework/assets/libs/model-view/ViewModel"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { Initialize } from "../initialize/Initialize"; import { GameMap } from "../map/GameMap"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { GameEvent } from "./config/GameEvent"; import { GameScoreStats } from "./config/HeroAttrs"; import { mLogger } from "./Logger"; import { gameDataSync } from "./GameDataSync"; import { FightSet } from "./config/GameSet"; /** * 用远程数据覆盖本地数据(统一方法) * @param remoteData 远程数据(云端或本地调试) */ export interface GameDate { openid?: string, score?: number, mission?: number, diamond?: number, gold?: number, task?: number, noStop?: boolean, showInfo?: boolean, tip_done?: { start: boolean, hero: boolean, equip: boolean, skill: boolean, shop: boolean, }, switch_effect?: boolean, // 音效开关 switch_music?: boolean, // 音乐开关 timestamp?: number, // 用于比对本地与云端数据的最新状态 } export interface CloudData { openid: string; data: GameDate; } /** * 死亡英雄快照:英雄真实死亡时记录,供后续复活机制(重新召唤)消费。 * 仅记录静态/成长数据,不含战斗内临时状态(buff、CD 等)。 */ export interface DeadHeroRecord { /** 英雄模板 uuid(决定重新召唤哪个英雄) */ uuid: number; /** 死亡时的英雄等级(复活后继承) */ lv: number; /** 卡牌等级(驱动 bg_node 颜色) */ card_lv: number; /** 死亡时占用的站位索引(复活时优先归位,-1 表示未分配) */ posIndex: number; /** 死亡时的回合数(用于统计/按回合清理) */ wave: number; } /** 游戏模块 */ @ecs.register('SingletonModule') export class SingletonModuleComp extends ecs.Comp { private debugMode: boolean = false; /** 游戏初始化模块 */ initialize: Initialize = null!; /** 游戏地图 */ map: GameMap = null!; /** 全局缓存的通用图集 */ uiconsAtlas: SpriteAtlas | null = null; mission: any = { status: 0, //0:未开始 1:进行中 2:胜利 3:失败 play: false, pause: false, in_select: false, in_fight: false, stop_mon_action: false, heroGrid: [-1, -1, -1, -1, -1, -1], monGrid: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], /** 本局真实死亡的英雄快照列表,供后续复活机制消费;整局结束(MissionEnd)时清空 */ dead_heroes: [] as DeadHeroRecord[], }; data: any = { openid: '', score: 0, mission: 1, diamond: 100, //商店购买 及 双倍奖励资源 gold: 1000, task: 0, noStop: false, showInfo: true, /** 新手指引完成标记:false=需要提醒,true=已提醒过(仅提醒 1 次) */ tip_done: { start: false, // 主页开始按钮 hero: false, // 召唤英雄面板按钮 equip: false, // 购买装备面板按钮 skill: false, // 购买技能卷轴面板按钮 shop: false, // 购买药品面板按钮 }, } vmdata: any = { game_over: false, game_pause: false, mission_data: { mon_num: 0,//怪物数量 pending_mon_num: 0,//待刷出怪物数量(MissionMonComp 每帧写入,供回合结束检测与 HUD 进度) wave_early_skip: 0,//本回合清场加速累计节省的秒数(MissionComp 用于还原 DDA 判定口径) hero_num: 0,//英雄数量 hero_max_num: FightSet.HERO_MAX_NUM,//英雄可召唤上限 hero_extend_max_num: FightSet.HERO_MAX_NUM + 1,//英雄可拓展上限 wave_time_num: 0,//回合时间 in_fight: false, fight_time: 0,//战斗时间 level: 1,//关卡等级 max_mission: 4,//最大关卡 coin: 0, refresh_stone: 0,//刷新石(刷新卡池时优先消耗,不足时消耗金币) time: 15 * 60,//游戏时间 }, scores: { score: 0, // 基础得分 // 战斗统计 crt_count: 0, // 暴击次数 wf_count: 0, // 风怒次数 dod_count: 0, // 闪避次数 back_count: 0, // 击退次数 stun_count: 0, // 击晕次数 freeze_count: 0, // 冰冻次数 // 伤害统计 total_dmg: 0, // 总伤害 atk_count: 0, // 攻击次数 avg_dmg: 0, // 平均伤害 thorns_dmg: 0, // 反伤伤害 crit_dmg_total: 0, // 暴击伤害总额 // 生存统计 heal_total: 0, // 治疗总量 lifesteal_total: 0, // 吸血总量 shield_block_count: 0, dead_trigger_count: 0, // 资源统计 exp_total: 0, // 经验总数 gold_total: 0, // 金币总数 gold_earned: 0, gold_spent: 0, refresh_count: 0, refresh_hit_count: 0, // 击杀统计 melee_kill_count: 0, // 近战怪击杀数量 remote_kill_count: 0, // 远程怪击杀数量 elite_kill_count: 0, // 精英怪击杀数量 boss_kill_count: 0, // Boss击杀数 // 战绩统计 wave_win_count: 0, wave_remain_monsters: 0, wave_all_alive_count: 0, passed_wave_20: false, highest_dmg: 0, score_combat: 0, score_output: 0, score_defense: 0, score_build: 0, score_efficiency: 0, achieved_highlights: [], } as GameScoreStats, gold: 0, // 金币数据(MVVM绑定字段) switch_effect: true, // 音效开关(MVVM绑定字段) switch_music: true, // 音乐开关(MVVM绑定字段) }; vmAdd() { VM.add(this.vmdata, "data"); // mLogger.log(this.debugMode, 'SMC', "[MissionComp]局内数据初始化",smc.vmdata.mission_data) } reset() { for (var key in this.vmdata) { delete this.vmdata[key]; } } /** * 重置单局评分数据 * 在每次新战斗开始时调用,确保上一局的得分不会带入新一局 */ resetScores() { this.vmdata.scores = { score: 0, crt_count: 0, wf_count: 0, dod_count: 0, back_count: 0, stun_count: 0, freeze_count: 0, total_dmg: 0, atk_count: 0, avg_dmg: 0, thorns_dmg: 0, crit_dmg_total: 0, heal_total: 0, lifesteal_total: 0, shield_block_count: 0, dead_trigger_count: 0, exp_total: 0, gold_total: 0, gold_earned: 0, gold_spent: 0, refresh_count: 0, refresh_hit_count: 0, melee_kill_count: 0, remote_kill_count: 0, elite_kill_count: 0, boss_kill_count: 0, wave_win_count: 0, wave_remain_monsters: 0, wave_all_alive_count: 0, passed_wave_20: false, highest_dmg: 0, score_combat: 0, score_output: 0, score_defense: 0, score_build: 0, score_efficiency: 0, achieved_highlights: [], } as GameScoreStats; } // ==================== 数据管理方法 ==================== /** * 判断是否为微信客户端 */ isWxClient(): boolean { return gameDataSync.isWxClient(); } updateCloudData() { return gameDataSync.updateCloudData(); } getCloudData() { gameDataSync.syncFromCloud(); } public async overrideLocalDataWithRemote(cloudData: any) { try { // 直接覆盖基础游戏数据 if (cloudData.openid) { this.data.openid = cloudData.openid; } // 直接覆盖出战英雄配置 if (cloudData.data) { Object.assign(this.data, cloudData.data); } // 触发UI更新 oops.message.dispatchEvent(GameEvent.GOLD_UPDATE); } catch (error) { mLogger.error(this.debugMode, 'SMC', `[SMC]: 数据覆盖失败:`, error); } } getGameDate() { let data: GameDate = { ...this.data, timestamp: Date.now() // 每次获取当前数据结构时都附带最新的时间戳 }; return data; } updateGold(gold: number, is_sync: boolean = true) { this.vmdata.gold += gold; if (is_sync) { gameDataSync.markDataDirty(); } oops.message.dispatchEvent(GameEvent.GOLD_UPDATE) return true } updateSwitchEffect(isOpen: boolean, is_sync: boolean = true) { this.vmdata.switch_effect = isOpen; if (oops.audio) oops.audio.switchEffect = isOpen; if (is_sync) { gameDataSync.markDataDirty(); } } updateSwitchMusic(isOpen: boolean, is_sync: boolean = true) { this.vmdata.switch_music = isOpen; if (oops.audio) oops.audio.switchMusic = isOpen; if (is_sync) { gameDataSync.markDataDirty(); } } /** * 在游戏载入早期调用,预加载常用图集 */ preloadCommonAssets() { resources.load("gui/ui6", SpriteAtlas, (err, atlas) => { if (!err && atlas) { // 增加引用计数,防止图集被引擎自动垃圾回收(GC)导致底层 spriteFrames 为 null atlas.addRef(); this.uiconsAtlas = atlas; } else { mLogger.error(this.debugMode, 'SMC', "预加载 gui/ui6 图集失败:", err); } }); } } export var smc: SingletonModuleComp = ecs.getSingleton(SingletonModuleComp);