#include "main.h"
#include "MyFPS.h"
#include "MyView.h"
#include "MyGame.h"
#include "Entity.h"
#include "OrganyaPlayer.h"
#include <SDL_syswm.h>
#include "TranslateLua.h"
#include "Pathfinding.h"
#include "MyUtil.h"
#include <algorithm>
#include <filesystem>
#define WINDOW_W 640
#define WINDOW_H 360
#define WINDOW_TITLE u8"The Mimiga Disappearance"
constexpr auto FIXED_DELTA_TIME = (1.0f / 60.0f); // 60 FPS 的固定时间步长;
#pragma comment(lib,"imm32.lib")
//系统变量
HWND hwnd = nullptr;
SDL_Window* sdl_window = nullptr;
MyDX11Renderer pRenderer;
MySDLInput pInput;
JoystickInput pJoystickInput;
MusicPlayer pMusic;
OrganyaPlayer orgPlayer;
bool bFullScreen = false;
bool bQuitGame = false;
bool isFreezed = false;
//已载入的资源
std::unordered_map<std::wstring, MyTextureAndSRV> loadedPxaTexture;
std::unordered_map<std::wstring, MyTextureAndSRV> loadedPxaTexture_InterArea;
std::unordered_map<std::wstring, MyTextureAndSRV> loadedPidTexture;
std::unordered_map<std::wstring, MyTextureAndSRV> loadedPidTexture_InterArea;
std::unordered_map<std::wstring, MyTextureAndSRV> loadedMsgboxTexture;
std::unordered_map<std::wstring, MyTextureAndSRV> loadedFaceTexture;
std::unordered_map<std::wstring, MyTextureAndSRV> loadedFaceTexture_InterArea;
std::unordered_map<std::wstring, MyTextureAndSRV> loadedSysTexture;
std::unordered_map<std::wstring, MyTextureAndSRV> loadedMcuiTexture;
std::unordered_map<UINT, MyTextureAndSRV> loadedResTexture;
std::map<std::wstring, std::wstring> roomNameTable;//翻译好的房间名表
std::unordered_map<std::wstring, Mix_Chunk*> loadedSound;//已载入的声音
std::unordered_map<int, void*> loadedDrum;//已载入的鼓
std::unordered_map<wchar_t, MyFontData> dbgFontMap;
TTF_Font* dbgFont = nullptr;
//调试变量
bool bShowDebug = false;
bool bLowQuality = false;
float bLowQuality_lastScale = 0;
bool bINI = false;//重新开始游戏
bool bESC = false;//返回标题画面
bool gameSpeedX3 = false;//3倍游戏速度
bool bLockCamera = false;//锁住相机镜头
//fps帧率
MyFPS myFPS;
//游戏变量
MyView view;
MyGame myGame;
MyView pausemenuView;
MyGame pausemenuGame;
InputSetting_Keyboard inputSetting_player1Keyboard;
InputSetting_Joystick inputSetting_player1Joystick;
InputSetting_Keyboard inputSetting_player2Keyboard;
InputSetting_Joystick inputSetting_player2Joystick;
int player1ControllerType = 0;//0键盘、1手柄
int player2ControllerType = 0;//0键盘、1手柄
bool pauseMenuBackGame = false;
bool pauseMenuBackTitle = false;
//最后一次游玩的存档位置
int lastPlaySlotPage = 0;
int lastPlaySlotIndex = 0;
//新游戏参数
std::wstring gameOption_gameName;
int gameOption_playerNum;
int gameOption_difficulty;
std::set<std::wstring> playingSound;
std::unordered_map<std::wstring, int> playingSound_cooldown;//音效表
int playingSound_cooldown_max = 5;
bool bDisableSound = false;//只在跳帧时启用
//实体函数
std::unordered_map<std::wstring, EntityDll> loadedEntityDll;
//命令行
std::vector<std::wstring> cmdList;
//使用的游戏存档名
std::wstring saveName;
//实体数据存档变量
std::unordered_map<std::wstring, std::unordered_map<std::wstring, std::wstring>> entitySaveVar;
//语言
std::wstring language_now;//当前语言设置
//音效表
std::unordered_map<std::wstring, std::wstring> soundTable;//音效表
std::unordered_map<int, std::wstring> drumTable;//鼓表
//字体表
std::string pixelFont_size12;
int pixelFont_size12_arg;
std::string pixelFont_size14;
int pixelFont_size14_arg;
std::string highQualityFont;
float msgboxTextOffy;
//主游戏脚本指令
ScriptCmd* mainGameCmd = nullptr;
//录制
bool record_done = false;
bool recording = false;
std::vector<GameInput> record;
HANDLE hMapFile = nullptr;
void* hMapFile_data = nullptr;
//面孔表
std::unordered_map<std::wstring, bool> facefileTable;
std::unordered_map<std::wstring, FaceDisplayInfo> faceIDTable;
//Windows消息
WNDPROC originalWndProc = nullptr;
//作弊
bool cheating = false;
//主函数
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
    //命令行
    SplitString(&cmdList, lpCmdLine, L' ');
	//禁止DPI缩放
	MySetupDPIAwareness();
	//初始化库
    InitZLib();
    SDL_Init(SDL_INIT_EVERYTHING);
    TTF_Init();
    Mix_Init(MIX_INIT_OGG | MIX_INIT_FLAC);
    IMG_Init(IMG_INIT_PNG);
    Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024);
    int numChannel = Mix_AllocateChannels(64);
    //载入字体表
    LoadFontTable();
    //载入面孔表
    LoadFaceTable();
    //载入音乐
    pMusic.Init();
    //载入Organya
    orgPlayer.Init();
    //加载实体dll
    LoadEntityDll();
	//创建窗口
    MyCreateSDLWindow(&sdl_window, &hwnd);
	//初始化渲染器
	pRenderer.Init(hwnd);
    //初始化gameFunc
    GameFunc gameFunc;
    InitGameFunc(&gameFunc);
    //初始化WindowsMSG Hook
    originalWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)SubclassedWndProc);
    //初始化手柄
    pJoystickInput.Init();
    auto jL = pJoystickInput.GetGameControllerList();
    //停止文本输入
    SDL_StopTextInput();
    //如果只接入第一个手柄，默认分配给玩家1，玩家2用键盘
    if (jL->size() == 1)
    {
        inputSetting_player1Joystick.pGameController = SDL_GameControllerFromPlayerIndex(0);
        player1ControllerType = 1;
    }
    //接入两个以上手柄
    else if (jL->size() >= 2)
    {
        inputSetting_player1Joystick.pGameController = SDL_GameControllerFromPlayerIndex(0);
        player1ControllerType = 1;
        inputSetting_player2Joystick.pGameController = SDL_GameControllerFromPlayerIndex(1);
        player2ControllerType = 1;
    }
    //开启作弊
    {
        FILE* fp = nullptr;
        fopen_s(&fp, "cheating", "rb");
        if (fp)
        {
            fclose(fp);
            cheating = true;
        }
    }
    //初始化游戏
    myGame.Init(&pRenderer, &pInput, &pJoystickInput, &view, &gameFunc);
    myGame.SetMainGame();
    myGame.EnableDrawDebug(true && cheating);
    //初始化暂停菜单
    pausemenuGame.Init(&pRenderer, &pInput, &pJoystickInput, &pausemenuView, &gameFunc);
    ReloadLanguage();
    //载入声音表 
    LoadSoundTable();
    ResetScale();
    //命令行测试
    do
    {
        if (!cmdList.empty())
        {
            if (cmdList[0] == L"MapTest")
            {
                int xPos, yPos;
                swscanf_s(cmdList[3].c_str(), L"%d", &xPos);
                swscanf_s(cmdList[4].c_str(), L"%d", &yPos);
                MapTest(cmdList[1].c_str(), cmdList[2].c_str(), xPos, yPos);
                break;
            }
            else if (cmdList[0] == L"Record")
            {
                int xPos, yPos, endX, endY;
                swscanf_s(cmdList[3].c_str(), L"%d", &xPos);
                swscanf_s(cmdList[4].c_str(), L"%d", &yPos);
                swscanf_s(cmdList[5].c_str(), L"%d", &endX);
                swscanf_s(cmdList[6].c_str(), L"%d", &endY);
                Record(cmdList[1].c_str(), cmdList[2].c_str(), xPos, yPos, endX, endY);
                break;
            }
        }
    } while (false);
    //过程动画
    IntroStage();
    while (true)
    {
        //游戏菜单
        if (TitleStage())
            break;
        //游戏过程
        if (GameStage())
            break;
    }
    //退出
    Quit();
    return 0;
}

