GameModelManager.ts 14.7 KB
import { Emitter, CompositeDisposable } from "simba-eventkit";
import { shallowEqual } from "simba-utils";
import { GameRecord } from "../../avg/game-data/GameRecord";
import { Language, ReadonlyPlot, ReadonlyPlots, SentenceType } from "../../avg/model/PlotModel";
import { PlotManager } from "../../avg/PlotManager";
import { GameConstData } from "../../common/gameplay/gamedata/GameConstData";
import { ConfigManager } from "../../common/gameplay/managers/ConfigManager";
import { ResUtils } from "../../common/utils/ResUtils";
import { bannerRewardsConfig } from "../../config/BannerRewardsConfig";
import { constConfig } from "../../config/ConstConfig";
import { itemConfig } from "../../config/ItemConfig";
import { IRole, role } from "../../config/Role";
import { PlotSceneType } from "../Enums";
import GameRoleDataModel from "./GameRoleDataModel";
import { MessageSceneModel } from "./MessageSceneModel";
import { EditorEvents } from "../../avg/EditorEvents";
import { achievementConfig } from "../../config/AchievementConfig";
import { dailyQuestConfig } from "../../config/DailyQuestConfig";
import { DatingEventSceneModel, DatingEventStatus } from "./DatingEventSceneModel";
import { dateSceneConfig } from "../../config/DateSceneConfig";
import { DateType } from "../../avg/EditorEnums";
import { FuncStateEnum, RedPotStateEnum } from "../../common/gameplay/gamedata/GameEnumData";
import { i18nConfig } from "../../config/I18nConfig";

export namespace GameModelManager {
    let emitter = new Emitter;

    export const MessageSceneChanged = emitter.createEvent<(scenes: MessageSceneModel[]) => void>();
    export const RedPotReduction = emitter.createEvent<(key: string) => void>();       //红点还原
    export const MainDatingEventChanged = emitter.createEvent<(scenes: DatingEventSceneModel[]) => void>();
    /**报幕数据准备就绪事件 */
    export const ForceClickDataReady = emitter.createEvent<() => void>();

    let messageScenes: MessageSceneModel[] | undefined;
    let mainDatingScenes: DatingEventSceneModel[] | undefined;
    let currPlots: ReadonlyPlot[] | undefined;
    let phoneCallIndex: number = -1;

    let unlockGift = false;

    export function isGiftUnlock() { return unlockGift; }

    export async function rollbackPhoneCallIfNeeded() {
        // if (phoneCallIndex < 0) {
        //     console.error("Error: no phone call.");
        // }
        if (phoneCallIndex >= 0 && phoneCallIndex < GameRecord.getCurrentRecordItems().length - 1) {
            let ret = await PlotManager.rollbackToIndex(phoneCallIndex);
            savePhoneCall();
            return ret;
        } else {
            return PlotManager.getCurrentPlots();
        }
    }

    //#region  //数据模块 
    let roleDatasMap: Map<number, GameRoleDataModel> = new Map<number, GameRoleDataModel>();
    let languagesMap: Map<number, string> = new Map<number, string>();       //语言
    //#endregion

    export function getCurrentChapterId() {
        return currPlots![0].chapterId;
    }

    export function getMessageScenesModel() {
        return messageScenes!;
    }

    export function getMessageSceneModel(id: number) {
        return messageScenes!.find(v => v.id === id);
    }
    export function getMainDatingEventScenesModel() {
        return mainDatingScenes!;
    }

    export function getMainDatingEventSceneModel(id: number) {
        return mainDatingScenes!.find(v => v.id === id);
    }


    async function initConfigData() {
        await ConfigManager.initConfigData();
        initLanguageConfig();
        initRoleConfig();
    }

    function initRoleConfig() {
        let cfg = ConfigManager.getAllConfig(role);
        for (let id in cfg) {
            let element = cfg[id] as IRole;
            if (element) {
                let data = new GameRoleDataModel();
                data.setConfig(element);
                roleDatasMap.set(element.id, data);
            }
        }
    }

    function getLanguagePath() {
        return "userdata/language/chinese"
    }
    async function initLanguageConfig() {
        let cfgFile = await ResUtils.loadRes<any>(getLanguagePath(), cc.JsonAsset);
        if (cfgFile) {
            let lans: Language[] = cfgFile as Language[];
            for (let i = 0; i < lans.length; ++i) {
                let element = lans[i] as Language;
                if (element) {
                    languagesMap.set(element.id, element.value);
                }
            }
        }
    }

    export function getLanguageTxt(id: number): string {
        if (languagesMap.has(id)) {
            return languagesMap.get(id) as string;
        } else {
            console.log("current language id is miss " + id);
            return "";
        }
    }

    export function getConfigLanguageTxt(id: number): string {
        return ConfigManager.getConfig(i18nConfig, id).cn;
    }

    export function getConstConfig(id: number) {
        return ConfigManager.getConfig(constConfig, id);
    }

    export function getItemConfig(id: number) {
        return ConfigManager.getConfig(itemConfig, id);
    }

    export function getItemConfigs() {
        return ConfigManager.getAllConfig(itemConfig);
    }

    export function getBannerRewardsConfigs() {
        return ConfigManager.getAllConfig(bannerRewardsConfig)
    }

    export function getBannerRewardsConfig(id: number) {
        return ConfigManager.getConfig(bannerRewardsConfig, id);
    }

    export function getDailyQuestConfigs() {
        return ConfigManager.getAllConfig(dailyQuestConfig)
    }

    export function getDailyQuestConfig(id: number) {
        return ConfigManager.getConfig(dailyQuestConfig, id);
    }

    export function getAchievementConfigs() {
        return ConfigManager.getAllConfig(achievementConfig)
    }

    export function getAchievementConfig(id: number) {
        return ConfigManager.getConfig(achievementConfig, id);
    }

    export function getRoleData(id: number) {
        return roleDatasMap.get(id);
    }

    export function getPlayerData() {
        return GameModelManager.getRoleData(GameConstData.GAME_CONST_PLAYER_ROLE_VALUE)!;
    }

    let disposable: CompositeDisposable;

    export async function init() {
        disposable = new CompositeDisposable;
        await initConfigData();
        phoneCallIndex = GameRecord.recordVariables["gm.pci"] as number;
        unlockGift = !!GameRecord.recordVariables["gm.unlockGift"];
        if (phoneCallIndex === undefined) phoneCallIndex = -1;
        let initRet: Promise<any>[] = [];
        { // init message models
            let ids = GameRecord.recordVariables["gm.ms"] as any;
            if (ids !== undefined) {
                messageScenes = ids.map((id: number) => new MessageSceneModel(id));
                initRet.push(...messageScenes!.map((s) => s.initFromRecords()));
            }
        }
        { // init main dating models
            let ids = GameRecord.recordVariables["gm.md"] as any;
            if (ids !== undefined) {
                mainDatingScenes = ids.map((id: number) => new DatingEventSceneModel(id));
                initRet.push(...mainDatingScenes!.map((s) => s.initFromRecords()));
            }
        }

        if (initRet.length) {
            await Promise.all(initRet);
        }

        disposable.add(PlotManager.PlotWillStart.on(handlePlotWillStart));
        disposable.add(PlotManager.PlotsRollbackEvent.on(handlePlotsRollback));
        disposable.add(EditorEvents.UNLOCK_GIFT.on(() => {
            unlockGift = true;
            GameRecord.recordVariables["gm.unlockGift"] = 1;
        }));
        initPlots();

        for (let scene of mainDatingScenes!) { // 检查主线剧情状态
            if (currPlots![0].plotSceneType !== PlotSceneType.DatingEvent ||
                (scene.id !== currPlots![0].plotSceneTypeId && scene.status !== DatingEventStatus.Completed)) {
                scene.status = DatingEventStatus.Completed;
            }
        }
    }

    function save() {
        let oldValue = GameRecord.recordVariables["gm.ms"];
        let newValue = messageScenes!.map(v => v.id) as any;
        if (!shallowEqual(oldValue, newValue)) {
            GameRecord.recordVariables["gm.ms"] = newValue;
        }
        oldValue = GameRecord.recordVariables["gm.md"];
        newValue = mainDatingScenes!.map(v => v.id) as any;
        if (!shallowEqual(oldValue, newValue)) {
            GameRecord.recordVariables["gm.md"] = newValue;
        }
    }

    function savePhoneCall() {
        GameRecord.recordVariables["gm.pci"] = phoneCallIndex;
        GameRecord.saveRecord();
    }

    async function initPlots() {
        let plots = PlotManager.getCurrentPlots();
        if (!currPlots) {
            currPlots = [...plots];
            if (!messageScenes) { // 第一次进游戏,根据剧情初始化数据
                messageScenes = [];
            }
            if (!mainDatingScenes) {
                mainDatingScenes = [];
            }
            for (let i = 0; i < plots.length; i++) {
                let plot = plots[i];
                if (plot.plotSceneType === PlotSceneType.Message) {
                    let index = messageScenes.findIndex(v => v.id === plot.plotSceneTypeId);
                    if (index < 0) {
                        index = 0;
                        let model = new MessageSceneModel(plot.plotSceneTypeId);
                        model.firstPlot = plot;
                        messageScenes.unshift(model);
                    }
                    // if (plot.sentences[0].content && plot.sentences[0].content.type !== SentenceType.SELECT) {
                    // messageScenes[index].lastPlot = plot;
                    // }
                }  else if (plot.plotSceneType === PlotSceneType.DatingEvent) {
                    let cfg = ConfigManager.getConfig(dateSceneConfig, plot.plotSceneTypeId);
                    if (cfg.DateType === DateType.Date_Normal) { // 主线约会剧情
                        if (plots[0] !== plot) { console.error("主线约会剧情应在主线开启"); continue; }
                        if (mainDatingScenes.findIndex(v => v.id === plot.plotSceneTypeId) < 0) {
                            let model = new DatingEventSceneModel(plot.plotSceneTypeId);
                            model.firstPlot = plot;
                            model.status = DatingEventStatus.New;
                            mainDatingScenes.push(model);
                        }
                    }
                }
            }
            save();
        }
    }