void MyCreateSDLWindow(SDL_Window** outSDL_Window, HWND* outHwnd)
{
    int graphScale = (int)round(2 * MyGetDPI() / 100.0);
    int showFlag = SDL_WINDOW_SHOWN;
    //config.xml
    //检查文件config.xml文件是否存在
    FILE* fp;
    _wfopen_s(&fp, L"config.xml", L"rb");
    //如果文件不存在则使用默认设置
    if (fp)
    {
        //如果文件存在
        fseek(fp, 0, SEEK_END);
        size_t fileLen = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        std::vector<BYTE> file_buff;
        file_buff.resize(fileLen);
        fread_s(file_buff.data(), file_buff.size(), fileLen, 1, fp);
        fclose(fp);
        file_buff.push_back(0);
        //读取XML
        tinyxml2::XMLDocument xmlDoc;
        xmlDoc.Parse((const char*)file_buff.data());
        tinyxml2::XMLElement* elem = nullptr;
        //寻找Display元素
        elem = nullptr;
        for (tinyxml2::XMLElement* pElem = xmlDoc.FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
        {
            if (!strcmp(pElem->Name(), "Display"))
            {
                elem = pElem;
                break;
            }
        }
        if (elem)
        {
            if (elem->BoolAttribute("fullscreen", false))
            {
                showFlag |= SDL_WINDOW_FULLSCREEN_DESKTOP;
                bFullScreen = true;
            }
            graphScale = (int)elem->FloatAttribute("scale", view.scale);
        }
        //寻找Option元素
        elem = nullptr;
        for (tinyxml2::XMLElement* pElem = xmlDoc.FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
        {
            if (!strcmp(pElem->Name(), "Option"))
            {
                elem = pElem;
                break;
            }
        }
        if (elem)
        {
            std::vector<wchar_t> wbuff;
            UTF8ToUTF16(elem->Attribute("language"), &wbuff);
            language_now = wbuff.data();
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    SDL_Window* pSDLWnd = SDL_CreateWindow((const char*)WINDOW_TITLE, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        WINDOW_W * graphScale, WINDOW_H * graphScale, showFlag);
    *outSDL_Window = pSDLWnd;
    view.window_w = (float)(WINDOW_W * graphScale);
    view.window_h = (float)(WINDOW_H * graphScale);
    view.grid_w = 16;
    view.grid_h = 16;
    view.scale = min(view.window_w / WINDOW_W, view.window_h / WINDOW_H);
    ResetScale();
    //取HWND
    SDL_SysWMinfo wmInfo;
    SDL_VERSION(&wmInfo.version);
    SDL_GetWindowWMInfo(pSDLWnd, &wmInfo);
    *outHwnd = wmInfo.info.win.window;
}

void SystemTask()
{
    SDL_Event e;
    const Uint8* s = SDL_GetKeyboardState(NULL);
    int bPollEvent = false;
    //切换为英文输入法
    //SwitchEnglishInput();
    while (SDL_PollEvent(&e) > 0)
    {
        bPollEvent = true;
        //系统
        switch (e.type)
        {
        case SDL_QUIT:
            Quit();
            exit(0);
            break;
        case SDL_WINDOWEVENT:
            switch (e.window.event)
            {
            case SDL_WINDOWEVENT_MINIMIZED:
            case SDL_WINDOWEVENT_FOCUS_LOST:
                FreezeGame();
                break;
            case SDL_WINDOWEVENT_RESTORED:
            case SDL_WINDOWEVENT_FOCUS_GAINED:
                ActivateGame();
                break;
            }
            break;
        }
        switch (e.key.state)
        {
        case SDL_PRESSED:
            if (s[SDL_SCANCODE_RETURN] && s[SDL_SCANCODE_RALT])
            {
                if (bFullScreen)
                    SDL_SetWindowFullscreen(sdl_window, 0);
                else
                    SDL_SetWindowFullscreen(sdl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
                bFullScreen = !bFullScreen;
                ResetScale();
            }
            break;
        }
    }
    //org音乐计时器
    orgPlayer.FadeCounterStep();
    //声音播放
    PlaySoundProc();
}

void SystemTaskRecord()
{
    const UINT64 frameRate = 1000 / 60;

    static UINT64 timeLast = SDL_GetTicks64();
    UINT64 timeNow = SDL_GetTicks64();
    SDL_Event e;
    const Uint8* s = SDL_GetKeyboardState(NULL);
    int bPollEvent = false;
    do
    {
        //切换为英文输入法
        //SwitchEnglishInput();
        while (SDL_PollEvent(&e) > 0)
        {
            bPollEvent = true;
            //系统
            switch (e.type)
            {
            case SDL_QUIT:
                Quit();
                exit(0);
                break;
            case SDL_WINDOWEVENT:
                switch (e.window.event)
                {
                case SDL_WINDOWEVENT_MINIMIZED:
                case SDL_WINDOWEVENT_FOCUS_LOST:
                case SDL_WINDOWEVENT_TAKE_FOCUS:
                    FreezeGame();
                    break;
                case SDL_WINDOWEVENT_RESTORED:
                case SDL_WINDOWEVENT_FOCUS_GAINED:
                    ActivateGame();
                    break;
                }
                break;
            }
            switch (e.key.state)
            {
            case SDL_PRESSED:
                if (s[SDL_SCANCODE_RETURN] && s[SDL_SCANCODE_RALT])
                {
                    if (bFullScreen)
                        SDL_SetWindowFullscreen(sdl_window, 0);
                    else
                        SDL_SetWindowFullscreen(sdl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
                    bFullScreen = !bFullScreen;
                    ResetScale();
                }
                break;
            }
        }
        SDL_Delay(1);
        timeNow = SDL_GetTicks64();
    } while (timeNow - timeLast < frameRate);
    timeLast += timeNow - timeLast;
    //org音乐计时器
    orgPlayer.FadeCounterStep();
    //声音播放
    PlaySoundProc();
}


MyTextureAndSRV* GetXXTexture(const wchar_t* pathFormatStr,
    const wchar_t* name, 
    std::unordered_map<std::wstring, MyTextureAndSRV>* loadedTex)
{
    auto iter = loadedTex->find(name);
    if (iter == loadedTex->end())
    {
        //单文件名
        std::vector<char> utf8;
        wchar_t wbuff[MAX_PATH];
        swprintf_s(wbuff, pathFormatStr, name);
        UTF16ToUTF8(wbuff, &utf8);
        //确认文件是否存在
        MyTextureAndSRV tas;
        FILE* fp;
        fopen_s(&fp, utf8.data(), "rb");
        if (fp)
        {
            fclose(fp);
            std::vector<char> utf82;
            wchar_t wbuff2[MAX_PATH];
            MyTextureAndSRV tas;
            pRenderer.MyLoadTexture(&tas.pTexture, utf8.data(), &tas.w, &tas.h);
            swprintf_s(wbuff2, L"%s", name);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pTexture->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            pRenderer.MyCreateShaderResourceView(&tas.pSRV, tas.pTexture);
            swprintf_s(wbuff2, L"%s_SRV", name);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pSRV->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            loadedTex->insert(std::make_pair(name, tas));
            return &loadedTex->at(name);
        }
        return nullptr;
    }
    return &iter->second;
}

MyTextureAndSRV* GetXXTextureInterArea(const wchar_t* pathFormatStr,
    const wchar_t* name,
    std::unordered_map<std::wstring, MyTextureAndSRV>* loadedTex,
    float scaleW, float scaleH)
{
    auto iter = loadedTex->find(name);
    if (iter == loadedTex->end())
    {
        //单文件名
        std::vector<char> utf8;
        wchar_t wbuff[MAX_PATH];
        swprintf_s(wbuff, pathFormatStr, name);
        UTF16ToUTF8(wbuff, &utf8);
        //确认文件是否存在
        MyTextureAndSRV tas;
        FILE* fp;
        fopen_s(&fp, utf8.data(), "rb");
        if (fp)
        {
            fclose(fp);
            std::vector<char> utf82;
            wchar_t wbuff2[MAX_PATH];
            MyTextureAndSRV tas;
            LinearScarePNG(&tas.pTexture, &pRenderer, utf8.data(), scaleW, scaleH);
            swprintf_s(wbuff2, L"%s", name);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pTexture->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            pRenderer.MyCreateShaderResourceView(&tas.pSRV, tas.pTexture);
            swprintf_s(wbuff2, L"%s_SRV", name);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pSRV->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            loadedTex->insert(std::make_pair(name, tas));
            return &loadedTex->at(name);
        }
        return nullptr;
    }
    return &iter->second;
}

MyTextureAndSRV* GetPxaTexture(const wchar_t* pxaName)
{
    return GetXXTexture(L"./data/pxa/%s.png", pxaName, &loadedPxaTexture);
}

MyTextureAndSRV* GetPxaTextureInterArea(const wchar_t* pxaName, float scaleW, float scaleH)
{
    return GetXXTextureInterArea(L"./data/pxa/%s.png", pxaName, &loadedPxaTexture_InterArea, scaleW, scaleH);
}

MyTextureAndSRV* GetPidTexture(const wchar_t* pidName)
{
    auto iter = loadedPidTexture.find(pidName);
    if (iter == loadedPidTexture.end())
    {
        //单文件名
        std::vector<char> utf8;
        wchar_t wbuff[MAX_PATH];
        bool languageFolder = false;
        if (language_now.length() == 0)
        {
            swprintf_s(wbuff, L"./data/pid/%s.png", pidName);
        }
        else
        {
            swprintf_s(wbuff, L"./data/language/%s/pid/%s.png", language_now.c_str(), pidName);
            languageFolder = true;
        }
        UTF16ToUTF8(wbuff, &utf8);
        //确认文件是否存在
        MyTextureAndSRV tas;
        FILE* fp = nullptr;
        if (languageFolder)
        {
            //确认翻译文件是否存在
            fopen_s(&fp, utf8.data(), "rb");
        }
        if (!fp)
        {
            //翻译文件不存在，使用默认文件
            swprintf_s(wbuff, L"./data/pid/%s.png", pidName);
            UTF16ToUTF8(wbuff, &utf8);
            fopen_s(&fp, utf8.data(), "rb");
        }
        if (fp)
        {
            fclose(fp);
            std::vector<char> utf82;
            wchar_t wbuff2[MAX_PATH];
            MyTextureAndSRV tas;
            pRenderer.MyLoadTexture(&tas.pTexture, utf8.data(), &tas.w, &tas.h);
            swprintf_s(wbuff2, L"%s", pidName);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pTexture->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            pRenderer.MyCreateShaderResourceView(&tas.pSRV, tas.pTexture);
            swprintf_s(wbuff2, L"%s_SRV", pidName);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pSRV->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            loadedPidTexture.insert(std::make_pair(pidName, tas));
            return &loadedPidTexture.at(pidName);
        }
        return nullptr;
    }
    return &iter->second;
}

MyTextureAndSRV* GetPidTextureInterArea(const wchar_t* pidName, float scaleW, float scaleH)
{
    auto iter = loadedPidTexture_InterArea.find(pidName);
    if (iter == loadedPidTexture_InterArea.end())
    {
        //单文件名
        std::vector<char> utf8;
        wchar_t wbuff[MAX_PATH];
        bool languageFolder = false;
        if (language_now.length() == 0)
        {
            swprintf_s(wbuff, L"./data/pid/%s.png", pidName);
        }
        else
        {
            swprintf_s(wbuff, L"./data/language/%s/pid/%s.png", language_now.c_str(), pidName);
            languageFolder = true;
        }
        UTF16ToUTF8(wbuff, &utf8);
        //确认文件是否存在
        MyTextureAndSRV tas;
        FILE* fp = nullptr;
        if (languageFolder)
        {
            //确认翻译文件是否存在
            fopen_s(&fp, utf8.data(), "rb");
        }
        if (!fp)
        {
            //翻译文件不存在，使用默认文件
            swprintf_s(wbuff, L"./data/pid/%s.png", pidName);
            UTF16ToUTF8(wbuff, &utf8);
            fopen_s(&fp, utf8.data(), "rb");
        }
        if (fp)
        {
            fclose(fp);
            std::vector<char> utf82;
            wchar_t wbuff2[MAX_PATH];
            MyTextureAndSRV tas;
            LinearScarePNG(&tas.pTexture, &pRenderer, utf8.data(), scaleW, scaleH);
            swprintf_s(wbuff2, L"%s", pidName);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pTexture->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            pRenderer.MyCreateShaderResourceView(&tas.pSRV, tas.pTexture);
            swprintf_s(wbuff2, L"%s_SRV", pidName);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pSRV->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            loadedPidTexture_InterArea.insert(std::make_pair(pidName, tas));
            return &loadedPidTexture_InterArea.at(pidName);
        }
        return nullptr;
    }
    return &iter->second;
}

MyTextureAndSRV* GetMsgboxTexture(const wchar_t* msgboxName)
{
    auto iter = loadedMsgboxTexture.find(msgboxName);
    if (iter == loadedMsgboxTexture.end())
    {
        std::vector<char> utf8;
        wchar_t wbuff[MAX_PATH];
        bool languageFolder = false;
        if (language_now.length() == 0)
        {
            swprintf_s(wbuff, L"./data/msgbox/%s.png", msgboxName);
        }
        else
        {
            swprintf_s(wbuff, L"./data/language/%s/msgbox/%s.png", language_now.c_str(), msgboxName);
            languageFolder = true;
        }
        UTF16ToUTF8(wbuff, &utf8);
        //确认文件是否存在
        MyTextureAndSRV tas;
        FILE* fp = nullptr;
        if (languageFolder)
        {
            //确认翻译文件是否存在
            fopen_s(&fp, utf8.data(), "rb");
        }
        if (!fp)
        {
            //翻译文件不存在，使用默认文件
            swprintf_s(wbuff, L"./data/msgbox/%s.png", msgboxName);
            UTF16ToUTF8(wbuff, &utf8);
            fopen_s(&fp, utf8.data(), "rb");
        }
        if (fp)
        {
            fclose(fp);
            std::vector<char> utf82;
            wchar_t wbuff2[MAX_PATH];
            MyTextureAndSRV tas;
            pRenderer.MyLoadTexture(&tas.pTexture, utf8.data(), &tas.w, &tas.h);
            swprintf_s(wbuff2, L"%s", msgboxName);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pTexture->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            pRenderer.MyCreateShaderResourceView(&tas.pSRV, tas.pTexture);
            swprintf_s(wbuff2, L"%s_SRV", msgboxName);
            UTF16ToUTF8(wbuff2, &utf82);
            tas.pSRV->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)utf82.size(), utf82.data());
            loadedMsgboxTexture.insert(std::make_pair(msgboxName, tas));
            return &loadedMsgboxTexture.at(msgboxName);
        }
    }
    return &iter->second;
}

MyTextureAndSRV* GetFaceTexture(const wchar_t* faceName)
{
    return GetXXTexture(L"./data/face/%s.png", faceName, &loadedFaceTexture);
}

MyTextureAndSRV* GetFaceTextureInterArea(const wchar_t* faceName, float scaleW, float scaleH)
{
    return GetXXTextureInterArea(L"./data/face/%s.png", faceName, &loadedFaceTexture_InterArea, scaleW, scaleH);
}

MyTextureAndSRV* GetSysTexture(const wchar_t* sysName)
{
    return GetXXTexture(L"./data/sys/%s.png", sysName, &loadedSysTexture);
}

MyTextureAndSRV* GetMcuiTexture(const wchar_t* mcuiName)
{
    return GetXXTexture(L"./data/mcui/%s.png", mcuiName, &loadedMcuiTexture);
}

MyTextureAndSRV* GetResTexture(UINT resID)
{
    auto iter = loadedResTexture.find(resID);
    if (iter == loadedResTexture.end())
    {
        MyTextureAndSRV tas;
        LoadResourceTexture(&pRenderer, resID, &tas.pTexture, &tas.pSRV, &tas.w, &tas.h);
        loadedResTexture.insert(std::make_pair(resID, tas));
        return &loadedResTexture.at(resID);
    }
    return &iter->second;
}

Mix_Chunk* GetSoundByName(const wchar_t* soundName)
{
    auto iter = loadedSound.find(soundName);
    if (iter == loadedSound.end())
    {
        wchar_t wbuff[MAX_PATH];
        swprintf_s(wbuff, L"./data/wav/%s.wav", soundName);
        std::vector<char> utf8;
        UTF16ToUTF8(wbuff, &utf8);
        auto Mix_Chunk = Mix_LoadWAV(utf8.data());
        loadedSound.insert(std::make_pair(soundName, Mix_Chunk));
        return Mix_Chunk;
    }
    return iter->second;
}

void* LoadDrumFromFile(const wchar_t* drumName);
void* GetDrumByIndex(int drumIndex)
{
    auto iter = loadedDrum.find(drumIndex);
    if (iter == loadedDrum.end())
    {
        auto iter2 = drumTable.find(drumIndex);
        if (iter2 != drumTable.end())
        {
            auto drumName = iter2->second.c_str();
            auto pSound = LoadDrumFromFile(drumName);
            loadedDrum.insert(std::make_pair(drumIndex, pSound));
            return pSound;
        }
        else
            return nullptr;
    }
    return iter->second;
}

void InitGameFunc(GameFunc* gameFunc)
{
    //初始化gameFunc
    gameFunc->PlaySound = PlaySound;
    gameFunc->PlayLoopSound = PlayLoopSound;
    gameFunc->StopLoopSound = StopLoopSound;
    gameFunc->GetFullScreen = GetFullScreen;
    gameFunc->SetFullScreen = SetFullScreen;
    gameFunc->GetShowDebug = GetShowDebug;
    gameFunc->SetShowDebug = SetShowDebug;
    gameFunc->EnlargeWindow = EnlargeWindow;
    gameFunc->ReduceWindow = ReduceWindow;
    gameFunc->GetMusicVolume = GetMusicVolume;
    gameFunc->SetMusicVolume = SetMusicVolume;
    gameFunc->GetSoundVolume = GetSoundVolume;
    gameFunc->SetSoundVolume = SetSoundVolume;
    gameFunc->OpenGameController = OpenGameController;
    gameFunc->GetScanCodeName = GetScanCodeName;
    gameFunc->GetGameControllerKeyName = GetGameControllerKeyName;
    gameFunc->GetGameControllerName = GetGameControllerName;
    gameFunc->ResetKeyboard = ResetKeyboard;
    gameFunc->GetAnyKeyPress = GetAnyKeyPress;
    gameFunc->GetAnyGameControllerKeyPress = GetAnyGameControllerKeyPress;
    gameFunc->GetKey = GetKey;
    gameFunc->ResetKeySetting_Player1Keyboard = ResetKeySetting_Player1Keyboard;
    gameFunc->ResetKeySetting_Player1Joystick = ResetKeySetting_Player1Joystick;
    gameFunc->ResetKeySetting_Player2Keyboard = ResetKeySetting_Player2Keyboard;
    gameFunc->ResetKeySetting_Player2Joystick = ResetKeySetting_Player2Joystick;
    gameFunc->GetLoadedEntity = (std::unordered_map<MapLayer*, EntityLayer>*(*)(void*))EntityRes::GetLoadedEntity;
    gameFunc->GetDisplayInfo = (void (*)(void*, float*, float*, float*, float*, float*))EntityRes::GetDisplayInfo;
    gameFunc->GetMousePos = (void (*)(void*, float*, float*))EntityRes::GetMousePos;
    gameFunc->InScreen = (bool (*)(void*, float, float))EntityRes::InScreen;
    gameFunc->GridTouch = (void (*)(void*, std::weak_ptr<Entity>, EntityLayer*))EntityRes::GridTouch;
    gameFunc->GetInputSetting_Player1Keyboard = GetInputSetting_Player1Keyboard;
    gameFunc->GetInputSetting_Player1Joystick = GetInputSetting_Player1Joystick;
    gameFunc->GetInputSetting_Player2Keyboard = GetInputSetting_Player2Keyboard;
    gameFunc->GetInputSetting_Player2Joystick = GetInputSetting_Player2Joystick;
    gameFunc->GetGameControllerFromIndex = GetGameControllerFromIndex;
    gameFunc->GetPlayer1ControllerType = GetPlayer1ControllerType;
    gameFunc->GetPlayer2ControllerType = GetPlayer2ControllerType;
    gameFunc->SetPlayer1ControllerType = SetPlayer1ControllerType;
    gameFunc->SetPlayer2ControllerType = SetPlayer2ControllerType;
    gameFunc->LoadConfig = LoadConfig;
    gameFunc->SaveConfig = SaveConfig;
    gameFunc->GetMyChar_Player1 = (std::weak_ptr<Entity> (*)(void*))GetMyChar_Player1;
    gameFunc->GetMyChar_Player1_menuKeyboard = (std::weak_ptr<Entity>(*)(void*))GetMyChar_Player1_menuKeyboard;
    gameFunc->GetMyChar_Player2 = (std::weak_ptr<Entity>(*)(void*))GetMyChar_Player2;
    gameFunc->GetMyChar_Player2_menuKeyboard = (std::weak_ptr<Entity>(*)(void*))GetMyChar_Player2_menuKeyboard;
    gameFunc->GetMapLayer = (MapLayer * (*)(void*, const wchar_t*))GetMapLayer;
    gameFunc->GetMapSize = (void(*)(void*, int*, int*))GetMapSize;
    gameFunc->SetNewNameOption = SetNewNameOption;
    gameFunc->SetSaveName = SetSaveName;
    gameFunc->DelSave = DelSave;
    gameFunc->SaveNameGetRoomName = SaveNameGetRoomName;
    gameFunc->SaveNameGetGameDifficulty = SaveNameGetGameDifficulty;
    gameFunc->WriteData = WriteData;
    gameFunc->ReadData = ReadData;
    gameFunc->SetPauseMenuBackGame = SetPauseMenuBackGame;
    gameFunc->SetPauseMenuBackTitle = SetPauseMenuBackTitle;
    gameFunc->SetDmgNum = (void (*)(void*, int, float, float, int, std::weak_ptr<Entity>))SetDmgNum;
    gameFunc->GameCreateEntity = GameCreateEntity;
    gameFunc->SetEntity = SetEntity;
    gameFunc->StartEvent = (void (*)(void*, const wchar_t*, const wchar_t*))StartEvent;
    gameFunc->IsLockKeyboard = (bool (*)(void* gameScript))IsLockKeyboard;
    gameFunc->ScriptIsRunning = (bool (*)(void* gameScript))ScriptIsRunning;
    gameFunc->GetPlayerUIData = (PlayerUIData * (*)(void*, int))GetPlayerUIData;
    gameFunc->StopMusic = StopMusic;
    gameFunc->ScreenShot = ScreenShot;
    gameFunc->QuitGame = QuitGame;
    gameFunc->GetLanguage = GetLanguage;
    gameFunc->SetLanguage = SetLanguage;
    gameFunc->GetAllLanguage = GetAllLanguage;
    gameFunc->ReloadLanguage = ReloadLanguage;
    gameFunc->SetLastPlaySlot = SetLastPlaySlot;
    gameFunc->GetLastPlaySlot = GetLastPlaySlot;
    gameFunc->GetPath = EntityRes::GetPath;
    gameFunc->LineTest = EntityRes::LineTest;
    gameFunc->SetMyChar_Player1 = (void (*)(void*, std::weak_ptr<Entity>))SetMyChar_Player1;
    gameFunc->SetMyChar_Player2 = (void (*)(void*, std::weak_ptr<Entity>))SetMyChar_Player2;
    gameFunc->SetGlobalEntity = (void (*)(void*, std::shared_ptr<Entity>, const wchar_t*))SetGlobalEntity;
    gameFunc->GetGlobalEntity = (std::weak_ptr<Entity>(*)(void*, const wchar_t*))GetGlobalEntity;
    gameFunc->GetGlobalEntityTable = (std::unordered_map<std::wstring, std::weak_ptr<Entity>>*(*)(void*))GetGlobalEntityTable;
    gameFunc->GetPlayerIndex = (int(*)(void*, std::weak_ptr<Entity>))GetPlayerIndex;
    gameFunc->GetPlayerDistance = (std::weak_ptr<Entity>(*)(void*, float, float))GetPlayerDistance;
    gameFunc->GetPlayerDistanceLR = (std::weak_ptr<Entity>(*)(void*, float, float, int))GetPlayerDistanceLR;
    gameFunc->GetPlayerDistanceWithLineTest = (std::weak_ptr<Entity>(*)(void*, float, float))GetPlayerDistanceWithLineTest;
    gameFunc->GetPlayerDistanceLRWithLineTest = (std::weak_ptr<Entity>(*)(void*, float, float, int))GetPlayerDistanceLRWithLineTest;
    gameFunc->SetQuake = (void (*)(void*, int, int))SetQuake;
    gameFunc->SetLocalCamera = (void (*)(void*, std::weak_ptr<Entity>, float))SetLocalCamera;
    gameFunc->UnsetLocalCamera = (void (*)(void*, std::weak_ptr<Entity>))UnsetLocalCamera;
    gameFunc->InBossFight = (bool(*)(void*))InBossFight;
    gameFunc->GetCheckEventPlayer = (std::weak_ptr<Entity>(*)(void*))GetCheckEventPlayer;
    gameFunc->PlayerHaveItem = (int (*)(void*, const wchar_t*))PlayerHaveItem;
    gameFunc->PlayerAddItem = (int (*)(void*, const wchar_t*, int))PlayerAddItem;
    gameFunc->PlayerDeleteItem = (int (*)(void*, const wchar_t*, int))PlayerDeleteItem;
    gameFunc->DecodeRecord = DecodeRecord;
    gameFunc->SlotPageEnableDraw = (void (*)(void*, bool))SlotPageEnableDraw;
    gameFunc->SlotPageDrawClear = (void (*)(void*))SlotPageDrawClear;
    gameFunc->SlotPageAddDraw = (void (*)(void*, const wchar_t*, std::weak_ptr<Entity>, std::weak_ptr<Entity>))SlotPageAddDraw;
    gameFunc->SlotPageReset = (void (*)(void*))SlotPageReset;
    gameFunc->SetScreenFlash = (void (*)(void*, int))SetScreenFlash;
}

void SwitchEnglishInput()
{
    auto hImc = ImmGetContext(hwnd);
    DWORD conversion, sentence;
    ImmGetConversionStatus(hImc, &conversion, &sentence);
    if ((conversion != 0 || sentence != 0))
    {
        //强行英文
        ImmSetConversionStatus(hImc, 0, 0);
    }
}

void ClearTexture(std::unordered_map<std::wstring, MyTextureAndSRV>* textureRes)
{
    for (auto& p : *textureRes)
    {
        auto& tas = p.second;
        if (tas.pSRV)
            tas.pSRV->Release();
        if (tas.pTexture)
            tas.pTexture->Release();
    }
    textureRes->clear();
}

//播放声音
void PlaySound(const wchar_t* soundName)
{
    if (bDisableSound)
        return;
    auto iter = soundTable.find(soundName);
    if (iter != soundTable.end())
    {
        //播放wav文件
        auto iter2 = playingSound_cooldown.find(iter->second.c_str());
        if (iter2 == playingSound_cooldown.end())
        {
            playingSound.insert(iter->second.c_str());//为了消除同一时候播放的多个相同音效
            playingSound_cooldown.insert(std::make_pair(iter->second.c_str(), playingSound_cooldown_max));
        }
    }
}

int PlayLoopSound(const wchar_t* soundName)
{
    auto iter = soundTable.find(soundName);
    if (iter != soundTable.end())
    {
        auto soundFileName = iter->second.c_str();
        //播放wav文件
        return Mix_PlayChannel(-1, GetSoundByName(soundFileName), -1);
    }
    return -1;
}

void StopLoopSound(int channelID)
{
    Mix_HaltChannel(channelID);
}

bool GetFullScreen()
{
    return bFullScreen;
}

void SetFullScreen(bool bFullScreen)
{
    if (bFullScreen)
        SDL_SetWindowFullscreen(sdl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
    else
        SDL_SetWindowFullscreen(sdl_window, 0);
    ::bFullScreen = bFullScreen;
    ResetScale();
}

bool GetShowDebug()
{
    return bShowDebug;
}

void SetShowDebug(bool bShowDebug)
{
    ::bShowDebug = bShowDebug;
}

void EnlargeWindow()
{
    int x, y;
    SDL_GetWindowPosition(sdl_window, &x, &y);
    SDL_SetWindowSize(sdl_window, (int)(view.scale + 1) * WINDOW_W, (int)(view.scale + 1) * WINDOW_H);
    SDL_SetWindowPosition(sdl_window, x - WINDOW_W / 2, y - WINDOW_H / 2);
    ResetScale();
}

void ReduceWindow()
{
    if (view.scale > 1)
    {
        int x, y;
        SDL_GetWindowPosition(sdl_window, &x, &y);
        SDL_SetWindowSize(sdl_window, (int)(view.scale - 1) * WINDOW_W, (int)(view.scale - 1) * WINDOW_H);
        SDL_SetWindowPosition(sdl_window, x + WINDOW_W / 2, y + WINDOW_H / 2);
        ResetScale();
    }
}

int GetMusicVolume()
{
    return (int)(pMusic.GetMusicVolume() * 10);
}

void SetMusicVolume(int vol)
{
    orgPlayer.SetMusicVolume((float)vol / 10.0f);
    pMusic.SetMusicVolume((float)vol / 10.0f);
}

int GetSoundVolume()
{
    return (int)(roundf((float)Mix_MasterVolume(-1) / 12.8f));
}

void SetSoundVolume(int vol)
{
    Mix_MasterVolume((int)(vol * 12.8f));
}

void OpenGameController()
{
    pJoystickInput.Reset();
}

void GetScanCodeName(std::vector<wchar_t>* outName, SDL_Scancode key)
{
    auto keyName = SDL_GetScancodeName(key);
    UTF8ToUTF16(keyName, outName);
}

void GetGameControllerKeyName(std::vector<wchar_t>* outName, SDL_GameControllerButtonBind* keyBind)
{
    switch (keyBind->bindType)
    {
    case SDL_CONTROLLER_BINDTYPE_BUTTON://按钮
        UTF8ToUTF16(SDL_GameControllerGetStringForButton((SDL_GameControllerButton)keyBind->value.button), outName);
        return;
    case SDL_CONTROLLER_BINDTYPE_AXIS://摇杆
        UTF8ToUTF16(SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)keyBind->value.axis), outName);
        return;
    }
    UTF8ToUTF16("unknow key", outName);
}

void GetGameControllerName(std::vector<wchar_t>* outName, SDL_GameController* pGameController)
{
    if (pGameController)
        UTF8ToUTF16(SDL_GameControllerName(pGameController), outName);
}

void ResetKeyboard()
{
    SDL_ResetKeyboard();
}

bool GetAnyKeyPress(SDL_Scancode* outKey)
{
    auto keyboard_state = SDL_GetKeyboardState(nullptr);
    for (int i = 0; i < SDL_NUM_SCANCODES; i++)
    {
        if (keyboard_state[i])
        {
            if (outKey)
                *outKey = (SDL_Scancode)i;
            return true;
        }
    }
    return false;
}

bool GetAnyGameControllerKeyPress(SDL_GameController* pGameController, SDL_GameControllerButtonBind* keyBind)
{
    if (!pGameController)
        return false;
    auto iter = pJoystickInput.GetGameControllerList()->find(pGameController);
    if (iter != pJoystickInput.GetGameControllerList()->end())
    {
        auto& data = *iter;
        SDL_GameControllerButtonBind bind;
        //是否按下了按钮
        bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
        for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
        {
            bind.value.button = i;
            if (pJoystickInput.GetState(pGameController, &bind))
            {
                if (keyBind)
                    *keyBind = bind;
                return true;
            }
        }
        //是否按下了摇杆
        bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
        for (int i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++)
        {
            bind.value.axis = i;
            if (pJoystickInput.GetState(pGameController, &bind))
            {
                if (keyBind)
                    *keyBind = bind;
                return true;
            }
        }
    }
    return false;
}

bool GetKey(SDL_Scancode key)
{
    return pInput.GetKeyBoardState(key);
}

void ResetKeySetting_Player1Keyboard()
{
    inputSetting_player1Keyboard = InputSetting_Keyboard();
}

void ResetKeySetting_Player1Joystick()
{
    auto joy = inputSetting_player1Joystick.pGameController;
    inputSetting_player1Joystick = InputSetting_Joystick();
    inputSetting_player1Joystick.pGameController = joy;
}

void ResetKeySetting_Player2Keyboard()
{
    inputSetting_player2Keyboard.op_up = SDL_SCANCODE_KP_8;
    inputSetting_player2Keyboard.op_down = SDL_SCANCODE_KP_5;
    inputSetting_player2Keyboard.op_left = SDL_SCANCODE_KP_4;
    inputSetting_player2Keyboard.op_right = SDL_SCANCODE_KP_6;
    inputSetting_player2Keyboard.op_ok = SDL_SCANCODE_K;
    inputSetting_player2Keyboard.op_cancel = SDL_SCANCODE_L;
    inputSetting_player2Keyboard.op_jump = SDL_SCANCODE_K;
    inputSetting_player2Keyboard.op_atk = SDL_SCANCODE_L;
    inputSetting_player2Keyboard.op_menu = SDL_SCANCODE_ESCAPE;
    inputSetting_player2Keyboard.op_inventory = SDL_SCANCODE_8;
    inputSetting_player2Keyboard.op_map = SDL_SCANCODE_9;
    inputSetting_player2Keyboard.op_pagePrev = SDL_SCANCODE_SEMICOLON;
    inputSetting_player2Keyboard.op_pageNext = SDL_SCANCODE_APOSTROPHE;
    inputSetting_player2Keyboard.op_switchMc = SDL_SCANCODE_J;
    inputSetting_player2Keyboard.op_skill_1 = SDL_SCANCODE_I;
    inputSetting_player2Keyboard.op_skill_2 = SDL_SCANCODE_O;
    inputSetting_player2Keyboard.op_skill_3 = SDL_SCANCODE_P;
    inputSetting_player2Keyboard.op_skill_4 = SDL_SCANCODE_LEFTBRACKET;
    inputSetting_player2Keyboard.op_skill_5 = SDL_SCANCODE_RIGHTBRACKET;
    inputSetting_player2Keyboard.op_skill_6 = SDL_SCANCODE_BACKSLASH;
}

void ResetKeySetting_Player2Joystick()
{
    auto joy = inputSetting_player2Joystick.pGameController;
    inputSetting_player2Joystick = InputSetting_Joystick();
    inputSetting_player2Joystick.pGameController = joy;
}

InputSetting_Keyboard* GetInputSetting_Player1Keyboard()
{
    return &inputSetting_player1Keyboard;
}

InputSetting_Joystick* GetInputSetting_Player1Joystick()
{
    return &inputSetting_player1Joystick;
}

InputSetting_Keyboard* GetInputSetting_Player2Keyboard()
{
    return &inputSetting_player2Keyboard;
}

InputSetting_Joystick* GetInputSetting_Player2Joystick()
{
    return &inputSetting_player2Joystick;
}

SDL_GameController* GetGameControllerFromIndex(int index)
{
    return SDL_GameControllerFromPlayerIndex(index);
}

int GetPlayer1ControllerType()
{
    return player1ControllerType;
}

int GetPlayer2ControllerType()
{
    return player2ControllerType;
}

void SetPlayer1ControllerType(int type)
{
    player1ControllerType = type;
}

void SetPlayer2ControllerType(int type)
{
    player2ControllerType = type;
}

std::filesystem::path GetExePath()
{
    char buffer[1024];
    GetModuleFileNameA(NULL, buffer, 1024);
    return std::filesystem::path(buffer).parent_path();
}

void LoadConfig()
{
    wchar_t szLocaleName[LOCALE_NAME_MAX_LENGTH];
    GetSystemDefaultLocaleName(szLocaleName, LOCALE_NAME_MAX_LENGTH);
    language_now = szLocaleName;
    //检查文件config.xml文件是否存在
    FILE* fp;
    _wfopen_s(&fp, L"config.xml", L"rb");
    //如果文件不存在则使用默认设置
    if (!fp)
    {
        ResetKeySetting_Player1Keyboard();
        ResetKeySetting_Player1Joystick();
        ResetKeySetting_Player2Keyboard();
        ResetKeySetting_Player2Joystick();
        return;
    }
    //如果文件存在
    fseek(fp, 0, SEEK_END);
    size_t fileLen = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    std::vector<BYTE> file_buff;
    file_buff.resize(fileLen);
    fread_s(file_buff.data(), file_buff.size(), fileLen, 1, fp);
    fclose(fp);
    file_buff.push_back(0);
    //读取XML
    tinyxml2::XMLDocument xmlDoc;
    xmlDoc.Parse((const char*)file_buff.data());
    //寻找PlayerX_Keyboard元素
    const char* PlayerX_KeyboardElemArr[2];
    PlayerX_KeyboardElemArr[0] = "Player1_Keyboard";
    PlayerX_KeyboardElemArr[1] = "Player2_Keyboard";
    InputSetting_Keyboard* iskArr[2];
    iskArr[0] = &inputSetting_player1Keyboard;
    iskArr[1] = &inputSetting_player2Keyboard;

    tinyxml2::XMLElement* elem = nullptr;
    for (int i = 0; i < _countof(PlayerX_KeyboardElemArr); i++)
    {
        for (tinyxml2::XMLElement* pElem = xmlDoc.FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
        {
            if (!strcmp(pElem->Name(), PlayerX_KeyboardElemArr[i]))
            {
                elem = pElem;
                break;
            }
        }
        if (elem)
        {
            InputSetting_Keyboard* inputSetting = iskArr[i];
            inputSetting->op_up = (SDL_Scancode)elem->IntAttribute("up", SDL_SCANCODE_UP);
            inputSetting->op_down = (SDL_Scancode)elem->IntAttribute("down", SDL_SCANCODE_DOWN);
            inputSetting->op_left = (SDL_Scancode)elem->IntAttribute("left", SDL_SCANCODE_LEFT);
            inputSetting->op_right = (SDL_Scancode)elem->IntAttribute("right", SDL_SCANCODE_RIGHT);
            inputSetting->op_ok = (SDL_Scancode)elem->IntAttribute("ok", SDL_SCANCODE_Z);
            inputSetting->op_cancel = (SDL_Scancode)elem->IntAttribute("cancel", SDL_SCANCODE_X);
            inputSetting->op_jump = (SDL_Scancode)elem->IntAttribute("jump", SDL_SCANCODE_Z);
            inputSetting->op_atk = (SDL_Scancode)elem->IntAttribute("atk", SDL_SCANCODE_X);
            inputSetting->op_menu = (SDL_Scancode)elem->IntAttribute("menu", SDL_SCANCODE_ESCAPE);
            inputSetting->op_inventory = (SDL_Scancode)elem->IntAttribute("inventory", SDL_SCANCODE_Q);
            inputSetting->op_map = (SDL_Scancode)elem->IntAttribute("map", SDL_SCANCODE_W);
            inputSetting->op_pagePrev = (SDL_Scancode)elem->IntAttribute("pagePrev", SDL_SCANCODE_A);
            inputSetting->op_pageNext = (SDL_Scancode)elem->IntAttribute("pageNext", SDL_SCANCODE_S);
            inputSetting->op_switchMc = (SDL_Scancode)elem->IntAttribute("switchMc", SDL_SCANCODE_LSHIFT);
            inputSetting->op_skill_1 = (SDL_Scancode)elem->IntAttribute("sk1", SDL_SCANCODE_A);
            inputSetting->op_skill_2 = (SDL_Scancode)elem->IntAttribute("sk2", SDL_SCANCODE_S);
            inputSetting->op_skill_3 = (SDL_Scancode)elem->IntAttribute("sk3", SDL_SCANCODE_D);
            inputSetting->op_skill_4 = (SDL_Scancode)elem->IntAttribute("sk4", SDL_SCANCODE_F);
            inputSetting->op_skill_5 = (SDL_Scancode)elem->IntAttribute("sk5", SDL_SCANCODE_G);
            inputSetting->op_skill_6 = (SDL_Scancode)elem->IntAttribute("sk6", SDL_SCANCODE_H);
        }
    }
    //寻找PlayerX_Joystick元素
    const char* PlayerX_JoystickElemArr[2];
    PlayerX_JoystickElemArr[0] = "Player1_Joystick";
    PlayerX_JoystickElemArr[1] = "Player2_Joystick";
    InputSetting_Joystick* isjArr[2];
    isjArr[0] = &inputSetting_player1Joystick;
    isjArr[1] = &inputSetting_player2Joystick;
    const char* keyElemName[] = { "Up","Down","Left","Right","Ok","Cancel","Jump","Atk","Menu","Inventory","Map",
       "PagePrev","PageNext","SwitchMc","Sk1","Sk2","Sk3","Sk4","Sk5","Sk6" };
    elem = nullptr;
    for (int i = 0; i < _countof(PlayerX_JoystickElemArr); i++)
    {
        for (tinyxml2::XMLElement* pElem = xmlDoc.FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
        {
            if (!strcmp(pElem->Name(), PlayerX_JoystickElemArr[i]))
            {
                elem = pElem;
                break;
            }
        }
        if (elem)
        {
            InputSetting_Joystick* js = isjArr[i];
            SDL_GameControllerButtonBind* bind[] = { &js->op_up,&js->op_down, &js->op_left,&js->op_right,&js->op_ok,
            &js->op_cancel,&js->op_jump,&js->op_atk,&js->op_menu,&js->op_inventory,&js->op_map,
            &js->op_pagePrev,&js->op_pageNext,&js->op_switchMc,&js->op_skill_1,&js->op_skill_2,&js->op_skill_3,
            &js->op_skill_4,&js->op_skill_5,&js->op_skill_6 };
            for (int j = 0; j < _countof(keyElemName); j++)
            {
                tinyxml2::XMLElement* keyElem = nullptr;
                for (tinyxml2::XMLElement* pElem = elem->FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
                {
                    if (!strcmp(pElem->Name(), keyElemName[j]))
                    {
                        keyElem = pElem;
                        break;
                    }
                }
                if (keyElem)
                {
                    bind[j]->bindType = (SDL_GameControllerBindType)keyElem->IntAttribute("SDL_GameControllerBindType");
                    bind[j]->value.button = keyElem->IntAttribute("button");
                }
            }
        }
    }

    //寻找Display元素
    elem = nullptr;
    for (tinyxml2::XMLElement* pElem = xmlDoc.FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
    {
        if (!strcmp(pElem->Name(), "Display"))
        {
            elem = pElem;
            break;
        }
    }
    if (elem)
    {
        SetShowDebug(elem->BoolAttribute("showdebug", false));
    }
    //寻找Audio元素
    elem = nullptr;
    for (tinyxml2::XMLElement* pElem = xmlDoc.FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
    {
        if (!strcmp(pElem->Name(), "Audio"))
        {
            elem = pElem;
            break;
        }
    }
    if (elem)
    {
        SetMusicVolume(elem->IntAttribute("musicvolume", 10));
        SetSoundVolume(elem->IntAttribute("soundvolume", 10));
    }
    //寻找Option元素
    elem = nullptr;
    for (tinyxml2::XMLElement* pElem = xmlDoc.FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
    {
        if (!strcmp(pElem->Name(), "Option"))
        {
            elem = pElem;
            break;
        }
    }
    if (elem)
    {
        std::vector<wchar_t> wbuff;
        auto str = elem->Attribute("language");
        if (str)
        {
            UTF8ToUTF16(elem->Attribute("language"), &wbuff);
            language_now = wbuff.data();
        }
        ReloadLanguage();
        lastPlaySlotPage = elem->IntAttribute("lastPlaySlotPage");
        lastPlaySlotIndex = elem->IntAttribute("lastPlaySlotIndex");
    }
    //检查语言文件夹是否存在、如果不存在则切到英文
    std::filesystem::path exeDir = GetExePath();
    std::filesystem::path relativePath = exeDir / L"data" / L"language" / language_now.c_str();
    if (!(std::filesystem::exists(relativePath) && std::filesystem::is_directory(relativePath)))
        language_now = L"en-us";
}

void SaveConfig()
{
    FILE* fp;
    _wfopen_s(&fp, L"config.xml", L"wb+");
    //如果文件不存在则使用默认设置
    if (!fp)
    {
        MessageBoxW(hwnd, L"文件config.xml无法写入", L"按键保存失败", MB_ICONWARNING);
        return;
    }
    std::vector<char> languageNow;
    UTF16ToUTF8(language_now.c_str(), &languageNow);
    InputSetting_Keyboard* inputSetting = GetInputSetting_Player1Keyboard();
    //写入xml文件
    tinyxml2::XMLDocument xmlDoc;
    //写入PlayerX_Keyboard元素
    const char* playerX_KeyboardElemArr[2];
    playerX_KeyboardElemArr[0] = "Player1_Keyboard";
    playerX_KeyboardElemArr[1] = "Player2_Keyboard";
    InputSetting_Keyboard* iskArr[2];
    iskArr[0] = &inputSetting_player1Keyboard;
    iskArr[1] = &inputSetting_player2Keyboard;
    for (int i = 0; i < _countof(playerX_KeyboardElemArr); i++)
    {
        auto elem_keyboard = xmlDoc.NewElement(playerX_KeyboardElemArr[i]);
        InputSetting_Keyboard* kb = iskArr[i];
        elem_keyboard->SetAttribute("up", kb->op_up);
        elem_keyboard->SetAttribute("down", kb->op_down);
        elem_keyboard->SetAttribute("left", kb->op_left);
        elem_keyboard->SetAttribute("right", kb->op_right);
        elem_keyboard->SetAttribute("ok", kb->op_ok);
        elem_keyboard->SetAttribute("cancel", kb->op_cancel);
        elem_keyboard->SetAttribute("jump", kb->op_jump);
        elem_keyboard->SetAttribute("atk", kb->op_atk);
        elem_keyboard->SetAttribute("menu", kb->op_menu);
        elem_keyboard->SetAttribute("inventory", kb->op_inventory);
        elem_keyboard->SetAttribute("map", kb->op_map);
        elem_keyboard->SetAttribute("pagePrev", kb->op_pagePrev);
        elem_keyboard->SetAttribute("pageNext", kb->op_pageNext);
        elem_keyboard->SetAttribute("switchMc", kb->op_switchMc);
        elem_keyboard->SetAttribute("sk1", kb->op_skill_1);
        elem_keyboard->SetAttribute("sk2", kb->op_skill_2);
        elem_keyboard->SetAttribute("sk3", kb->op_skill_3);
        elem_keyboard->SetAttribute("sk4", kb->op_skill_4);
        elem_keyboard->SetAttribute("sk5", kb->op_skill_5);
        elem_keyboard->SetAttribute("sk6", kb->op_skill_6);
        xmlDoc.InsertEndChild(elem_keyboard);
    }
    //写入PlayerX_Joystick元素
    const char* playerX_JoystickElemArr[2];
    playerX_JoystickElemArr[0] = "Player1_Joystick";
    playerX_JoystickElemArr[1] = "Player2_Joystick";
    InputSetting_Joystick* isjArr[2];
    isjArr[0] = &inputSetting_player1Joystick;
    isjArr[1] = &inputSetting_player2Joystick;
    const char* keyElemName[] = { "Up","Down","Left","Right","Ok","Cancel","Jump","Atk","Menu","Inventory","Map",
        "PagePrev","PageNext","SwitchMc","Sk1","Sk2","Sk3","Sk4","Sk5","Sk6" };
    for (int i = 0; i < _countof(playerX_JoystickElemArr); i++)
    {
        auto elem_joystick = xmlDoc.NewElement(playerX_JoystickElemArr[i]);
        InputSetting_Joystick* js = isjArr[i];
        SDL_GameControllerButtonBind* bind[] = { &js->op_up,&js->op_down, &js->op_left,&js->op_right,&js->op_ok,
        &js->op_cancel,&js->op_jump,&js->op_atk,&js->op_menu,&js->op_inventory,&js->op_map,
        &js->op_pagePrev,&js->op_pageNext,&js->op_switchMc,&js->op_skill_1,&js->op_skill_2,&js->op_skill_3,
        &js->op_skill_4,&js->op_skill_5,&js->op_skill_6 };
        for (int j = 0; j < _countof(keyElemName); j++)
        {
            auto elem = xmlDoc.NewElement(keyElemName[j]);
            elem->SetAttribute("SDL_GameControllerBindType", bind[j]->bindType);
            elem->SetAttribute("button", bind[j]->value.button);
            elem_joystick->InsertEndChild(elem);
        }
        xmlDoc.InsertEndChild(elem_joystick);
    }
    //写入Display元素
    auto elem_display = xmlDoc.NewElement("Display");
    elem_display->SetAttribute("fullscreen", bFullScreen);
    elem_display->SetAttribute("scale", view.scale);
    elem_display->SetAttribute("showdebug", bShowDebug);
    xmlDoc.InsertEndChild(elem_display);
    //写入Audio元素
    auto elem_audio = xmlDoc.NewElement("Audio");
    elem_audio->SetAttribute("musicvolume", GetMusicVolume());
    elem_audio->SetAttribute("soundvolume", GetSoundVolume());
    xmlDoc.InsertEndChild(elem_audio);
    //写入Option元素
    auto elem_option = xmlDoc.NewElement("Option");
    if (language_now.length())
    {
        //写入当前语言设置
        elem_option->SetAttribute("language", languageNow.data());
    }
    //写入最后载入的存档位
    elem_option->SetAttribute("lastPlaySlotPage", lastPlaySlotPage);
    elem_option->SetAttribute("lastPlaySlotIndex", lastPlaySlotIndex);
    xmlDoc.InsertEndChild(elem_option);
    //写出到文件
    tinyxml2::XMLPrinter xmlPrinter(fp);
    xmlDoc.Print(&xmlPrinter);
    fclose(fp);
}

std::weak_ptr<Entity> GetMyChar_Player1(EntityRes* entityRes)
{
    return entityRes->GetMyChar_Player1();
}

std::weak_ptr<Entity> GetMyChar_Player1_menuKeyboard(EntityRes* entityRes)
{
    return entityRes->GetMyChar_Player1_menuKeyboard();
}

std::weak_ptr<Entity> GetMyChar_Player2(EntityRes* entityRes)
{
    return entityRes->GetMyChar_Player2();
}

std::weak_ptr<Entity> GetMyChar_Player2_menuKeyboard(EntityRes* entityRes)
{
    return entityRes->GetMyChar_Player2_menuKeyboard();
}

MapLayer* GetMapLayer(GameMap* gameMap, const wchar_t* layerName)
{
    return gameMap->GetMapLayer(layerName);
}

void SetLayerVisible(GameMap* gameMap, const wchar_t* layerName, bool bVisible)
{
    auto& layerList = *gameMap->GetLayerList();
    for (auto& layer : layerList)
    {
        if (layer.layerName == layerName)
        {
            layer.visible = bVisible;
            return;
        }
    }
}

void GetMapSize(GameMap* gameMap, int* outMapW, int* outMapH)
{
    int* pMapW = nullptr;
    int* pMapH = nullptr;
    gameMap->GetMapSize(&pMapW, &pMapH);
    *outMapW = *pMapW;
    *outMapH = *pMapH;
}

void SetNewNameOption(const wchar_t* gameName, int playerNum, int difficulty)
{
    gameOption_gameName = gameName;
    gameOption_playerNum = playerNum;
    gameOption_difficulty = difficulty;
}

void SetSaveName(const wchar_t* saveName)
{
    ::saveName = saveName;
}

void DelSave(const wchar_t* saveName)
{
    //删除游戏存档
    wchar_t wbuff[MAX_PATH];
    swprintf_s(wbuff, L"./data/save/%s.sav", saveName);
    _wremove(wbuff);
}

void SaveNameGetRoomName(std::wstring* outRoomName, const wchar_t* saveName)
{
    //检测存档情况
    //文件./data/save/xxx.sav是否存在
    std::vector<wchar_t> utf16;
    wchar_t wbuff[MAX_PATH];
    swprintf_s(wbuff, L"./data/save/%s.sav", saveName);
    FILE* fp;
    _wfopen_s(&fp, wbuff, L"rb");
    if (!fp)
        return;
    std::vector<BYTE> cbuff;
    fseek(fp, 0, SEEK_END);
    size_t fileLen = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    cbuff.resize(fileLen);
    fread_s(cbuff.data(), fileLen, fileLen, 1, fp);
    fclose(fp);
    std::vector<BYTE> byte_buff;
    size_t len = 0;
    ZLibUncompressData(&byte_buff, &len, cbuff.data(), fileLen);
    tinyxml2::XMLDocument xmlDoc;
    xmlDoc.Parse((const char*)byte_buff.data());
    //寻找Room元素
    tinyxml2::XMLElement* elem = nullptr;
    for (tinyxml2::XMLElement* pElem = xmlDoc.FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
    {
        if (!strcmp(pElem->Name(), "Room"))
        {
            elem = pElem;
            break;
        }
    }
    if (elem)
    {
        //读取Room元素
        UTF8ToUTF16(elem->Attribute("name"), &utf16);
        GameMap tempGameMap;
        MyView tempView;
        GamePxa gamePxa;
        tempGameMap.Init(&tempView, &gamePxa);
        tempGameMap.LoadMap(utf16.data());
        *outRoomName = tempGameMap.GetMapShowName();
    }
}

int SaveNameGetGameDifficulty(const wchar_t* saveName)
{
    //检测存档情况
    //文件./data/save/xxx.sav是否存在
    std::vector<wchar_t> utf16;
    wchar_t wbuff[MAX_PATH];
    swprintf_s(wbuff, L"./data/save/%s.sav", saveName);
    FILE* fp;
    _wfopen_s(&fp, wbuff, L"rb");
    if (!fp)
        return -1;
    std::vector<BYTE> cbuff;
    fseek(fp, 0, SEEK_END);
    size_t fileLen = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    cbuff.resize(fileLen);
    fread_s(cbuff.data(), fileLen, fileLen, 1, fp);
    fclose(fp);
    std::vector<BYTE> byte_buff;
    size_t len = 0;
    ZLibUncompressData(&byte_buff, &len, cbuff.data(), fileLen);
    tinyxml2::XMLDocument xmlDoc;
    xmlDoc.Parse((const char*)byte_buff.data());
    //寻找Option元素
    tinyxml2::XMLElement* elem = nullptr;
    for (tinyxml2::XMLElement* pElem = xmlDoc.FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
    {
        if (!strcmp(pElem->Name(), "Option"))
        {
            elem = pElem;
            break;
        }
    }
    if (elem)
    {
        //读取Option元素
        return elem->IntAttribute("difficulty");
    }
    return -1;
}

void WriteData(const wchar_t* eid, const wchar_t* field, const wchar_t* value)
{
    auto iter = entitySaveVar.find(eid);
    if (iter == entitySaveVar.end())
    {
        entitySaveVar.insert(std::make_pair(eid, std::unordered_map<std::wstring, std::wstring>()));
        iter = entitySaveVar.find(eid);
    }
    auto& entityData = iter->second;
    auto iter_entityData = entityData.find(field);
    if (iter_entityData == entityData.end())
    {
        entityData.insert(std::make_pair(field, value));
        return;
    }
    iter_entityData->second = value;
}

const wchar_t* ReadData(const wchar_t* eid, const wchar_t* field, const wchar_t* defaultVal)
{
    auto iter = entitySaveVar.find(eid);
    if (iter == entitySaveVar.end())
        return defaultVal;
    auto& entityData = iter->second;
    auto iter_entityData = entityData.find(field);
    if (iter_entityData == entityData.end())
        return defaultVal;
    return iter_entityData->second.c_str();
}

void SetPauseMenuBackGame(bool bBack)
{
    pauseMenuBackGame = bBack;
}

void SetPauseMenuBackTitle(bool bBack)
{
    pauseMenuBackTitle = bBack;
}

void SetDmgNum(DmgNum* dmgNum, int val, float x, float y, int style, std::weak_ptr<Entity> e)
{
    dmgNum->SetNum(val, x, y, style, e);
}

void SetEntity(std::shared_ptr<Entity> parent, std::shared_ptr<Entity> newE)
{
    newE->mapLayer = parent->mapLayer;
    newE->entityLayer = parent->entityLayer;
    newE->gameFunc = parent->gameFunc;
    newE->cbRect.roomColor = parent->cbRect.roomColor;
    newE->cbRect.pxmArr = parent->cbRect.pxmArr;
    newE->cbRect.pxaArr = parent->cbRect.pxaArr;
    newE->cbRect.updatedPxmArr = parent->cbRect.updatedPxmArr;
    newE->cbRect.pxmArr_w = parent->cbRect.pxmArr_w;
    newE->cbRect.pxaArr_w = parent->cbRect.pxaArr_w;
    newE->cbRect.Collision = parent->cbRect.Collision;
    newE->Init(newE);
    EntityLayer* entityLayer = (EntityLayer*)parent->entityLayer;
    if (entityLayer)
        entityLayer->addList.push_back(newE);
}

void StartEvent(GameScript* gameScript, const wchar_t* eventName, const wchar_t* triggerType)
{
    gameScript->StartEvent(eventName, triggerType);
}

bool IsLockKeyboard(GameScript* gameScript)
{
    return gameScript->GetKEY();
}

bool ScriptIsRunning(GameScript* gameScript)
{
    return gameScript->IsRunning();
}

PlayerUIData* GetPlayerUIData(GameUI* gameUI, int playerIndex)
{
    return gameUI->GetPlayerUIData(playerIndex);
}

void StopMusic()
{
    pMusic.StopMusic();
}

void ScreenShot()
{
    pRenderer.SetScreenSlot();
    PlaySound(L"YNJSelect");
}

void QuitGame()
{
    bQuitGame = true;
}

const wchar_t* GetLanguage()
{
    return language_now.c_str();
}

void SetLanguage(const wchar_t* lang)
{
    language_now = lang;
    ReloadLanguage();
}

void GetAllLanguage(std::vector<std::wstring>* languageList)
{
    if (!languageList)
        return;
    std::wstring str;
    WIN32_FIND_DATA findData;
    HANDLE hFind = FindFirstFile(L"./data/language/*", &findData);
    if (hFind == INVALID_HANDLE_VALUE)
        return;
    do
    {
        if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            str = findData.cFileName;
            if (str == L"." ||
                str == L"..")
                continue;
            languageList->push_back(str.c_str());
        }
    } while (FindNextFile(hFind, &findData));
    FindClose(hFind);
}

void ReloadLanguage()
{
    ClearTexture(&loadedPidTexture);
    ClearTexture(&loadedMsgboxTexture);
    myGame.ReloadLanguage();
    pausemenuGame.ReloadLanguage();
}

void SetLastPlaySlot(int slotPage, int index)
{
    lastPlaySlotPage = slotPage;
    lastPlaySlotIndex = index;
}

void GetLastPlaySlot(int* outSlotPage, int* outIndex)
{
    if (outSlotPage)
        *outSlotPage = lastPlaySlotPage;
    if (outIndex)
        *outIndex = lastPlaySlotIndex;
}

void SetMyChar_Player1(MyGame* game, std::weak_ptr<Entity> e)
{
    game->SetMyChar_Player1(e);
}

void SetMyChar_Player2(MyGame* game, std::weak_ptr<Entity> e)
{
    game->SetMyChar_Player2(e);
}

void SetGlobalEntity(MyGame* game, std::shared_ptr<Entity> e, const wchar_t* globalEntityID)
{
    game->SetGlobalEntity(e, globalEntityID);
}

std::weak_ptr<Entity> GetGlobalEntity(MyGame* game, const wchar_t* globalEntityID)
{
    return game->GetGlobalEntity(globalEntityID);
}

std::unordered_map<std::wstring, std::weak_ptr<Entity>>* GetGlobalEntityTable(MyGame* game)
{
    return game->GetGlobalEntityTable();
}

int GetPlayerIndex(MyGame* game, std::weak_ptr<Entity> e)
{
    return game->GetPlayerIndex(e);
}

std::weak_ptr<Entity> GetPlayerDistance(MyGame* game, float x, float y)
{
    return game->GetPlayerDistance(x, y);
}

std::weak_ptr<Entity> GetPlayerDistanceLR(MyGame* game, float x, float y, int lr)
{
    return game->GetPlayerDistanceLR(x, y, lr);
}

std::weak_ptr<Entity> GetPlayerDistanceWithLineTest(MyGame* game, float x, float y)
{
    return game->GetPlayerDistanceWithLineTest(x, y);
}

std::weak_ptr<Entity> GetPlayerDistanceLRWithLineTest(MyGame* game, float x, float y, int lr)
{
    return game->GetPlayerDistanceLRWithLineTest(x, y, lr);
}

void SetQuake(MyGame* game, int times, int mode)
{
    game->SetQuake(times, mode);
}

void SetLocalCamera(MyGame* game, std::weak_ptr<Entity> e, float focusTimes)
{
    game->SetLocalCamera(e, focusTimes);
}

void UnsetLocalCamera(MyGame* game, std::weak_ptr<Entity> e)
{
    game->UnsetLocalCamera(e);
}

bool InBossFight(MyGame* game)
{
    return game->InBossFight();
}

std::weak_ptr<Entity> GetCheckEventPlayer(MyGame* game)
{
    return game->GetCheckEventPlayer();
}

int PlayerHaveItem(MyGame* game, const wchar_t* regID)
{
    return game->GetCmd()->ITC(regID);
}

int PlayerAddItem(MyGame* game, const wchar_t* regID, int num)
{
    return game->GetCmd()->ITP(regID, num);
}

int PlayerDeleteItem(MyGame* game, const wchar_t* regID, int num)
{
    return game->GetCmd()->ITS(regID, num);
}

void DecodeRecord(const wchar_t* recordStr, const void** outInputArr, size_t* outDataSize)
{
    static std::vector<BYTE> outData;
    std::vector<char> buff;
    std::vector<BYTE> compressData;
    UTF16ToUTF8(recordStr, &buff);
    Base64Decode(compressData, buff.data());
    ZLibUncompressData(&outData, outDataSize, compressData.data(), compressData.size());
    *outInputArr = outData.data();
}

void SlotPageEnableDraw(SlotDrawer* slotDrawer, bool bEnable)
{
    slotDrawer->EnableDraw(bEnable);
}

void SlotPageDrawClear(SlotDrawer* slotDrawer)
{
    slotDrawer->DrawClear();
}

void SlotPageAddDraw(SlotDrawer* slotDrawer, const wchar_t* saveName, std::weak_ptr<Entity> player1Icon, std::weak_ptr<Entity> player2Icon)
{
    slotDrawer->AddDraw(player1Icon, player2Icon, saveName);
}

void SlotPageReset(SlotDrawer* slotDrawer)
{
    slotDrawer->Reset();
}

void SetScreenFlash(GameFlash* gameFlash, int times)
{
    gameFlash->SetScreenFlash(times);
}

void SysKeyBoard()
{
    //低画质
    if (pInput.GetKeyBoardState(SDL_SCANCODE_F1))
    {
        pInput.SetKeyLock(SDL_SCANCODE_F1);
        bLowQuality = !bLowQuality;
        bLowQuality_lastScale = view.scale;
        ResetScale();
    }
    //截图
    if (pInput.GetKeyBoardState(SDL_SCANCODE_F2))
    {
        pInput.SetKeyLock(SDL_SCANCODE_F2);
        ScreenShot();
    }
    //切换调试显示
    if (pInput.GetKeyBoardState(SDL_SCANCODE_F3))
    {
        pInput.SetKeyLock(SDL_SCANCODE_F3);
        bShowDebug = !bShowDebug;
    }
    if (cheating)
    {
        //加速游戏
        if (pInput.GetKeyBoardState(SDL_SCANCODE_F4))
        {
            //120帧游戏
            gameSpeedX3 = true;
        }
        else
        {
            //变回60帧
            gameSpeedX3 = false;
        }
        //锁住镜头
        if (pInput.GetKeyBoardState(SDL_SCANCODE_F5))
        {
            pInput.SetKeyLock(SDL_SCANCODE_F5);
            bLockCamera = !bLockCamera;
            wchar_t wbuff[MAX_PATH];
            static std::vector<char> utf8;
            static std::vector<wchar_t> wbuff_title;
            if (bLockCamera)
            {
                UTF8ToUTF16((const char*)WINDOW_TITLE, &wbuff_title);
                swprintf_s(wbuff, L"%s [游戏相机已被锁住 按F5解锁]", wbuff_title.data());
                UTF16ToUTF8(wbuff, &utf8);
                SDL_SetWindowTitle(sdl_window, utf8.data());
            }
            else
            {
                SDL_SetWindowTitle(sdl_window, (const char*)WINDOW_TITLE);
            }
        }
        //消灭所有敌人
        if (pInput.GetKeyBoardState(SDL_SCANCODE_F6))
        {
            pInput.SetKeyLock(SDL_SCANCODE_F6);
            myGame.KillAllEnemy();
        }
        //玩家移动到鼠标位置
        if (pInput.GetKeyBoardState(SDL_SCANCODE_F7))
        {
            myGame.MoveAllPlayerToMousePos();
        }
        //启用或关闭垂直同步
        if (pInput.GetKeyBoardState(SDL_SCANCODE_F8))
        {
            pInput.SetKeyLock(SDL_SCANCODE_F8);
            pRenderer.SetVSync(!pRenderer.GetVSync());
        }
        //尝试控制目标（如果是可操控的玩家角色）
        if (pInput.GetKeyBoardState(SDL_SCANCODE_F9))
        {
            pInput.SetKeyLock(SDL_SCANCODE_F9);
            myGame.TryControlMousePosEntity();
        }
    }
    //缩小窗口
    if (pInput.GetKeyBoardState(SDL_SCANCODE_MINUS))
    {
        pInput.SetKeyLock(SDL_SCANCODE_MINUS);
        float scale = bLowQuality ? bLowQuality_lastScale : view.scale;
        if (scale > 1)
        {
            int x, y;
            SDL_GetWindowPosition(sdl_window, &x, &y);
            SDL_SetWindowSize(sdl_window, (int)(scale - 1) * WINDOW_W, (int)(scale - 1) * WINDOW_H);
            SDL_SetWindowPosition(sdl_window, x + WINDOW_W / 2, y + WINDOW_H / 2);
            ResetScale();
            SaveConfig();
            if (bLowQuality)
                bLowQuality_lastScale--;
        }
    }
    //放大窗口
    else if (pInput.GetKeyBoardState(SDL_SCANCODE_EQUALS))
    {
        pInput.SetKeyLock(SDL_SCANCODE_EQUALS);
        float scale = bLowQuality ? bLowQuality_lastScale : view.scale;
        int x, y;
        SDL_GetWindowPosition(sdl_window, &x, &y);
        SDL_SetWindowSize(sdl_window, (int)(scale + 1) * WINDOW_W, (int)(scale + 1) * WINDOW_H);
        SDL_SetWindowPosition(sdl_window, x - WINDOW_W / 2, y - WINDOW_H / 2);
        ResetScale();
        SaveConfig();
        if (bLowQuality)
            bLowQuality_lastScale++;
    }
}

void ResetScale()
{
    int w, h;
    SDL_GetWindowSize(sdl_window, &w, &h);
    if (bLowQuality)
    {
        w = 640;
        h = 360;
    }
    view.window_w = (float)w;
    view.window_h = (float)h;
    view.scale = min(view.window_w / WINDOW_W, view.window_h / WINDOW_H);
    pRenderer.OnSize(w, h, false);
    ClearDbgFont();
    dbgFont = pRenderer.MyLoadFont(highQualityFont.c_str(), (int)(8 * view.scale));
    myGame.OnResetScale(w, h);
    pausemenuGame.OnResetScale(w, h);
    ClearTexture(&loadedPidTexture_InterArea);
    ClearTexture(&loadedPxaTexture_InterArea);
    ClearTexture(&loadedFaceTexture_InterArea);
}

void ClearDbgFont()
{
    //重置调试系统字体
    if (dbgFont)
        TTF_CloseFont(dbgFont);
    dbgFont = nullptr;
    for (auto& p : dbgFontMap)
    {
        auto& tas = p.second;
        if (tas.pSRV)
            tas.pSRV->Release();
        if (tas.pTexture)
            tas.pTexture->Release();
    }
    dbgFontMap.clear();
}

void ClearLoadedTexture()
{
    for (auto& p : loadedResTexture)
    {
        auto& tas = p.second;
        if (tas.pSRV)
            tas.pSRV->Release();
        if (tas.pTexture)
            tas.pTexture->Release();
    }
    loadedResTexture.clear();
    ClearTexture(&loadedPxaTexture);
    ClearTexture(&loadedPxaTexture_InterArea);
    ClearTexture(&loadedPidTexture);
    ClearTexture(&loadedPidTexture_InterArea);
    ClearTexture(&loadedMsgboxTexture);
    ClearTexture(&loadedFaceTexture);
    ClearTexture(&loadedFaceTexture_InterArea);
    ClearTexture(&loadedSysTexture);
    ClearTexture(&loadedMcuiTexture);
}

void AudioBackend_DestroySound(void* sound);//Sound.cpp
void ClearLoadedSound()
{
    for (auto& p : loadedSound)
    {
        if (p.second)
            Mix_FreeChunk(p.second);
    }
    loadedSound.clear();
    for (auto& p : loadedDrum)
    {
        if (p.second)
            AudioBackend_DestroySound(p.second);
    }
    loadedDrum.clear();
    //Organya退出
    orgPlayer.Quit();
}

void DrawDebug(MyGame* pGame)
{
    //绘制调试信息
    if (bShowDebug)
    {
        MyRect rc;
        wchar_t wbuff[MAX_PATH];
        float nextX = 0, nextY = 0;
        auto wmychar = pGame->GetMyChar_Player1().lock();
        int LsNum, McNum, AmNum, PeNum;
        pGame->GetDebugEntityNum(&LsNum, &McNum, &AmNum, &PeNum);
        //左侧
        swprintf_s(wbuff, L"FPS:%d\n", (int)myFPS.fps_current);
        pRenderer.DrawText_Unicode(&dbgFontMap, dbgFont, wbuff, nextX, &nextX, &nextY);
        if (wmychar)
        {
            swprintf_s(wbuff, L"Pos:%d %d\n", (int)wmychar->x, (int)wmychar->y);
            pRenderer.DrawText_Unicode(&dbgFontMap, dbgFont, wbuff, nextX, &nextX, &nextY);
        }
        swprintf_s(wbuff, L"Entity:%d Player:%d Projectile:%d Particle:%d All:%d\n", LsNum, McNum, AmNum, PeNum, LsNum + McNum + AmNum + PeNum);
        pRenderer.DrawText_Unicode(&dbgFontMap, dbgFont, wbuff, nextX, &nextX, &nextY);
        //右侧
        swprintf_s(wbuff, L"Renderer:Direct3d11\n");
        pRenderer.DrawText_UnicodeLen(&rc, &dbgFontMap, dbgFont, wbuff);
        nextX = view.window_w - rc.GetWidth(), nextY = 0;
        pRenderer.DrawText_Unicode(&dbgFontMap, dbgFont, wbuff, nextX, &nextX, &nextY);
        swprintf_s(wbuff, L"Scale:%d\n", (int)view.scale);
        pRenderer.DrawText_UnicodeLen(&rc, &dbgFontMap, dbgFont, wbuff);
        nextX = view.window_w - rc.GetWidth();
        pRenderer.DrawText_Unicode(&dbgFontMap, dbgFont, wbuff, nextX, &nextX, &nextY);
        swprintf_s(wbuff, L"Display:%dx%d\n", (int)view.window_w, (int)view.window_h);
        pRenderer.DrawText_UnicodeLen(&rc, &dbgFontMap, dbgFont, wbuff);
        nextX = view.window_w - rc.GetWidth();
        pRenderer.DrawText_Unicode(&dbgFontMap, dbgFont, wbuff, nextX, &nextX, &nextY);
    }
}

void MapTest(const wchar_t* mapName, const wchar_t* mycharName, int xPos, int yPos)
{
    myGame.LoadSetting(mapName, mycharName, xPos, yPos);
    myGame.SetUIVisiable(true);//启用血条显示
    float gameRunningTime = FIXED_DELTA_TIME;
    while (true)
    {
        if (isFreezed)
        {
            Sleep(1);
        }
        else
        {
            float previousTime = (float)SDL_GetTicks();
            while (gameRunningTime >= FIXED_DELTA_TIME)
            {
                if (gameSpeedX3)
                {
                    for (int i = 0; i < 3; i++)
                        myGame.Step();
                }
                else
                {
                    myGame.Step();
                }
                gameRunningTime -= FIXED_DELTA_TIME;
            }
            pRenderer.RenderClear();
            myGame.Draw(std::clamp(gameRunningTime / FIXED_DELTA_TIME, 0.0f, 1.0f));
            DrawDebug(&myGame);
            pRenderer.RenderPresent();
            SysKeyBoard();
            myGame.Controll();
            myFPS.CalcFps();
            gameRunningTime += ((float)SDL_GetTicks() - previousTime) / 1000.0f; // 转换为秒
        }
        SystemTask();
    }
    myGame.SetUIVisiable(false);//启用血条显示
}
void DrawRecordGoal(int endPosX, int endPosY)
{
    DWORD c = RGBA(0xff, 0xff, 0, 0xff);
    pRenderer.SetPenDraw();
    float arrow_x = (endPosX * view.grid_w) * view.scale - floorf(view.offX * view.scale);
    float arrow_y = (endPosY * view.grid_h) * view.scale - floorf(view.offY * view.scale);

    MyDX11PenVertex v0, v1;
    v0.Set(arrow_x + (view.grid_w / 2 - 1.5f) * view.scale,
        arrow_y + 3 * view.scale,
        0, c);
    v1.Set(arrow_x + (view.grid_w / 2 + 1.5f) * view.scale,
        arrow_y + 3 * view.scale,
        0, c);
    pRenderer.AddLine(v0, v1);
    MyDX11PenVertex v2;
    v2.Set(arrow_x + (view.grid_w / 2 + 1.5f) * view.scale,
        arrow_y + 9 * view.scale,
        0, c);
    pRenderer.AddLine(v1, v2);
    MyDX11PenVertex v3;
    v3.Set(arrow_x + (view.grid_w / 2 + 4) * view.scale,
        arrow_y + 9 * view.scale,
        0, c);
    pRenderer.AddLine(v2, v3);
    MyDX11PenVertex v4;
    v4.Set(arrow_x + (view.grid_w / 2) * view.scale,
        arrow_y + 14 * view.scale,
        0, c);
    pRenderer.AddLine(v3, v4);
    MyDX11PenVertex v5;
    v5.Set(arrow_x + (view.grid_w / 2 - 4) * view.scale,
        arrow_y + 9 * view.scale,
        0, c);
    pRenderer.AddLine(v4, v5);
    MyDX11PenVertex v6;
    v6.Set(arrow_x + (view.grid_w / 2 - 1.5f) * view.scale,
        arrow_y + 9 * view.scale,
        0, c);
    pRenderer.AddLine(v5, v6);
    MyDX11PenVertex v7;
    v7.Set(arrow_x + (view.grid_w / 2 - 1.5f) * view.scale,
        arrow_y + 3 * view.scale,
        0, c);
    pRenderer.AddLine(v6, v7);
    pRenderer.DrawLine();
}
void RecordStep(int endPosX, int endPosY)
{
    auto wmc_now = myGame.GetMyChar_Player1().lock();
    if (!wmc_now)
        return;
    if (!record_done)
    {
        if (!recording)//如果未开始录制
        {
            //如果检测到玩家开始操作则录制开始
            if (wmc_now->input.key != 0)
            {
                recording = true;
                PlaySound(L"YNJSelect");
            }
        }
        if (recording)//开始录制
        {
            record.push_back(wmc_now->input);
            //如果玩家抵达到了终点或者按下了录制终止按钮Esc
            bool bEnd = false;
            if (pInput.GetKeyBoardState(SDL_SCANCODE_ESCAPE))
                bEnd = true;
            if (bEnd || (abs(endPosX * view.grid_w + 8 - wmc_now->x) < 2 && abs(endPosY * view.grid_h + 8 - wmc_now->y) < 2))
            {
                //录制结束
                recording = false;
                record_done = true;
                PlaySound(L"MenuCancel");
                //写入共享内存
                size_t dataSize = record.size() * sizeof(GameInput);
                size_t buffSize = dataSize + sizeof(int) * 2;
                hMapFile = CreateFileMapping(
                    INVALID_HANDLE_VALUE,
                    NULL,
                    PAGE_READWRITE,
                    0,
                    (DWORD)buffSize,
                    L"OcStore_Record");
                if (!hMapFile)
                {
                    MessageBox(hwnd, L"创建文件映射失败", L"无法将数据送回编辑器", MB_ICONWARNING);
                    exit(0);
                }
                hMapFile_data = MapViewOfFile(
                    hMapFile,
                    FILE_MAP_ALL_ACCESS,
                    0,
                    0,
                    BUFSIZ);
                memcpy_s(hMapFile_data, buffSize, &dataSize, sizeof(dataSize));
                GameInput* inputData = (GameInput*)((char*)hMapFile_data + 4);
                int time = 0;
                for (auto& i : record)
                {
                    inputData[time] = i;
                    time++;
                }
            }
        }
    }
    if (!recording && record_done)
    {
        //等待编辑器接受数据后自动关闭
        if (hMapFile_data)
        {
            auto pWait = (int*)hMapFile_data;
            if (pWait[0] == 0)
            {
                //编辑器已完成读取
                //关闭程序
                UnmapViewOfFile(hMapFile_data);
                CloseHandle(hMapFile);
                exit(0);
            }
        }
    }
}

void Record(const wchar_t* mapName, const wchar_t* mycharName, int xPos, int yPos, int endPosX, int endPosY)
{
    //启用无敌人模式
    myGame.NoEnemyMode();
    myGame.LoadSetting(mapName, mycharName, xPos, yPos);
    myGame.FullLight();
    while (true)
    {
        if (isFreezed)
        {
            Sleep(1);
        }
        else
        {
            myGame.Step();
            RecordStep(endPosX, endPosY);
            pRenderer.RenderClear();
            myGame.Draw(1);
            DrawRecordGoal(endPosX, endPosY);
            DrawDebug(&myGame);
            pRenderer.RenderPresent();
            SysKeyBoard();
            myGame.Controll();
            myFPS.CalcFps();
        }
        SystemTaskRecord();
    }
}

void IntroStage()
{
    myGame.LoadSetting(L"IntroStage");
    float gameRunningTime = FIXED_DELTA_TIME;
    while (!myGame.TitleStageOk())
    {
        if (isFreezed)
        {
            Sleep(1);
        }
        else
        {
            float previousTime = (float)SDL_GetTicks();
            while (gameRunningTime >= FIXED_DELTA_TIME)
            {
                if (gameSpeedX3)
                {
                    for (int i = 0; i < 3; i++)
                        myGame.Step();
                }
                else
                {
                    myGame.Step();
                }
                gameRunningTime -= FIXED_DELTA_TIME;
            }
            pRenderer.RenderClear();
            myGame.Draw(std::clamp(gameRunningTime / FIXED_DELTA_TIME, 0.0f, 1.0f));
            DrawDebug(&myGame);
            pRenderer.RenderPresent();
            SysKeyBoard();
            myGame.Controll();
            myFPS.CalcFps();
            gameRunningTime += ((float)SDL_GetTicks() - previousTime) / 1000.0f; // 转换为秒
        }
        SystemTask();
    }
    //锁住ok键
    std::weak_ptr<Entity> players[4];
    players[0] = myGame.GetMyChar_Player1();
    players[1] = myGame.GetMyChar_Player1_menuKeyboard();
    players[2] = myGame.GetMyChar_Player2();
    players[3] = myGame.GetMyChar_Player2_menuKeyboard();
    bool playOkSound = true;
    for (auto i = 0; i < _countof(players); i++)
    {
        auto wplayer = players[i].lock();
        if (wplayer)
        {
            if (playOkSound)
            {
                playOkSound = false;
                if (wplayer->input.GetOk())
                {
                    PlaySound(L"MenuOk");
                }
            }
            wplayer->input.SetOk(false);
            wplayer->inputLock.SetOk(true);
        }
    }
    //停止音乐
    pMusic.StopMusic();
    orgPlayer.StopMusic();
    //延迟30帧
   /* int counter = 30;
    pRenderer.RenderClear();
    pRenderer.RenderPresent();
    while (counter > 0)
    {
        SystemTask();
        counter--;
    }*/
}

bool TitleStage()
{
    auto player1 = myGame.GetMyChar_Player1();
    myGame.LoadSetting(L"TitleStage");
    saveName = L"";
    float gameRunningTime = FIXED_DELTA_TIME;
    while (!saveName.length())
    {
        if (isFreezed)
        {
            Sleep(1);
        }
        else
        {
            float previousTime = (float)SDL_GetTicks();
            while (gameRunningTime >= FIXED_DELTA_TIME)
            {
                if (gameSpeedX3)
                {
                    for (int i = 0; i < 3; i++)
                        myGame.Step();
                }
                else
                {
                    myGame.Step();
                }
                gameRunningTime -= FIXED_DELTA_TIME;
            }
            pRenderer.RenderClear();
            myGame.Draw(std::clamp(std::clamp(gameRunningTime / FIXED_DELTA_TIME, 0.0f, 1.0f), 0.0f, 1.0f));
            DrawDebug(&myGame);
            pRenderer.RenderPresent();
            SysKeyBoard();
            myGame.Controll();
            myFPS.CalcFps();
            gameRunningTime += ((float)SDL_GetTicks() - previousTime) / 1000.0f; // 转换为秒
        }
        SystemTask();
        if (bQuitGame)
            return true;
    }
    //停止音乐
    pMusic.StopMusic();
    orgPlayer.StopMusic();
    //锁住ok键
    std::weak_ptr<Entity> players[4];
    players[0] = myGame.GetMyChar_Player1();
    players[1] = myGame.GetMyChar_Player1_menuKeyboard();
    players[2] = myGame.GetMyChar_Player2();
    players[3] = myGame.GetMyChar_Player2_menuKeyboard();
    for (auto i = 0; i < _countof(players); i++)
    {
        auto wplayer = players[i].lock();
        if (wplayer)
        {
            wplayer->input.SetOk(false);
            wplayer->inputLock.SetOk(true);
        }
    }
    //延迟30帧
    int counter = 30;
    pRenderer.RenderClear();
    pRenderer.RenderPresent();
    while (counter > 0)
    {
        SDL_Delay((Uint32)(FIXED_DELTA_TIME * 1000.0f));
        SystemTask();
        counter--;
    }
    return false;
}

bool GameStage()
{
    do
    {
        bESC = bINI = false;
        //载入存档或者新游戏
        if (!LoadGameFromFile())
            myGame.LoadSetting(gameOption_gameName.c_str());//载入新游戏
        myGame.SetUIVisiable(true);//启用血条显示
        myGame.EnableItemSystem(true);//启用物品栏
        float gameRunningTime = FIXED_DELTA_TIME;
        while (true)
        {
            if (isFreezed)
            {
                Sleep(1);
            }
            else
            {
                float previousTime = (float)SDL_GetTicks();
                while (gameRunningTime >= FIXED_DELTA_TIME)
                {
                    if (gameSpeedX3)
                    {
                        for (int i = 0; i < 3; i++)
                            myGame.Step();
                    }
                    else
                    {
                        myGame.Step();
                    }
                    gameRunningTime -= FIXED_DELTA_TIME;
#ifdef _DEBUG
                    break;
#endif // _DEBUG
                }
                pRenderer.RenderClear();
                myGame.Draw(std::clamp(gameRunningTime / FIXED_DELTA_TIME, 0.0f, 1.0f));
                DrawDebug(&myGame);
                pRenderer.RenderPresent();
                SysKeyBoard();
                myGame.Controll();
                myFPS.CalcFps();
                gameRunningTime += ((float)SDL_GetTicks() - previousTime) / 1000.0f; // 转换为秒
            }
            SystemTask();
            if (PauseMenu(std::clamp(gameRunningTime / FIXED_DELTA_TIME, 0.0f, 1.0f)) || bINI || bESC)//暂停菜单
                break;
            if (bQuitGame)
                return true;
        }
        myGame.SetUIVisiable(false);//禁用血条显示
        myGame.EnableItemSystem(false);//禁用物品栏
        //停止音乐
        if (pMusic.IsPlaying())
            pMusic.StopMusic();
        //延迟30帧
        int counter = 30;
        pRenderer.RenderClear();
        pRenderer.RenderPresent();
        while (counter > 0)
        {
            SDL_Delay((Uint32)(FIXED_DELTA_TIME * 1000.0f));
            SystemTask();
            counter--;
        }
    } while (bINI);
    return false;
}

bool PauseMenu(float delteTime_MainGame)
{
    bool openPauseMenu = false;
    std::weak_ptr<Entity> players[4];
    players[0] = myGame.GetMyChar_Player1();
    players[1] = myGame.GetMyChar_Player1_menuKeyboard();
    players[2] = myGame.GetMyChar_Player2();
    players[3] = myGame.GetMyChar_Player2_menuKeyboard();
    for (auto i = 0; i < _countof(players); i++)
    {
        auto wplayer = players[i].lock();
        if (wplayer)
        {
            if (wplayer->input.GetMenu())
                openPauseMenu = true;
        }
    }
    if (!openPauseMenu)
        return false;
    orgPlayer.PauseMenuFreezeMusic();
    myGame.EnableDrawDebug(false && cheating);
    bool showDebug = bShowDebug;
    pausemenuGame.LoadSetting(L"PauseMenu");
    bShowDebug = showDebug;
    pausemenuGame.EnableDrawDebug(true && cheating);
    pauseMenuBackGame = false;
    pauseMenuBackTitle = false;
    pausemenuView.window_w = view.window_w;
    pausemenuView.window_h = view.window_h;
    pausemenuView.scale = view.scale;
    pausemenuGame.Step();//解决一些加载显示问题
    float gameRunningTime = FIXED_DELTA_TIME;
    while (true)
    {
        if (isFreezed)
        {
            Sleep(1);
        }
        else
        {
            float previousTime = (float)SDL_GetTicks();
            while (gameRunningTime >= FIXED_DELTA_TIME)
            {
                if (gameSpeedX3)
                {
                    for (int i = 0; i < 3; i++)
                        pausemenuGame.Step();
                }
                else
                {
                    pausemenuGame.Step();
                }
                gameRunningTime -= FIXED_DELTA_TIME;
            }
            if (pauseMenuBackGame || pauseMenuBackTitle)
                break;
            pausemenuView.window_w = view.window_w;
            pausemenuView.window_h = view.window_h;
            pausemenuView.scale = view.scale;
            pRenderer.RenderClear();
            myGame.Draw(delteTime_MainGame);
            pausemenuGame.Draw(std::clamp(gameRunningTime / FIXED_DELTA_TIME, 0.0f, 1.0f));
            DrawDebug(&pausemenuGame);
            pRenderer.RenderPresent();
            SysKeyBoard();
            pausemenuGame.Controll();
            myFPS.CalcFps();
            gameRunningTime += ((float)SDL_GetTicks() - previousTime) / 1000.0f; // 转换为秒
        }
        SystemTask();
    }
    myGame.EnableDrawDebug(true && cheating);
    //防止误跳跃
    myGame.LockAllPlayerJump();
    orgPlayer.PauseMenuActivateMusic();
    return pauseMenuBackTitle;
}

void Quit()
{
    myGame.Reset();
    ClearDbgFont();
    ClearLoadedTexture();
    ClearLoadedSound();
}

void LoadEntityDll()
{
    WIN32_FIND_DATAW findData;
    HANDLE hFind = INVALID_HANDLE_VALUE;

    hFind = FindFirstFileW(L".\\data\\entity\\*.dll", &findData);
    if (hFind == INVALID_HANDLE_VALUE) 
    {
        MessageBoxW(hwnd, L"加载实体Dll失败", L"", MB_ICONERROR);
        exit(0);
    }
    do 
    {
        if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 
        {
            loadedEntityDll.insert(std::make_pair(findData.cFileName, EntityDll()));
            auto& entityDll = loadedEntityDll.at(findData.cFileName);
            for (int i = (int)wcslen(findData.cFileName); i > 0; i--)
            {
                if (findData.cFileName[i] == L'.')
                {
                    findData.cFileName[i] = 0;
                    break;
                }
            }
            entityDll.Load(findData.cFileName);
        }
    } while (FindNextFileW(hFind, &findData));
    FindClose(hFind);
}

std::shared_ptr<Entity> GameCreateEntity(const wchar_t* name)
{
    for (auto& p : loadedEntityDll)
    {
        std::shared_ptr<Entity> we;
        p.second._GameCreateEntity(name, &we);
        if (!we)
            continue;
        return we;
    }
    //错误找不到实体
    wchar_t err[MAX_PATH];
    swprintf_s(err, L"找不到实体%s", name);
    MessageBoxW(hwnd, err, L"创建实体失败", MB_ICONWARNING);
    //尝试创建null实体
    name = L"null";
    for (auto& p : loadedEntityDll)
    {
        std::shared_ptr<Entity> we;
        p.second._GameCreateEntity(name, &we);
        if (!we)
            continue;
        return we;
    }
    //错误找不到实体
    swprintf_s(err, L"找不到实体%s", name);
    MessageBoxW(hwnd, err, L"创建实体失败", MB_ICONWARNING);
    exit(0);
}

//拆分字符串
void SplitString(std::vector<std::wstring>* outList, const wchar_t* splitStr, wchar_t delimiter)
{
    std::wstring str = splitStr;
    outList->clear();
    size_t start = 0, end = 0;
    while ((end = str.find(delimiter, start)) != std::wstring::npos)
    {
        outList->push_back(str.substr(start, end - start));
        start = end + 1;
    }
    outList->push_back(str.substr(start));
}

void SaveGameToFile()
{
    //保存游戏存档
    std::vector<char> utf8;
    myGame.SaveGame(&utf8);
    //压缩数据
    std::vector<BYTE> compressData;
    size_t compressData_len;
    ZLibCompressData(&compressData, &compressData_len, utf8.data(), strlen(utf8.data()) + 1);
    //写入本地文件
    wchar_t filename[MAX_PATH];
    swprintf_s(filename, L"./data/save/%s.sav", saveName.c_str());
    FILE* fp;
    _wfopen_s(&fp, filename, L"wb+");
    if (!fp)
    {
        wchar_t err[MAX_PATH];
        swprintf_s(err, L"保存游戏到本地目录%s失败", filename);
        MessageBoxW(hwnd, err, L"文件写入失败", MB_ICONWARNING);
        return;
    }
    fwrite(compressData.data(), compressData_len, 1, fp);
    fclose(fp);
}

bool LoadGameFromFile()
{
    //载入游戏存档
    wchar_t filename[MAX_PATH];
    swprintf_s(filename, L"./data/save/%s.sav", saveName.c_str());
    FILE* fp;
    _wfopen_s(&fp, filename, L"rb");
    if (!fp)
        return false;
    std::vector<BYTE> cbuff;
    fseek(fp, 0, SEEK_END);
    size_t fileLen = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    cbuff.resize(fileLen);
    fread_s(cbuff.data(), fileLen, fileLen, 1, fp);
    fclose(fp);
    std::vector<BYTE> byte_buff;
    size_t len = 0;
    ZLibUncompressData(&byte_buff, &len, cbuff.data(), fileLen);
    myGame.LoadGame((const char*)byte_buff.data());
    return true;
}

void GameRestart()
{
    //载入存档或者新游戏
    bINI = true;
}

void GameBackTitle()
{
    //返回标题画面
    bESC = true;
}

void LoadSoundTable()
{
    auto L = NewTranslateLua();
    std::vector<char> utf8;
    std::vector<wchar_t> wbuff;
    UTF16ToUTF8(L"./data/soundTable.lua", &utf8);
    if (luaL_dofile(L, utf8.data()) != LUA_OK)
    {
        auto err = lua_tostring(L, -1);
        UTF8ToUTF16(err, &wbuff);
        wchar_t title[MAX_PATH];
        swprintf_s(title, L"在%s脚本发生错误", wbuff.data());
        MessageBox(hwnd, wbuff.data(), title, MB_ICONWARNING);
        lua_pop(L, 1);
    }
    lua_close(L);
}

void LoadFontTable()
{
    auto L = NewTranslateLua();
    std::vector<char> utf8;
    std::vector<wchar_t> wbuff;
    UTF16ToUTF8(L"./data/fontTable.lua", &utf8);
    if (luaL_dofile(L, utf8.data()) != LUA_OK)
    {
        auto err = lua_tostring(L, -1);
        UTF8ToUTF16(err, &wbuff);
        wchar_t title[MAX_PATH];
        swprintf_s(title, L"在%s脚本发生错误", wbuff.data());
        MessageBox(hwnd, wbuff.data(), title, MB_ICONWARNING);
        lua_pop(L, 1);
    }
    lua_close(L);
}

void LoadFaceTable()
{
    auto L = NewTranslateLua();
    std::vector<char> utf8;
    std::vector<wchar_t> wbuff;
    UTF16ToUTF8(L"./data/faceTable.lua", &utf8);
    if (luaL_dofile(L, utf8.data()) != LUA_OK)
    {
        auto err = lua_tostring(L, -1);
        UTF8ToUTF16(err, &wbuff);
        wchar_t title[MAX_PATH];
        swprintf_s(title, L"在%s脚本发生错误", wbuff.data());
        MessageBox(hwnd, wbuff.data(), title, MB_ICONWARNING);
        lua_pop(L, 1);
    }
    lua_close(L);
}

void PlaySoundProc()
{
    for (auto& p : playingSound)
        Mix_PlayChannel(-1, GetSoundByName(p.c_str()), 0);
    playingSound.clear();
    for (auto& p : playingSound_cooldown)
        if (p.second > 0)
            p.second--;
    std::erase_if(playingSound_cooldown, [](const auto& p)
    {
        return p.second <= 0;
    });
}

void EntityInitCheck(std::shared_ptr<Entity>& we)
{
    if (!we->initCheck)
    {
        wchar_t err[MAX_PATH];
        swprintf_s(err, L"实体%s[%s][%s]未初始化，\n是否手动初始化？", we->el.name.c_str(), we->ani.png.c_str(), we->ani.sprite_index.c_str());
        if (MessageBox(hwnd, err, L"实体未通过初始化检查", MB_ICONWARNING | MB_YESNO) == IDYES)
            we->Init(we);
    }
}

void EntitySharedPtrCheck(std::shared_ptr<Entity>& we)
{
    if (we.use_count() > 1)
    {
        wchar_t err[MAX_PATH];
        swprintf_s(err, L"实体%s[%s][%s]在其他地方保存了强引用，数量总共为%d个。\n请修改程序！", we->el.name.c_str(), we->ani.png.c_str(), we->ani.sprite_index.c_str(), we.use_count());
        if (MessageBox(hwnd, err, L"实体未通过强引用数量检查", MB_ICONWARNING | MB_YESNO) == IDYES)
            we->Init(we);
    }
}

void FreezeGame()
{
    //暂停游戏
    isFreezed = true;
    Mix_Pause(-1);  // 暂停所有音乐和音效
    orgPlayer.FreezeMusic();
}

void ActivateGame()
{
    isFreezed = false;
    Mix_Resume(-1);  // 恢复所有音乐和音效
    orgPlayer.ActivateMusic();
}

//Windows消息处理部分
bool g_isWindowBeingDragged = false;
RECT g_lastWindowRect;
UINT_PTR g_dragCheckTimer = 0;
// 定时器回调函数
VOID CALLBACK DragCheckTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 
{
    if (g_isWindowBeingDragged)
    {
        RECT currentRect;
        GetWindowRect(hwnd, &currentRect);

        // 检查窗口位置是否改变
        if (memcmp(&g_lastWindowRect, &currentRect, sizeof(RECT)))
        {
            // 窗口位置改变了，仍在拖动中
            memcpy(&g_lastWindowRect, &currentRect, sizeof(RECT));
        }
        else 
        {
            // 窗口位置没有改变，检查鼠标状态
            if (!(GetAsyncKeyState(VK_LBUTTON) & 0x8000))
            {
                // 鼠标左键已释放，拖动结束
                g_isWindowBeingDragged = false;
                KillTimer(hwnd, g_dragCheckTimer);
                g_dragCheckTimer = 0;
                ActivateGame();
            }
        }
    }
}


LRESULT CALLBACK SubclassedWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_NCLBUTTONDOWN:
        if (wParam == HTCAPTION && !g_isWindowBeingDragged) 
        {
            g_isWindowBeingDragged = true;
            GetWindowRect(hwnd, &g_lastWindowRect);
            // 设置定时器，每100毫秒检查一次
            g_dragCheckTimer = SetTimer(hwnd, 1, 100, DragCheckTimerProc);
            FreezeGame();
        }
        break;
    case WM_ENTERSIZEMOVE:
        if (!g_isWindowBeingDragged) 
        {
            g_isWindowBeingDragged = true;
            GetWindowRect(hwnd, &g_lastWindowRect);
            g_dragCheckTimer = SetTimer(hwnd, 1, 100, DragCheckTimerProc);
            FreezeGame();
        }
        break;
    }
    //调用原始窗口过程函数
    return CallWindowProc(originalWndProc, hwnd, uMsg, wParam, lParam);
}