    async function handlePlotWillStart(plot: ReadonlyPlot, branch: number) {
        handleNewPlot(currPlots![branch], plot, branch);
        currPlots![branch] = plot;
        // for (let i = 0; i < plots.length; i++) {
        //     if (!currPlots![i] || (currPlots![i].id !== plots[i].id)) {
        //         handleNewPlot(currPlots![i], plots[i], i);
        //         currPlots![i] = plots[i];
        //     }
        // }
    }

    async function handlePlotsRollback(plots: ReadonlyPlots) {
        messageScenes = undefined;
        currPlots = undefined;
        disposable.dispose();
        await init();
        GameRecord.startTransaction();
        GameRecord.endTransaction(true);
    }

    async function handleNewPlot(prevPlot: ReadonlyPlot | undefined, plot: ReadonlyPlot, branch: number) {
        if (plot.plotSceneType === PlotSceneType.Message) {
            let idx = messageScenes!.findIndex(v => v.id === plot.plotSceneTypeId);
            if (idx >= 0) { // 原会话中有新消息
                let model = messageScenes![idx];
                if (idx > 0) {
                    for (let i = idx; i > 0; i--) {
                        messageScenes![i] = messageScenes![i - 1];
                    }
                    messageScenes![0] = model;
                }
                if (plot.sentences[0].content && plot.sentences[0].content.type !== SentenceType.SELECT) {
                    // model.lastPlot = plot;
                }
            } else { // 新会话
                let model = new MessageSceneModel(plot.plotSceneTypeId);
                model.firstPlot = plot;
                if (plot.sentences[0].content && plot.sentences[0].content.type !== SentenceType.SELECT) {
                    // model.lastPlot = plot;
                }
                messageScenes!.unshift(model);
            }
            MessageSceneChanged.emit(messageScenes!);
            save();
        } else if (plot.plotSceneType === PlotSceneType.PhoneCall) {
            if (!prevPlot || prevPlot.plotSceneType !== PlotSceneType.PhoneCall) {
                phoneCallIndex = GameRecord.getCurrentRecordItems().length; // 当前存档还没生成,不需要减1
                savePhoneCall();
            }
        } else if (plot.plotSceneType === PlotSceneType.DatingEvent) {
            let cfg = ConfigManager.getConfig(dateSceneConfig, plot.plotSceneTypeId);
            if (cfg.DateType === DateType.Date_Normal) { // 主线约会剧情
                if (branch !== 0) { console.error("主线约会剧情应在主线开启"); return; }
                let idx = mainDatingScenes!.findIndex(v => v.id === plot.plotSceneTypeId);
                if (idx < 0) { // 新开启
                    let model = new DatingEventSceneModel(plot.plotSceneTypeId);
                    model.firstPlot = plot;
                    model.status = DatingEventStatus.New;
                    mainDatingScenes!.push(model);
                    MainDatingEventChanged.emit(mainDatingScenes!);
                }
                save();
            }
        }

        if (prevPlot && prevPlot.plotSceneType === PlotSceneType.PhoneCall && plot.plotSceneType !== PlotSceneType.PhoneCall) {
            phoneCallIndex = -1;
            savePhoneCall();
        }

        if (prevPlot && (prevPlot.plotSceneType !== plot.plotSceneType || prevPlot.plotSceneTypeId !== plot.plotSceneTypeId)) { // 切换场景
            if (prevPlot.plotSceneType === PlotSceneType.Message) { // 最后一个是选项的情况下,此时已做选择,记录最后一条剧情
                // let model = messageScenes!.find(v => v.id === prevPlot.plotSceneTypeId);
                // model!.lastPlot = prevPlot;
            }
            if (prevPlot.plotSceneType === PlotSceneType.DatingEvent) {
                let model = mainDatingScenes!.find(v => v.id === prevPlot.plotSceneTypeId);
                if (model) model.status = DatingEventStatus.Completed;
            }
        }
    }

    export function checkFuncUnloced(key: string) {
        return GameRecord.recordVariables[key] === FuncStateEnum.locked ? false : true;
    }

    export function checkRedPot(key: string) {
        return GameRecord.globalVariables["red_pot_" + key] === RedPotStateEnum.Show;
    }

    export function setRedPotState(key: string, state: RedPotStateEnum) {
        GameRecord.globalVariables["red_pot_" + key] = state;
        GameRecord.autoSave();
    }
}