From 1b47cf76d3801676d6266fee960b31f3acf234fc Mon Sep 17 00:00:00 2001 From: DavivaD Date: Thu, 2 Aug 2018 07:38:32 +0200 Subject: [PATCH 01/29] Implement Mod Manager --- src/CMakeLists.txt | 2 + src/app/app.cpp | 20 ++ src/app/app.h | 2 + src/app/pathman.cpp | 23 ++- src/app/pathman.h | 1 + src/common/event.cpp | 8 + src/common/event.h | 8 + src/common/restext.cpp | 8 + src/common/restext.h | 3 + src/graphics/engine/engine.h | 8 +- src/graphics/engine/text.cpp | 2 + src/level/robotmain.cpp | 1 + src/level/robotmain.h | 1 + src/ui/mainui.cpp | 7 +- src/ui/mainui.h | 2 + src/ui/screen/screen_setup.cpp | 24 ++- src/ui/screen/screen_setup_mods.cpp | 281 ++++++++++++++++++++++++++++ src/ui/screen/screen_setup_mods.h | 50 +++++ 18 files changed, 444 insertions(+), 7 deletions(-) create mode 100644 src/ui/screen/screen_setup_mods.cpp create mode 100644 src/ui/screen/screen_setup_mods.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c58172bd..1dd6edb1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -570,6 +570,8 @@ set(BASE_SOURCES ui/screen/screen_setup_game.h ui/screen/screen_setup_graphics.cpp ui/screen/screen_setup_graphics.h + ui/screen/screen_setup_mods.cpp + ui/screen/screen_setup_mods.h ui/screen/screen_setup_sound.cpp ui/screen/screen_setup_sound.h ui/screen/screen_welcome.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index 3444481c..ff8df246 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -699,6 +699,26 @@ bool CApplication::Create() return true; } +void CApplication::Reload() +{ + m_sound->Create(); + m_engine->ReloadAllTextures(); + CThread musicLoadThread([this]() + { + SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); + m_systemUtils->GetCurrentTimeStamp(musicLoadStart); + m_sound->CacheAll(); + SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp(); + m_systemUtils->GetCurrentTimeStamp(musicLoadEnd); + float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC); + GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime); + }, + "Sound loading thread"); + musicLoadThread.Start(); + m_controller->GetRobotMain()->UpdateCustomLevelList(); +} + + bool CApplication::CreateVideoSurface() { Uint32 videoFlags = SDL_WINDOW_OPENGL; diff --git a/src/app/app.h b/src/app/app.h index ccae3a5c..2a7cc67d 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -168,6 +168,8 @@ public: ParseArgsStatus ParseArguments(int argc, char *argv[]); //! Initializes the application bool Create(); + //! Reloads the application + void Reload(); //! Main event loop int Run(); //! Returns the code to be returned at main() exit diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index 60715ac1..26eebdf8 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -72,7 +72,28 @@ void CPathManager::AddModAutoloadDir(const std::string &modAutoloadDirPath) void CPathManager::AddMod(const std::string &modPath) { - m_mods.push_back(modPath); + std::string::size_type ON; + ON = modPath.find('~'); + if (ON == std::string::npos) + { + GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); + m_mods.push_back(modPath); + } + else + { + GetLogger()->Info("Found Excluded mod: '%s'\n", modPath.c_str()); + } +} + +void CPathManager::RemoveMod(const std::string &modPath) +{ + std::string::size_type ON; + ON = modPath.find('~'); + if (ON == std::string::npos) + { + GetLogger()->Info("Unloading mod: '%s'\n", modPath.c_str()); + CResourceManager::RemoveLocation(modPath); + } } const std::string& CPathManager::GetDataPath() diff --git a/src/app/pathman.h b/src/app/pathman.h index dd18d66e..66dcae59 100644 --- a/src/app/pathman.h +++ b/src/app/pathman.h @@ -39,6 +39,7 @@ public: void SetSavePath(const std::string &savePath); void AddModAutoloadDir(const std::string &modAutoloadDirPath); void AddMod(const std::string &modPath); + void RemoveMod(const std::string &modPath); const std::string& GetDataPath(); const std::string& GetLangPath(); diff --git a/src/common/event.cpp b/src/common/event.cpp index 8dded9e1..7ceea30e 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -196,6 +196,7 @@ void InitializeEventTypeTexts() EVENT_TYPE_TEXT[EVENT_INTERFACE_SETUPp] = "EVENT_INTERFACE_SETUPp"; EVENT_TYPE_TEXT[EVENT_INTERFACE_SETUPc] = "EVENT_INTERFACE_SETUPc"; EVENT_TYPE_TEXT[EVENT_INTERFACE_SETUPs] = "EVENT_INTERFACE_SETUPs"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_SETUPm] = "EVENT_INTERFACE_SETUPm"; EVENT_TYPE_TEXT[EVENT_INTERFACE_DEVICE] = "EVENT_INTERFACE_DEVICE"; EVENT_TYPE_TEXT[EVENT_INTERFACE_RESOL] = "EVENT_INTERFACE_RESOL"; @@ -265,6 +266,13 @@ void InitializeEventTypeTexts() EVENT_TYPE_TEXT[EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT]= "EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT"; EVENT_TYPE_TEXT[EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT]= "EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_UNLOADED] = "EVENT_INTERFACE_MODS_UNLOADED"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_LOADED] = "EVENT_INTERFACE_MODS_LOADED"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_WORKSHOP] = "EVENT_INTERFACE_WORKSHOP"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_DIR] = "EVENT_INTERFACE_MODS_DIR"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_LOAD] = "EVENT_INTERFACE_LOAD"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_UNLOAD] = "EVENT_INTERFACE_UNLOAD"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_GLINTl] = "EVENT_INTERFACE_GLINTl"; EVENT_TYPE_TEXT[EVENT_INTERFACE_GLINTr] = "EVENT_INTERFACE_GLINTr"; EVENT_TYPE_TEXT[EVENT_INTERFACE_GLINTu] = "EVENT_INTERFACE_GLINTu"; diff --git a/src/common/event.h b/src/common/event.h index 64c52e41..faabfd9f 100644 --- a/src/common/event.h +++ b/src/common/event.h @@ -231,6 +231,7 @@ enum EventType EVENT_INTERFACE_SETUPp = 432, EVENT_INTERFACE_SETUPc = 433, EVENT_INTERFACE_SETUPs = 434, + EVENT_INTERFACE_SETUPm = 435, EVENT_INTERFACE_DEVICE = 440, EVENT_INTERFACE_RESOL = 441, @@ -304,6 +305,13 @@ enum EventType EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT = 573, EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT = 574, + EVENT_INTERFACE_MODS_UNLOADED = 580, + EVENT_INTERFACE_MODS_LOADED = 581, + EVENT_INTERFACE_WORKSHOP = 582, + EVENT_INTERFACE_MODS_DIR = 583, + EVENT_INTERFACE_LOAD = 584, + EVENT_INTERFACE_UNLOAD = 585, + EVENT_INTERFACE_GLINTl = 590, EVENT_INTERFACE_GLINTr = 591, EVENT_INTERFACE_GLINTu = 592, diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 9bac8434..ac664d58 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -89,6 +89,9 @@ void InitializeRestext() stringsText[RT_SETUP_KEY1] = TR("1) First click on the key you want to redefine."); stringsText[RT_SETUP_KEY2] = TR("2) Then press the key you want to use instead."); + stringsText[RT_MODS_UNLOADED] = TR("Unloaded Mods:"); + stringsText[RT_MODS_LOADED] = TR("Loaded Mods:"); + stringsText[RT_PERSO_FACE] = TR("Face type:"); stringsText[RT_PERSO_GLASSES] = TR("Eyeglasses:"); stringsText[RT_PERSO_HAIR] = TR("Hair color:"); @@ -176,11 +179,16 @@ void InitializeRestext() stringsEvent[EVENT_INTERFACE_QUIT] = TR("Quit\\Quit Colobot: Gold Edition"); stringsEvent[EVENT_INTERFACE_BACK] = TR("<< Back \\Back to the previous screen"); stringsEvent[EVENT_INTERFACE_PLAY] = TR("Play\\Start mission!"); + stringsEvent[EVENT_INTERFACE_WORKSHOP] = TR("Workshop\\Open Workshop to search Mods"); + stringsEvent[EVENT_INTERFACE_MODS_DIR] = TR("Open Directory\\Open Mods directory"); + stringsEvent[EVENT_INTERFACE_LOAD] = TR("Load\\Load Mod"); + stringsEvent[EVENT_INTERFACE_UNLOAD] = TR("Unload\\Unload Mod"); stringsEvent[EVENT_INTERFACE_SETUPd] = TR("Device\\Driver and resolution settings"); stringsEvent[EVENT_INTERFACE_SETUPg] = TR("Graphics\\Graphics settings"); stringsEvent[EVENT_INTERFACE_SETUPp] = TR("Game\\Game settings"); stringsEvent[EVENT_INTERFACE_SETUPc] = TR("Controls\\Keyboard, joystick and mouse settings"); stringsEvent[EVENT_INTERFACE_SETUPs] = TR("Sound\\Music and game sound volume"); + stringsEvent[EVENT_INTERFACE_SETUPm] = TR("Mods\\Manage installed mods"); stringsEvent[EVENT_INTERFACE_DEVICE] = TR("Unit"); stringsEvent[EVENT_INTERFACE_RESOL] = TR("Resolution"); stringsEvent[EVENT_INTERFACE_FULL] = TR("Full screen\\Full screen or window mode"); diff --git a/src/common/restext.h b/src/common/restext.h index 6acfb610..81cff850 100644 --- a/src/common/restext.h +++ b/src/common/restext.h @@ -86,6 +86,9 @@ enum ResTextType RT_SETUP_KEY1 = 82, RT_SETUP_KEY2 = 83, + RT_MODS_UNLOADED = 85, + RT_MODS_LOADED = 86, + RT_PERSO_FACE = 90, RT_PERSO_GLASSES = 91, RT_PERSO_HAIR = 92, diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index 794bd9e8..10fa04f4 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -1182,6 +1182,10 @@ public: void EnablePauseBlur(); void DisablePauseBlur(); + //! Reloads all textures + /** This additionally sends EVENT_RELOAD_TEXTURES to reload all textures not maintained by CEngine **/ + void ReloadAllTextures(); + protected: //! Resets some states and flushes textures after device was changed (e.g. resoulution changed) /** Instead of calling this directly, send EVENT_RESOLUTION_CHANGED event **/ @@ -1280,10 +1284,6 @@ protected: }; static void WriteScreenShotThread(std::unique_ptr data); - //! Reloads all textures - /** This additionally sends EVENT_RELOAD_TEXTURES to reload all textures not maintained by CEngine **/ - void ReloadAllTextures(); - protected: CApplication* m_app; CSystemUtils* m_systemUtils; diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index a16439f4..c930db8e 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -261,6 +261,8 @@ void CText::FlushCache() m_lastCachedFont = nullptr; m_lastFontType = FONT_COMMON; m_lastFontSize = 0; + + Create(); } int CText::GetTabSize() diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 3bf3e2d5..14e26f9e 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -321,6 +321,7 @@ std::string PhaseToString(Phase phase) if (phase == PHASE_SETUPp) return "PHASE_SETUPp"; if (phase == PHASE_SETUPc) return "PHASE_SETUPc"; if (phase == PHASE_SETUPs) return "PHASE_SETUPs"; + if (phase == PHASE_SETUPm) return "PHASE_SETUPm"; if (phase == PHASE_SETUPds) return "PHASE_SETUPds"; if (phase == PHASE_SETUPgs) return "PHASE_SETUPgs"; if (phase == PHASE_SETUPps) return "PHASE_SETUPps"; diff --git a/src/level/robotmain.h b/src/level/robotmain.h index 2614f2cc..68768beb 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -61,6 +61,7 @@ enum Phase PHASE_SETUPp, PHASE_SETUPc, PHASE_SETUPs, + PHASE_SETUPm, PHASE_SETUPds, PHASE_SETUPgs, PHASE_SETUPps, diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp index 2ffb17fe..7e63a7db 100644 --- a/src/ui/mainui.cpp +++ b/src/ui/mainui.cpp @@ -53,6 +53,7 @@ #include "ui/screen/screen_setup_display.h" #include "ui/screen/screen_setup_game.h" #include "ui/screen/screen_setup_graphics.h" +#include "ui/screen/screen_setup_mods.h" #include "ui/screen/screen_setup_sound.h" #include "ui/screen/screen_welcome.h" @@ -82,6 +83,7 @@ CMainUserInterface::CMainUserInterface() m_screenSetupDisplay = MakeUnique(); m_screenSetupGame = MakeUnique(); m_screenSetupGraphics = MakeUnique(); + m_screenSetupMods = MakeUnique(); m_screenSetupSound = MakeUnique(); m_screenMainMenu = MakeUnique(); m_screenPlayerSelect = MakeUnique(m_dialog.get()); @@ -142,6 +144,7 @@ CScreenSetup* CMainUserInterface::GetSetupScreen(Phase phase) if(phase == PHASE_SETUPp) return m_screenSetupGame.get(); if(phase == PHASE_SETUPc) return m_screenSetupControls.get(); if(phase == PHASE_SETUPs) return m_screenSetupSound.get(); + if(phase == PHASE_SETUPm) return m_screenSetupMods.get(); assert(false); return nullptr; } @@ -182,7 +185,7 @@ void CMainUserInterface::ChangePhase(Phase phase) m_screenLevelList->SetLevelCategory(m_main->GetLevelCategory()); m_currentScreen = m_screenLevelList.get(); } - if (m_phase >= PHASE_SETUPd && m_phase <= PHASE_SETUPs) + if (m_phase >= PHASE_SETUPd && m_phase <= PHASE_SETUPm) { CScreenSetup* screenSetup = GetSetupScreen(m_phase); screenSetup->SetInSimulation(false); @@ -345,6 +348,7 @@ void CMainUserInterface::GlintMove() m_phase == PHASE_SETUPp || m_phase == PHASE_SETUPc || m_phase == PHASE_SETUPs || + m_phase == PHASE_SETUPm || m_phase == PHASE_SETUPds || m_phase == PHASE_SETUPgs || m_phase == PHASE_SETUPps || @@ -534,6 +538,7 @@ void CMainUserInterface::FrameParticle(float rTime) m_phase == PHASE_SETUPp || m_phase == PHASE_SETUPc || m_phase == PHASE_SETUPs || + m_phase == PHASE_SETUPm || m_phase == PHASE_READ ) { pParti = partiPosBig; diff --git a/src/ui/mainui.h b/src/ui/mainui.h index 77074492..0d3ce91e 100644 --- a/src/ui/mainui.h +++ b/src/ui/mainui.h @@ -55,6 +55,7 @@ class CScreenSetupControls; class CScreenSetupDisplay; class CScreenSetupGame; class CScreenSetupGraphics; +class CScreenSetupMods; class CScreenSetupSound; class CScreenWelcome; @@ -119,6 +120,7 @@ protected: std::unique_ptr m_screenSetupDisplay; std::unique_ptr m_screenSetupGame; std::unique_ptr m_screenSetupGraphics; + std::unique_ptr m_screenSetupMods; std::unique_ptr m_screenSetupSound; std::unique_ptr m_screenWelcome; diff --git a/src/ui/screen/screen_setup.cpp b/src/ui/screen/screen_setup.cpp index 823207af..e9c36a71 100644 --- a/src/ui/screen/screen_setup.cpp +++ b/src/ui/screen/screen_setup.cpp @@ -83,7 +83,7 @@ void CScreenSetup::CreateInterface() ddim.y = 0.05f; pw->CreateGroup(pos, ddim, 3, EVENT_NULL); // transparent -> gray - ddim.x = 0.78f/5-0.01f; + ddim.x = 0.65f/5-0.01f; ddim.y = 0.06f; pos.x = 0.115f; pos.y = 0.76f; @@ -116,6 +116,12 @@ void CScreenSetup::CreateInterface() pb->SetState(STATE_CARD); pb->SetState(STATE_CHECK, (m_tab == PHASE_SETUPs)); + pos.x += ddim.x+0.01f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_SETUPm); + pb->SetState(STATE_SHADOW); + pb->SetState(STATE_CARD); + pb->SetState(STATE_CHECK, (m_tab == PHASE_SETUPm)); + pos.x = 0.10f; ddim.x = 0.80f; pos.y = 0.34f; @@ -148,6 +154,10 @@ bool CScreenSetup::EventProcess(const Event &event) CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return false; + CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_SETUPm)); + if ( pb == nullptr ) return false; + pb->SetState(STATE_ENABLE); + if ( event.type == pw->GetEventTypeClose() || event.type == EVENT_INTERFACE_BACK || (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(ESCAPE)) ) @@ -179,6 +189,10 @@ bool CScreenSetup::EventProcess(const Event &event) m_main->ChangePhase(PHASE_SETUPs); return false; + case EVENT_INTERFACE_SETUPm: + m_main->ChangePhase(PHASE_SETUPm); + return false; + default: break; } @@ -188,6 +202,10 @@ bool CScreenSetup::EventProcess(const Event &event) CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return false; + CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_SETUPm)); + if ( pb == nullptr ) return false; + pb->ClearState(STATE_ENABLE); + if ( event.type == pw->GetEventTypeClose() || event.type == EVENT_INTERFACE_BACK || (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(ESCAPE)) ) @@ -221,6 +239,10 @@ bool CScreenSetup::EventProcess(const Event &event) m_main->ChangePhase(PHASE_SETUPss); return false; + case EVENT_INTERFACE_SETUPm: + m_main->ChangePhase(PHASE_SETUPm); + return false; + default: break; } diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp new file mode 100644 index 00000000..960d7e21 --- /dev/null +++ b/src/ui/screen/screen_setup_mods.cpp @@ -0,0 +1,281 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2018, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#include "ui/screen/screen_setup_mods.h" + +#include "app/app.h" +#include "app/pathman.h" + +#include "common/restext.h" +#include "common/config.h" +#include "common/logger.h" +#include "common/settings.h" + +#include "common/resources/resourcemanager.h" +#include "level/parser/parser.h" + +#include "ui/controls/button.h" +#include "ui/controls/edit.h" +#include "ui/controls/interface.h" +#include "ui/controls/label.h" +#include "ui/controls/list.h" +#include "ui/controls/window.h" + +#include +#include + +namespace Ui +{ + +CScreenSetupMods::CScreenSetupMods() +{ +} + +void CScreenSetupMods::SetActive() +{ + m_tab = PHASE_SETUPm; +} + +void CScreenSetupMods::CreateInterface() +{ + CWindow* pw; + CLabel* pl; + CButton* pb; + CList* pli; + Math::Point pos, ddim; + std::string name; + + CScreenSetup::CreateInterface(); + pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if ( pw == nullptr ) return; + + // Displays a list of unloaded mods: + pos.x = ox+sx*3; + pos.y = oy+sy*9; + ddim.x = dim.x*6; + ddim.y = dim.y*1; + GetResource(RES_TEXT, RT_MODS_UNLOADED, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, name); + pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); + + pos.y = oy+sy*3.75f; + ddim.x = dim.x*6.5f; + ddim.y = dim.y*6.05f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_MODS_UNLOADED); + pli->SetState(STATE_SHADOW); + UpdateUnloadedModList(); + + // Displays a list of loaded mods: + pos.x = ox+sx*9.5f; + pos.y = oy+sy*9; + ddim.x = dim.x*6; + ddim.y = dim.y*1; + GetResource(RES_TEXT, RT_MODS_LOADED, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, name); + pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); + + pos.y = oy+sy*3.75f; + ddim.x = dim.x*6.5f; + ddim.y = dim.y*6.05f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_MODS_LOADED); + pli->SetState(STATE_SHADOW); + UpdateLoadedModList(); + + pos = pli->GetPos(); + ddim = pli->GetDim(); + pos.x = ox+sx*8.2f; + pos.y = oy+sy*2; + ddim.x = dim.x*1; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, 40, EVENT_INTERFACE_WORKSHOP); + pb->SetState(STATE_SHADOW); + + pos.x += dim.x*1.3f; + pb = pw->CreateButton(pos, ddim, 57, EVENT_INTERFACE_MODS_DIR); + pb->SetState(STATE_SHADOW); + + pos.x += dim.x*1.3f; + ddim.x = dim.x*2.5f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_LOAD); + pb->SetState(STATE_SHADOW); + pb->ClearState(STATE_ENABLE); + + pos.x += dim.x*2.8f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_UNLOAD); + pb->SetState(STATE_SHADOW); + pb->ClearState(STATE_ENABLE); +} +bool CScreenSetupMods::EventProcess(const Event &event) +{ + CWindow* pw; + CButton* pb; + CList* pl; + int result; + std::string modName, modPath, modPathRaw, OFF = "~"; + + if (!CScreenSetup::EventProcess(event)) return false; + + pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if ( pw == nullptr ) return false; + + if (event.type == EVENT_INTERFACE_LOAD) + { + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); + if ( pl == nullptr ) return false; + modName = pl->GetItemName(pl->GetSelect()); + + modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; + modPath = modPathRaw.c_str(); + boost::filesystem::rename(modPath+OFF+modName, modPath+modName); + m_pathManager->AddMod(modPath+modName); + m_app->Reload(); + m_main->ChangePhase(PHASE_SETUPm); + } + if (event.type == EVENT_INTERFACE_UNLOAD) + { + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); + if ( pl == nullptr ) return false; + modName = pl->GetItemName(pl->GetSelect()); + + modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; + modPath = modPathRaw.c_str(); + m_pathManager->RemoveMod(modPath+modName); + boost::filesystem::rename(modPath+modName, modPath+OFF+modName); + m_app->Reload(); + m_main->ChangePhase(PHASE_SETUPm); + } + if (event.type == EVENT_INTERFACE_MODS_DIR) + { + modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods"; + #if defined(PLATFORM_WINDOWS) + result = system(("start \""+modPathRaw+"\"").c_str()); + #elif defined(PLATFORM_LINUX) + result = system(("xdg-open \""+modPathRaw+"\"").c_str()); + #elif defined(PLATFORM_MACOSX) + result = system(("open \""+modPathRaw+"\"").c_str()); + #endif + if (result == -1) + { + GetLogger()->Error("Failed to open Mods directory! Does directory exists?\n"); + } + } + switch (event.type) + { + case EVENT_INTERFACE_MODS_UNLOADED: + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); + if ( pl == nullptr ) break; + + pb = static_cast(pw->SearchControl(EVENT_INTERFACE_UNLOAD)); + if ( pb == nullptr ) break; + pl->SetSelect(-1); + pb->ClearState(STATE_ENABLE); + + pb = static_cast(pw->SearchControl(EVENT_INTERFACE_LOAD)); + if ( pb == nullptr ) break; + pb->SetState(STATE_ENABLE); + break; + + case EVENT_INTERFACE_MODS_LOADED: + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); + if ( pl == nullptr ) break; + + pb = static_cast(pw->SearchControl(EVENT_INTERFACE_LOAD)); + if ( pb == nullptr ) break; + pl->SetSelect(-1); + pb->ClearState(STATE_ENABLE); + + pb = static_cast(pw->SearchControl(EVENT_INTERFACE_UNLOAD)); + if ( pb == nullptr ) break; + pb->SetState(STATE_ENABLE); + break; + + case EVENT_INTERFACE_WORKSHOP: + #if defined(PLATFORM_WINDOWS) + result = system("start \"https://colobot.info/forum/forumdisplay.php?fid=60\""); + #elif defined(PLATFORM_LINUX) + result = system("xdg-open \"https://colobot.info/forum/forumdisplay.php?fid=60\""); + #elif defined(PLATFORM_MACOSX) + result = system("open \"https://colobot.info/forum/forumdisplay.php?fid=60\""); + #endif + if (result == -1) + { + GetLogger()->Error("Failed to open Workshop page! Is any Web Broswer installed?\n"); + } + break; + default: + return true; + } + return false; +} +void CScreenSetupMods::UpdateUnloadedModList() +{ + CWindow* pw; + CList* pl; + int i = 0; + std::string modName; + + pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if ( pw == nullptr ) return; + + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); + if ( pl == nullptr ) return; + pl->Flush(); + + auto modsDir = CResourceManager::ListDirectories("mods/"); + std::sort(modsDir.begin(), modsDir.end()); + + for(auto const& modNameRaw : modsDir) + { + modName = modNameRaw; + std::string::size_type ON; + ON = modName.find('~'); + if (ON != std::string::npos) + { + modName.erase(0,1); + pl->SetItemName(i++, modName); + } + } + pl->ShowSelect(false); // shows the selected columns +} +void CScreenSetupMods::UpdateLoadedModList() +{ + CWindow* pw; + CList* pl; + int i = 0; + + pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if ( pw == nullptr ) return; + + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); + if ( pl == nullptr ) return; + pl->Flush(); + + auto modsDir = CResourceManager::ListDirectories("mods/"); + std::sort(modsDir.begin(), modsDir.end()); + + for(auto const &modName : modsDir) + { + std::string::size_type ON; + ON = modName.find('~'); + if (ON == std::string::npos) + pl->SetItemName(i++, modName); + } + pl->ShowSelect(false); // shows the selected columns +} +} // namespace Ui diff --git a/src/ui/screen/screen_setup_mods.h b/src/ui/screen/screen_setup_mods.h new file mode 100644 index 00000000..0d54502f --- /dev/null +++ b/src/ui/screen/screen_setup_mods.h @@ -0,0 +1,50 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2018, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#pragma once + +#include "ui/screen/screen_setup.h" + +#include + +class CPathManager; + +namespace Ui +{ + +class CScreenSetupMods : public CScreenSetup +{ +public: + CScreenSetupMods(); + void SetActive() override; + + void CreateInterface() override; + bool EventProcess(const Event &event) override; + +protected: + void UpdateUnloadedModList(); + void UpdateLoadedModList(); + +protected: + CPathManager* m_pathManager; + std::vector m_unloadedModList; + std::vector m_loadedModList; +}; + +} // namespace Ui From e823c209faf2084dca8aacc73d8b223b955f27f9 Mon Sep 17 00:00:00 2001 From: DavivaD Date: Thu, 2 Aug 2018 17:21:31 +0200 Subject: [PATCH 02/29] Fix crash while loading mod and blank settings menu while paused game --- src/app/pathman.cpp | 30 ++++++++++++++++++++++++------ src/level/robotmain.cpp | 4 +++- src/level/robotmain.h | 1 + src/ui/maindialog.cpp | 1 + src/ui/mainui.cpp | 2 +- src/ui/screen/screen_setup.cpp | 1 - 6 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index 26eebdf8..9656d3eb 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -77,11 +77,11 @@ void CPathManager::AddMod(const std::string &modPath) if (ON == std::string::npos) { GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); - m_mods.push_back(modPath); + CResourceManager::AddLocation(modPath, true); } else { - GetLogger()->Info("Found Excluded mod: '%s'\n", modPath.c_str()); + GetLogger()->Info("Found excluded mod: '%s'\n", modPath.c_str()); } } @@ -171,15 +171,33 @@ void CPathManager::InitPaths() GetLogger()->Trace("Searching for mods in '%s'...\n", modAutoloadDir.c_str()); for (const std::string& modPath : FindModsInDir(modAutoloadDir)) { - GetLogger()->Info("Autoloading mod: '%s'\n", modPath.c_str()); - CResourceManager::AddLocation(modPath); + std::string::size_type ON; + ON = modPath.find('~'); + if (ON == std::string::npos) + { + GetLogger()->Info("Autoloading mod: '%s'\n", modPath.c_str()); + CResourceManager::AddLocation(modPath); + } + else + { + GetLogger()->Info("Found excluded mod: '%s'\n", modPath.c_str()); + } } } for (const std::string& modPath : m_mods) { - GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); - CResourceManager::AddLocation(modPath); + std::string::size_type ON; + ON = modPath.find('~'); + if (ON == std::string::npos) + { + GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); + CResourceManager::AddLocation(modPath); + } + else + { + GetLogger()->Info("Found excluded mod: '%s'\n", modPath.c_str()); + } } CResourceManager::SetSaveLocation(m_savePath); diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 14e26f9e..14397feb 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -327,6 +327,7 @@ std::string PhaseToString(Phase phase) if (phase == PHASE_SETUPps) return "PHASE_SETUPps"; if (phase == PHASE_SETUPcs) return "PHASE_SETUPcs"; if (phase == PHASE_SETUPss) return "PHASE_SETUPss"; + if (phase == PHASE_SETUPms) return "PHASE_SETUPms"; if (phase == PHASE_WRITEs) return "PHASE_WRITEs"; if (phase == PHASE_READ) return "PHASE_READ"; if (phase == PHASE_READs) return "PHASE_READs"; @@ -339,7 +340,7 @@ std::string PhaseToString(Phase phase) bool IsInSimulationConfigPhase(Phase phase) { - return (phase >= PHASE_SETUPds && phase <= PHASE_SETUPss) || phase == PHASE_READs || phase == PHASE_WRITEs; + return (phase >= PHASE_SETUPds && phase <= PHASE_SETUPms) || phase == PHASE_READs || phase == PHASE_WRITEs; } bool IsPhaseWithWorld(Phase phase) @@ -3865,6 +3866,7 @@ void CRobotMain::ChangeColor() m_phase != PHASE_SETUPps && m_phase != PHASE_SETUPcs && m_phase != PHASE_SETUPss && + m_phase != PHASE_SETUPms && m_phase != PHASE_WIN && m_phase != PHASE_LOST && m_phase != PHASE_APPERANCE ) return; diff --git a/src/level/robotmain.h b/src/level/robotmain.h index 68768beb..cd3345e4 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -67,6 +67,7 @@ enum Phase PHASE_SETUPps, PHASE_SETUPcs, PHASE_SETUPss, + PHASE_SETUPms, PHASE_WRITEs, PHASE_READ, PHASE_READs, diff --git a/src/ui/maindialog.cpp b/src/ui/maindialog.cpp index 01d87dc2..395a875e 100644 --- a/src/ui/maindialog.cpp +++ b/src/ui/maindialog.cpp @@ -107,6 +107,7 @@ bool CMainDialog::EventProcess(const Event &event) if ( CScreenSetup::GetTab() == PHASE_SETUPp ) m_main->ChangePhase(PHASE_SETUPps); if ( CScreenSetup::GetTab() == PHASE_SETUPc ) m_main->ChangePhase(PHASE_SETUPcs); if ( CScreenSetup::GetTab() == PHASE_SETUPs ) m_main->ChangePhase(PHASE_SETUPss); + if ( CScreenSetup::GetTab() == PHASE_SETUPm ) m_main->ChangePhase(PHASE_SETUPss); } if ( pressedButton == EVENT_INTERFACE_WRITE ) diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp index 7e63a7db..1ca364bc 100644 --- a/src/ui/mainui.cpp +++ b/src/ui/mainui.cpp @@ -192,7 +192,7 @@ void CMainUserInterface::ChangePhase(Phase phase) screenSetup->SetActive(); m_currentScreen = screenSetup; } - if (m_phase >= PHASE_SETUPds && m_phase <= PHASE_SETUPss) + if (m_phase >= PHASE_SETUPds && m_phase <= PHASE_SETUPms) { CScreenSetup* screenSetup = GetSetupScreen(static_cast(m_phase - PHASE_SETUPds + PHASE_SETUPd)); screenSetup->SetInSimulation(true); diff --git a/src/ui/screen/screen_setup.cpp b/src/ui/screen/screen_setup.cpp index e9c36a71..3dbc0836 100644 --- a/src/ui/screen/screen_setup.cpp +++ b/src/ui/screen/screen_setup.cpp @@ -240,7 +240,6 @@ bool CScreenSetup::EventProcess(const Event &event) return false; case EVENT_INTERFACE_SETUPm: - m_main->ChangePhase(PHASE_SETUPm); return false; default: From cd140f13841f468ff375cf23c54ae6447d8f2aaa Mon Sep 17 00:00:00 2001 From: DavivaD Date: Sat, 27 Jul 2019 03:36:03 +0200 Subject: [PATCH 03/29] Move mod loading/unloading stuff to a new class + fix some derps --- src/app/pathman.cpp | 40 +++++++++--------- src/ui/screen/screen_setup.cpp | 2 +- src/ui/screen/screen_setup_mods.cpp | 64 ++++++++++++++++++----------- src/ui/screen/screen_setup_mods.h | 4 +- 4 files changed, 65 insertions(+), 45 deletions(-) diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index 9656d3eb..11776d19 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -72,9 +72,9 @@ void CPathManager::AddModAutoloadDir(const std::string &modAutoloadDirPath) void CPathManager::AddMod(const std::string &modPath) { - std::string::size_type ON; - ON = modPath.find('~'); - if (ON == std::string::npos) + std::string::size_type enabled; + enabled = modPath.find('~'); + if (enabled == std::string::npos) { GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); CResourceManager::AddLocation(modPath, true); @@ -87,9 +87,9 @@ void CPathManager::AddMod(const std::string &modPath) void CPathManager::RemoveMod(const std::string &modPath) { - std::string::size_type ON; - ON = modPath.find('~'); - if (ON == std::string::npos) + std::string::size_type enabled; + enabled = modPath.find('~'); + if (enabled == std::string::npos) { GetLogger()->Info("Unloading mod: '%s'\n", modPath.c_str()); CResourceManager::RemoveLocation(modPath); @@ -171,9 +171,9 @@ void CPathManager::InitPaths() GetLogger()->Trace("Searching for mods in '%s'...\n", modAutoloadDir.c_str()); for (const std::string& modPath : FindModsInDir(modAutoloadDir)) { - std::string::size_type ON; - ON = modPath.find('~'); - if (ON == std::string::npos) + std::string::size_type enabled; + enabled = modPath.find('~'); + if (enabled == std::string::npos) { GetLogger()->Info("Autoloading mod: '%s'\n", modPath.c_str()); CResourceManager::AddLocation(modPath); @@ -187,17 +187,17 @@ void CPathManager::InitPaths() for (const std::string& modPath : m_mods) { - std::string::size_type ON; - ON = modPath.find('~'); - if (ON == std::string::npos) - { - GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); - CResourceManager::AddLocation(modPath); - } - else - { - GetLogger()->Info("Found excluded mod: '%s'\n", modPath.c_str()); - } + std::string::size_type enabled; + enabled = modPath.find('~'); + if (enabled == std::string::npos) + { + GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); + CResourceManager::AddLocation(modPath); + } + else + { + GetLogger()->Info("Found excluded mod: '%s'\n", modPath.c_str()); + } } CResourceManager::SetSaveLocation(m_savePath); diff --git a/src/ui/screen/screen_setup.cpp b/src/ui/screen/screen_setup.cpp index 3dbc0836..66bdac20 100644 --- a/src/ui/screen/screen_setup.cpp +++ b/src/ui/screen/screen_setup.cpp @@ -240,7 +240,7 @@ bool CScreenSetup::EventProcess(const Event &event) return false; case EVENT_INTERFACE_SETUPm: - return false; + assert(false); // should never get here default: break; diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp index 960d7e21..696e6a02 100644 --- a/src/ui/screen/screen_setup_mods.cpp +++ b/src/ui/screen/screen_setup_mods.cpp @@ -1,6 +1,6 @@ /* * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2018, Daniel Roux, EPSITEC SA & TerranovaTeam + * Copyright (C) 2001-2019, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsitec.ch; http://colobot.info; http://github.com/colobot * * This program is free software: you can redistribute it and/or modify @@ -127,7 +127,7 @@ bool CScreenSetupMods::EventProcess(const Event &event) CButton* pb; CList* pl; int result; - std::string modName, modPath, modPathRaw, OFF = "~"; + std::string modName, modPath; if (!CScreenSetup::EventProcess(event)) return false; @@ -139,11 +139,8 @@ bool CScreenSetupMods::EventProcess(const Event &event) pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); if ( pl == nullptr ) return false; modName = pl->GetItemName(pl->GetSelect()); + LoadMod(modName); - modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; - modPath = modPathRaw.c_str(); - boost::filesystem::rename(modPath+OFF+modName, modPath+modName); - m_pathManager->AddMod(modPath+modName); m_app->Reload(); m_main->ChangePhase(PHASE_SETUPm); } @@ -152,23 +149,21 @@ bool CScreenSetupMods::EventProcess(const Event &event) pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); if ( pl == nullptr ) return false; modName = pl->GetItemName(pl->GetSelect()); + UnloadMod(modName); - modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; - modPath = modPathRaw.c_str(); - m_pathManager->RemoveMod(modPath+modName); - boost::filesystem::rename(modPath+modName, modPath+OFF+modName); m_app->Reload(); m_main->ChangePhase(PHASE_SETUPm); } if (event.type == EVENT_INTERFACE_MODS_DIR) { - modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods"; + modPath = CResourceManager::GetSaveLocation() + "/" + "mods"; #if defined(PLATFORM_WINDOWS) - result = system(("start \""+modPathRaw+"\"").c_str()); + std::replace(modPath.begin(), modPath.end(), '/', '\\'); + result = system(("explorer \""+modPath+"\"").c_str()); #elif defined(PLATFORM_LINUX) - result = system(("xdg-open \""+modPathRaw+"\"").c_str()); + result = system(("xdg-open \""+modPath+"\"").c_str()); #elif defined(PLATFORM_MACOSX) - result = system(("open \""+modPathRaw+"\"").c_str()); + result = system(("open \""+modPath+"\"").c_str()); //TODO: Test on macOS #endif if (result == -1) { @@ -207,11 +202,11 @@ bool CScreenSetupMods::EventProcess(const Event &event) case EVENT_INTERFACE_WORKSHOP: #if defined(PLATFORM_WINDOWS) - result = system("start \"https://colobot.info/forum/forumdisplay.php?fid=60\""); + result = system("rundll32 url.dll,FileProtocolHandler \"https://www.moddb.com/games/colobot-gold-edition\""); #elif defined(PLATFORM_LINUX) - result = system("xdg-open \"https://colobot.info/forum/forumdisplay.php?fid=60\""); + result = system("xdg-open \"https://www.moddb.com/games/colobot-gold-edition\""); #elif defined(PLATFORM_MACOSX) - result = system("open \"https://colobot.info/forum/forumdisplay.php?fid=60\""); + result = system("open \"https://www.moddb.com/games/colobot-gold-edition\""); //TODO: Test on macOS #endif if (result == -1) { @@ -223,6 +218,29 @@ bool CScreenSetupMods::EventProcess(const Event &event) } return false; } + +void CScreenSetupMods::UnloadMod(std::string modName) +{ + std::string modPath, modPathRaw, disabled = "~"; + + modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; + modPath = modPathRaw.c_str(); + + m_pathManager->RemoveMod(modPath+modName); + boost::filesystem::rename(modPath+modName, modPath+disabled+modName); +} + +void CScreenSetupMods::LoadMod(std::string modName) +{ + std::string modPath, modPathRaw, disabled = "~"; + + modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; + modPath = modPathRaw.c_str(); + + boost::filesystem::rename(modPath+disabled+modName, modPath+modName); + m_pathManager->AddMod(modPath+modName); +} + void CScreenSetupMods::UpdateUnloadedModList() { CWindow* pw; @@ -243,9 +261,9 @@ void CScreenSetupMods::UpdateUnloadedModList() for(auto const& modNameRaw : modsDir) { modName = modNameRaw; - std::string::size_type ON; - ON = modName.find('~'); - if (ON != std::string::npos) + std::string::size_type enabled; + enabled = modName.find('~'); + if (enabled != std::string::npos) { modName.erase(0,1); pl->SetItemName(i++, modName); @@ -271,9 +289,9 @@ void CScreenSetupMods::UpdateLoadedModList() for(auto const &modName : modsDir) { - std::string::size_type ON; - ON = modName.find('~'); - if (ON == std::string::npos) + std::string::size_type enabled; + enabled = modName.find('~'); + if (enabled == std::string::npos) pl->SetItemName(i++, modName); } pl->ShowSelect(false); // shows the selected columns diff --git a/src/ui/screen/screen_setup_mods.h b/src/ui/screen/screen_setup_mods.h index 0d54502f..e61d75bc 100644 --- a/src/ui/screen/screen_setup_mods.h +++ b/src/ui/screen/screen_setup_mods.h @@ -1,6 +1,6 @@ /* * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2018, Daniel Roux, EPSITEC SA & TerranovaTeam + * Copyright (C) 2001-2019, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsitec.ch; http://colobot.info; http://github.com/colobot * * This program is free software: you can redistribute it and/or modify @@ -38,6 +38,8 @@ public: bool EventProcess(const Event &event) override; protected: + void UnloadMod(std::string ModName); + void LoadMod(std::string ModName); void UpdateUnloadedModList(); void UpdateLoadedModList(); From 7d30a827477d1611bea5a2b8f60523e58d6df7b0 Mon Sep 17 00:00:00 2001 From: DavivaD Date: Sat, 27 Jul 2019 16:59:51 +0200 Subject: [PATCH 04/29] Move platform-dependent code to system_*.cpp --- src/common/system/system.cpp | 10 +++++++++ src/common/system/system.h | 6 ++++++ src/common/system/system_linux.cpp | 22 ++++++++++++++++++++ src/common/system/system_linux.h | 3 +++ src/common/system/system_macosx.cpp | 22 ++++++++++++++++++++ src/common/system/system_macosx.h | 3 +++ src/common/system/system_other.cpp | 10 +++++++++ src/common/system/system_other.h | 3 +++ src/common/system/system_windows.cpp | 22 ++++++++++++++++++++ src/common/system/system_windows.h | 3 +++ src/ui/screen/screen_setup_mods.cpp | 31 ++++++---------------------- 11 files changed, 110 insertions(+), 25 deletions(-) diff --git a/src/common/system/system.cpp b/src/common/system/system.cpp index 44064556..3c306ac0 100644 --- a/src/common/system/system.cpp +++ b/src/common/system/system.cpp @@ -192,3 +192,13 @@ std::string CSystemUtils::GetSaveDir() { return "./saves"; } + +void CSystemUtils::OpenPath(std::string path) +{ + assert(false); +} + +void CSystemUtils::OpenWebsite(std::string website) +{ + assert(false); +} diff --git a/src/common/system/system.h b/src/common/system/system.h index aaf54954..2ef0b5cb 100644 --- a/src/common/system/system.h +++ b/src/common/system/system.h @@ -134,6 +134,12 @@ public: //! Returns the save dir location virtual std::string GetSaveDir(); + //! Opens a path with default file browser + virtual void OpenPath(std::string path); + + //! Opens a website with default web browser + virtual void OpenWebsite(std::string website); + //! Sleep for given amount of microseconds virtual void Usleep(int usecs) = 0; diff --git a/src/common/system/system_linux.cpp b/src/common/system/system_linux.cpp index 6578830d..2dda1c00 100644 --- a/src/common/system/system_linux.cpp +++ b/src/common/system/system_linux.cpp @@ -126,6 +126,28 @@ std::string CSystemUtilsLinux::GetSaveDir() #endif } +void CSystemUtilsLinux::OpenPath(std::string path) +{ + int result; + + result = system(("xdg-open \""+path+"\"").c_str()); + if (result == -1) + { + GetLogger()->Error("Failed to open path: %s\n", path.c_str()); + } +} + +void CSystemUtilsLinux::OpenWebsite(std::string website) +{ + int result; + + result = system(("xdg-open \""+website+"\"").c_str()); + if (result == -1) + { + GetLogger()->Error("Failed to open website: %s\n", website.c_str()); + } +} + void CSystemUtilsLinux::Usleep(int usec) { usleep(usec); diff --git a/src/common/system/system_linux.h b/src/common/system/system_linux.h index f1576f31..c41c30d4 100644 --- a/src/common/system/system_linux.h +++ b/src/common/system/system_linux.h @@ -45,6 +45,9 @@ public: std::string GetSaveDir() override; + void OpenPath(std::string path) override; + void OpenWebsite(std::string website) override; + void Usleep(int usec) override; private: diff --git a/src/common/system/system_macosx.cpp b/src/common/system/system_macosx.cpp index 9ef7ce1d..129037aa 100644 --- a/src/common/system/system_macosx.cpp +++ b/src/common/system/system_macosx.cpp @@ -113,6 +113,28 @@ std::string CSystemUtilsMacOSX::GetSaveDir() #endif } +void CSystemUtilsLinux::OpenPath(std::string path) +{ + int result; + + result = system(("open \""+path+"\"").c_str()); // TODO: Test on macOS + if (result == -1) + { + GetLogger()->Error("Failed to open path: %s\n", path.c_str()); + } +} + +void CSystemUtilsLinux::OpenWebsite(std::string website) +{ + int result; + + result = system(("open \""+website+"\"").c_str()); // TODO: Test on macOS + if (result == -1) + { + GetLogger()->Error("Failed to open website: %s\n", website.c_str()); + } +} + void CSystemUtilsMacOSX::Usleep(int usec) { usleep(usec); diff --git a/src/common/system/system_macosx.h b/src/common/system/system_macosx.h index 5b572ec4..dc4d2733 100644 --- a/src/common/system/system_macosx.h +++ b/src/common/system/system_macosx.h @@ -36,6 +36,9 @@ public: std::string GetLangPath() override; std::string GetSaveDir() override; + void OpenPath(std::string path) override; + void OpenWebsite(std::string website) override; + void Usleep(int usec) override; private: diff --git a/src/common/system/system_other.cpp b/src/common/system/system_other.cpp index a3311a74..7d439868 100644 --- a/src/common/system/system_other.cpp +++ b/src/common/system/system_other.cpp @@ -39,6 +39,16 @@ long long int CSystemUtilsOther::TimeStampExactDiff(SystemTimeStamp* before, Sys return (after->sdlTicks - before->sdlTicks) * 1000000ll; } +void CSystemUtilsOther::OpenPath(std::string path) +{ + // TODO +} + +void CSystemUtilsOther::OpenWebsite(std::string website) +{ + // TODO +} + void CSystemUtilsOther::Usleep(int usec) { SDL_Delay(usec / 1000); // close enough diff --git a/src/common/system/system_other.h b/src/common/system/system_other.h index ac80701b..d457b1e6 100644 --- a/src/common/system/system_other.h +++ b/src/common/system/system_other.h @@ -49,6 +49,9 @@ public: void GetCurrentTimeStamp(SystemTimeStamp *stamp) override; long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after) override; + void OpenPath(std::string path) override; + void OpenWebsite(std::string website) override; + void Usleep(int usec) override; }; diff --git a/src/common/system/system_windows.cpp b/src/common/system/system_windows.cpp index a20bfcf1..3400817d 100644 --- a/src/common/system/system_windows.cpp +++ b/src/common/system/system_windows.cpp @@ -131,6 +131,28 @@ std::string CSystemUtilsWindows::GetSaveDir() #endif } +void CSystemUtilsWindows::OpenPath(std::string path) +{ + int result; + + result = system(("explorer \""+path+"\"").c_str()); // TODO: Test on macOS + if (result == -1) + { + GetLogger()->Error("Failed to open path: %s\n", path.c_str()); + } +} + +void CSystemUtilsWindows::OpenWebsite(std::string website) +{ + int result; + + result = system(("rundll32 url.dll,FileProtocolHandler \""+website+"\"").c_str()); // TODO: Test on macOS + if (result == -1) + { + GetLogger()->Error("Failed to open website: %s\n", website.c_str()); + } +} + void CSystemUtilsWindows::Usleep(int usec) { LARGE_INTEGER ft; diff --git a/src/common/system/system_windows.h b/src/common/system/system_windows.h index 74f02455..54c8611e 100644 --- a/src/common/system/system_windows.h +++ b/src/common/system/system_windows.h @@ -43,6 +43,9 @@ public: std::string GetSaveDir() override; + void OpenPath(std::string path) override; + void OpenWebsite(std::string website) override; + void Usleep(int usec) override; public: diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp index 696e6a02..d3266e2d 100644 --- a/src/ui/screen/screen_setup_mods.cpp +++ b/src/ui/screen/screen_setup_mods.cpp @@ -22,6 +22,8 @@ #include "app/app.h" #include "app/pathman.h" +#include "common/system/system.h" + #include "common/restext.h" #include "common/config.h" #include "common/logger.h" @@ -126,8 +128,8 @@ bool CScreenSetupMods::EventProcess(const Event &event) CWindow* pw; CButton* pb; CList* pl; - int result; - std::string modName, modPath; + std::string modName, modPath, website = "https://www.moddb.com/games/colobot-gold-edition"; + auto systemUtils = CSystemUtils::Create(); // platform-specific utils if (!CScreenSetup::EventProcess(event)) return false; @@ -157,18 +159,7 @@ bool CScreenSetupMods::EventProcess(const Event &event) if (event.type == EVENT_INTERFACE_MODS_DIR) { modPath = CResourceManager::GetSaveLocation() + "/" + "mods"; - #if defined(PLATFORM_WINDOWS) - std::replace(modPath.begin(), modPath.end(), '/', '\\'); - result = system(("explorer \""+modPath+"\"").c_str()); - #elif defined(PLATFORM_LINUX) - result = system(("xdg-open \""+modPath+"\"").c_str()); - #elif defined(PLATFORM_MACOSX) - result = system(("open \""+modPath+"\"").c_str()); //TODO: Test on macOS - #endif - if (result == -1) - { - GetLogger()->Error("Failed to open Mods directory! Does directory exists?\n"); - } + systemUtils->OpenPath(modPath); } switch (event.type) { @@ -201,17 +192,7 @@ bool CScreenSetupMods::EventProcess(const Event &event) break; case EVENT_INTERFACE_WORKSHOP: - #if defined(PLATFORM_WINDOWS) - result = system("rundll32 url.dll,FileProtocolHandler \"https://www.moddb.com/games/colobot-gold-edition\""); - #elif defined(PLATFORM_LINUX) - result = system("xdg-open \"https://www.moddb.com/games/colobot-gold-edition\""); - #elif defined(PLATFORM_MACOSX) - result = system("open \"https://www.moddb.com/games/colobot-gold-edition\""); //TODO: Test on macOS - #endif - if (result == -1) - { - GetLogger()->Error("Failed to open Workshop page! Is any Web Broswer installed?\n"); - } + systemUtils->OpenWebsite(website); break; default: return true; From 6f3b14202ec33ce697adc67bc5d10ab672833cab Mon Sep 17 00:00:00 2001 From: DavivaD Date: Sat, 27 Jul 2019 17:03:33 +0200 Subject: [PATCH 05/29] Mod Manager i18n --- po/colobot.pot | 21 +++++++++++++++++++++ po/cs.po | 21 +++++++++++++++++++++ po/de.po | 21 +++++++++++++++++++++ po/fr.po | 21 +++++++++++++++++++++ po/pl.po | 21 +++++++++++++++++++++ po/ru.po | 21 +++++++++++++++++++++ 6 files changed, 126 insertions(+) diff --git a/po/colobot.pot b/po/colobot.pot index 5d71af8c..675932b8 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -120,6 +120,12 @@ msgstr "" msgid "2) Then press the key you want to use instead." msgstr "" +msgid "Unloaded Mods:" +msgstr "" + +msgid "Loaded Mods:" +msgstr "" + msgid "Face type:" msgstr "" @@ -334,6 +340,18 @@ msgstr "" msgid "Play\\Start mission!" msgstr "" +msgid "Workshop\\Open Workshop to search Mods" +msgstr "" + +msgid "Open Directory\\Open Mods directory" +msgstr "" + +msgid "Load\\Load Mod" +msgstr "" + +msgid "Unload\\Unload Mod" +msgstr "" + msgid "Device\\Driver and resolution settings" msgstr "" @@ -349,6 +367,9 @@ msgstr "" msgid "Sound\\Music and game sound volume" msgstr "" +msgid "Mods\\Manage installed mods" +msgstr "" + msgid "Unit" msgstr "" diff --git a/po/cs.po b/po/cs.po index 555df720..cee89994 100644 --- a/po/cs.po +++ b/po/cs.po @@ -817,12 +817,18 @@ msgstr "Seznam uložených misí" msgid "Load a saved mission" msgstr "Nahrát uloženou misi" +msgid "Load\\Load Mod" +msgstr "" + msgid "Load\\Load a saved mission" msgstr "Nahrát\\Nahrát uloženou misi" msgid "Load\\Loads the selected mission" msgstr "Nahrát\\Nahraje vybranou misi" +msgid "Loaded Mods:" +msgstr "" + msgid "Loading basic level settings" msgstr "Načítám základní nastavení mapy" @@ -877,6 +883,9 @@ msgstr "Mise na této planetě:" msgid "Missions\\Select mission" msgstr "Mise\\Vyberte misi" +msgid "Mods\\Manage installed mods" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Vodorovné převrácení posunu\\Při vodorovném posunu kamery myší pousouvat opačným směrem" @@ -1039,6 +1048,9 @@ msgstr "Otevřít" msgid "Open (Ctrl+O)" msgstr "Otevřít (Ctrl+O)" +msgid "Open Directory\\Open Mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Chybí levá složená závorka" @@ -1640,6 +1652,12 @@ msgstr "Neznámá zástupná sekvence" msgid "Unknown function" msgstr "Neznámá funkce" +msgid "Unload\\Unload Mod" +msgstr "" + +msgid "Unloaded Mods:" +msgstr "" + msgid "Up (\\key gup;)" msgstr "Vzhůru (\\key gup;)" @@ -1706,6 +1724,9 @@ msgstr "Létající detektor" msgid "Withdraw shield (\\key action;)" msgstr "Vypnout štít (\\key action;)" +msgid "Workshop\\Open Workshop to search Mods" +msgstr "" + msgid "Worm" msgstr "Červ" diff --git a/po/de.po b/po/de.po index 553ead83..ca53d3cb 100644 --- a/po/de.po +++ b/po/de.po @@ -819,12 +819,18 @@ msgstr "Liste der gespeicherten Missionen" msgid "Load a saved mission" msgstr "Gespeicherte Mission laden" +msgid "Load\\Load Mod" +msgstr "" + msgid "Load\\Load a saved mission" msgstr "Laden\\Eine gespeicherte Mission öffnen" msgid "Load\\Loads the selected mission" msgstr "Laden\\Öffnet eine gespeicherte Mission" +msgid "Loaded Mods:" +msgstr "" + msgid "Loading basic level settings" msgstr "Lade Level-Grundeinstellungen" @@ -893,6 +899,9 @@ msgstr "Liste der Missionen des Planeten:" msgid "Missions\\Select mission" msgstr "Missionen\\Aufbruch ins Weltall" +msgid "Mods\\Manage installed mods" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Umkehr X\\Umkehr der Kameradrehung X-Achse" @@ -1055,6 +1064,9 @@ msgstr "Öffnen" msgid "Open (Ctrl+O)" msgstr "Öffnen (Ctrl+O)" +msgid "Open Directory\\Open Mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Es fehlt eine offene geschweifte Klammer\"{\"" @@ -1657,6 +1669,12 @@ msgstr "" msgid "Unknown function" msgstr "Unbekannte Funktion" +msgid "Unload\\Unload Mod" +msgstr "" + +msgid "Unloaded Mods:" +msgstr "" + msgid "Up (\\key gup;)" msgstr "Steigt (\\key gup;)" @@ -1723,6 +1741,9 @@ msgstr "Schnüffler" msgid "Withdraw shield (\\key action;)" msgstr "Schutzschild einholen (\\key action;)" +msgid "Workshop\\Open Workshop to search Mods" +msgstr "" + msgid "Worm" msgstr "Wurm" diff --git a/po/fr.po b/po/fr.po index ce3f5fdf..76afb80c 100644 --- a/po/fr.po +++ b/po/fr.po @@ -821,12 +821,18 @@ msgstr "Liste des missions enregistrées" msgid "Load a saved mission" msgstr "Chargement d'une mission enregistrée" +msgid "Load\\Load Mod" +msgstr "" + msgid "Load\\Load a saved mission" msgstr "Charger\\Charger une mission enregistrée" msgid "Load\\Loads the selected mission" msgstr "Charger\\Charger la mission sélectionnée" +msgid "Loaded Mods:" +msgstr "" + msgid "Loading basic level settings" msgstr "Chargement des configurations de base du niveau" @@ -895,6 +901,9 @@ msgstr "Liste des missions du chapitre :" msgid "Missions\\Select mission" msgstr "Missions\\La grande aventure" +msgid "Mods\\Manage installed mods" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Inversion souris X\\Inversion de la rotation lorsque la souris touche un bord" @@ -1057,6 +1066,9 @@ msgstr "Ouvrir" msgid "Open (Ctrl+O)" msgstr "Ouvrir (Ctrl+O)" +msgid "Open Directory\\Open Mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Début d'un bloc attendu" @@ -1660,6 +1672,12 @@ msgstr "" msgid "Unknown function" msgstr "Routine inconnue" +msgid "Unload\\Unload Mod" +msgstr "" + +msgid "Unloaded Mods:" +msgstr "" + msgid "Up (\\key gup;)" msgstr "Monte (\\key gup;)" @@ -1726,6 +1744,9 @@ msgstr "Robot renifleur volant" msgid "Withdraw shield (\\key action;)" msgstr "Refermer le bouclier (\\key action;)" +msgid "Workshop\\Open Workshop to search Mods" +msgstr "" + msgid "Worm" msgstr "Ver" diff --git a/po/pl.po b/po/pl.po index f9f76de8..4dd75cc4 100644 --- a/po/pl.po +++ b/po/pl.po @@ -816,12 +816,18 @@ msgstr "Lista zapisanych misji" msgid "Load a saved mission" msgstr "Wczytaj zapisaną misję" +msgid "Load\\Load Mod" +msgstr "" + msgid "Load\\Load a saved mission" msgstr "Wczytaj\\Wczytuje zapisaną misję" msgid "Load\\Loads the selected mission" msgstr "Wczytaj\\Wczytuje zaznaczoną misję" +msgid "Loaded Mods:" +msgstr "" + msgid "Loading basic level settings" msgstr "Wczytywanie ustawień poziomu" @@ -876,6 +882,9 @@ msgstr "Misje na tej planecie:" msgid "Missions\\Select mission" msgstr "Misje\\Wybierz misję" +msgid "Mods\\Manage installed mods" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Odwrócenie myszy X\\Odwrócenie kierunków przewijania w poziomie" @@ -1038,6 +1047,9 @@ msgstr "Otwórz" msgid "Open (Ctrl+O)" msgstr "Otwórz (Ctrl+O)" +msgid "Open Directory\\Open Mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Brak klamry otwierającej" @@ -1639,6 +1651,12 @@ msgstr "" msgid "Unknown function" msgstr "Funkcja nieznana" +msgid "Unload\\Unload Mod" +msgstr "" + +msgid "Unloaded Mods:" +msgstr "" + msgid "Up (\\key gup;)" msgstr "Góra (\\key gup;)" @@ -1705,6 +1723,9 @@ msgstr "Szperacz latający" msgid "Withdraw shield (\\key action;)" msgstr "Wyłącz osłonę (\\key action;)" +msgid "Workshop\\Open Workshop to search Mods" +msgstr "" + msgid "Worm" msgstr "Robal" diff --git a/po/ru.po b/po/ru.po index 0763e742..21266749 100644 --- a/po/ru.po +++ b/po/ru.po @@ -825,12 +825,18 @@ msgstr "Список сохраненных миссий" msgid "Load a saved mission" msgstr "Загрузить" +msgid "Load\\Load Mod" +msgstr "" + msgid "Load\\Load a saved mission" msgstr "Загрузить\\Загрузить сохраненную миссию" msgid "Load\\Loads the selected mission" msgstr "Загрузить\\Загрузить выбранную миссию" +msgid "Loaded Mods:" +msgstr "" + msgid "Loading basic level settings" msgstr "Загрузка основных настроек уровня" @@ -899,6 +905,9 @@ msgstr "Миссии на этой планете:" msgid "Missions\\Select mission" msgstr "Миссии\\Выбор миссии" +msgid "Mods\\Manage installed mods" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Инверсия мыши по оси X\\Инверсия прокрутки по оси Х" @@ -1063,6 +1072,9 @@ msgstr "Открыть" msgid "Open (Ctrl+O)" msgstr "Открыть (Ctrl+O)" +msgid "Open Directory\\Open Mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Открывающая скобка отсутствует" @@ -1670,6 +1682,12 @@ msgstr "" msgid "Unknown function" msgstr "Неизвестная функция" +msgid "Unload\\Unload Mod" +msgstr "" + +msgid "Unloaded Mods:" +msgstr "" + msgid "Up (\\key gup;)" msgstr "Вверх (\\key gup;)" @@ -1736,6 +1754,9 @@ msgstr "Летающий искатель" msgid "Withdraw shield (\\key action;)" msgstr "Снять щит (\\key action;)" +msgid "Workshop\\Open Workshop to search Mods" +msgstr "" + msgid "Worm" msgstr "Червь" From 50c3c45ef8d536c594cbe5fa65b615ea2397de47 Mon Sep 17 00:00:00 2001 From: DavivaD Date: Sat, 27 Jul 2019 17:58:12 +0200 Subject: [PATCH 06/29] Show only local mods on the list --- src/ui/screen/screen_setup_mods.cpp | 39 ++++++++++++++++++----------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp index d3266e2d..3779ee32 100644 --- a/src/ui/screen/screen_setup_mods.cpp +++ b/src/ui/screen/screen_setup_mods.cpp @@ -41,6 +41,9 @@ #include #include +#include + +using namespace boost::filesystem; namespace Ui { @@ -224,10 +227,11 @@ void CScreenSetupMods::LoadMod(std::string modName) void CScreenSetupMods::UpdateUnloadedModList() { - CWindow* pw; - CList* pl; - int i = 0; - std::string modName; + CWindow* pw; + CList* pl; + int i = 0; + std::string modPath, modPathRaw; + directory_iterator end_itr; pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; @@ -236,12 +240,13 @@ void CScreenSetupMods::UpdateUnloadedModList() if ( pl == nullptr ) return; pl->Flush(); - auto modsDir = CResourceManager::ListDirectories("mods/"); - std::sort(modsDir.begin(), modsDir.end()); + modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; + modPath = modPathRaw.c_str(); - for(auto const& modNameRaw : modsDir) + for (directory_iterator itr(modPath); itr != end_itr; ++itr) { - modName = modNameRaw; + std::string modName = itr->path().string(); + boost::erase_all(modName, modPath); std::string::size_type enabled; enabled = modName.find('~'); if (enabled != std::string::npos) @@ -250,13 +255,16 @@ void CScreenSetupMods::UpdateUnloadedModList() pl->SetItemName(i++, modName); } } + pl->ShowSelect(false); // shows the selected columns } void CScreenSetupMods::UpdateLoadedModList() { - CWindow* pw; - CList* pl; - int i = 0; + CWindow* pw; + CList* pl; + int i = 0; + std::string modPath, modPathRaw; + directory_iterator end_itr; pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; @@ -265,16 +273,19 @@ void CScreenSetupMods::UpdateLoadedModList() if ( pl == nullptr ) return; pl->Flush(); - auto modsDir = CResourceManager::ListDirectories("mods/"); - std::sort(modsDir.begin(), modsDir.end()); + modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; + modPath = modPathRaw.c_str(); - for(auto const &modName : modsDir) + for (directory_iterator itr(modPath); itr != end_itr; ++itr) { + std::string modName = itr->path().string(); + boost::erase_all(modName, modPath); std::string::size_type enabled; enabled = modName.find('~'); if (enabled == std::string::npos) pl->SetItemName(i++, modName); } + pl->ShowSelect(false); // shows the selected columns } } // namespace Ui From 5f76722ecb7bba6c2739f2498b76f87406a853c3 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Fri, 17 Jul 2020 17:27:09 +0200 Subject: [PATCH 07/29] Add error dialogs for open path/url buttons --- po/colobot.pot | 14 +++++ po/cs.po | 14 +++++ po/de.po | 14 +++++ po/fr.po | 14 +++++ po/pl.po | 14 +++++ po/pt.po | 35 ++++++++++++ po/ru.po | 14 +++++ src/common/restext.cpp | 4 ++ src/common/restext.h | 4 ++ src/common/system/system.cpp | 8 +-- src/common/system/system.h | 6 ++- src/common/system/system_linux.cpp | 24 ++++----- src/common/system/system_linux.h | 4 +- src/common/system/system_macosx.cpp | 24 ++++----- src/common/system/system_macosx.h | 4 +- src/common/system/system_other.cpp | 10 ---- src/common/system/system_other.h | 3 -- src/common/system/system_windows.cpp | 25 ++++----- src/common/system/system_windows.h | 4 +- src/ui/mainui.cpp | 2 +- src/ui/screen/screen_setup_mods.cpp | 79 ++++++++++++++++++---------- src/ui/screen/screen_setup_mods.h | 5 +- 22 files changed, 233 insertions(+), 92 deletions(-) diff --git a/po/colobot.pot b/po/colobot.pot index 262b251a..a6b61ea3 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -172,6 +172,20 @@ msgstr "" msgid "This menu is for userlevels from mods, but you didn't install any" msgstr "" +msgid "Could not open the file explorer!" +msgstr "" + +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "Keyword help(\\key cbot;)" msgstr "" diff --git a/po/cs.po b/po/cs.po index 0d4d482b..7fff13fe 100644 --- a/po/cs.po +++ b/po/cs.po @@ -440,6 +440,12 @@ msgstr "Kopírovat" msgid "Copy (Ctrl+C)" msgstr "Kopírovat (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Současná mise uložena" @@ -1555,6 +1561,10 @@ msgstr "Filtrování textur\\Filtrování textur" msgid "Textures" msgstr "Textury" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "Souboj skončil" @@ -1567,6 +1577,10 @@ msgstr "Funkce nevrátila žádnou hodnotu" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "Mise ještě nebyla splněna (pro podrobnosti stiskněte \\key help;)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Operaci nelze provést s operandy těchto dvou typů" diff --git a/po/de.po b/po/de.po index b03c22ab..702bab19 100644 --- a/po/de.po +++ b/po/de.po @@ -441,6 +441,12 @@ msgstr "Kopieren" msgid "Copy (Ctrl+C)" msgstr "Kopieren (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Mission gespeichert" @@ -1572,6 +1578,10 @@ msgstr "Texturfilterung\\Texturfilterung" msgid "Textures" msgstr "Texturen" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "" @@ -1584,6 +1594,10 @@ msgstr "Die Funktion hat kein Ergebnis zurückgegeben" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "Mission noch nicht beendet (Drücken Sie auf \\key help; für weitere Informationen)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Die zwei Operanden sind nicht kompatibel" diff --git a/po/fr.po b/po/fr.po index 6c49754a..dcb5d42e 100644 --- a/po/fr.po +++ b/po/fr.po @@ -443,6 +443,12 @@ msgstr "Copier" msgid "Copy (Ctrl+C)" msgstr "Copier (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Enregistrement effectué" @@ -1574,6 +1580,10 @@ msgstr "Filtrage de textures\\Filtrage de textures" msgid "Textures" msgstr "Textures" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "La bataille est terminée" @@ -1586,6 +1596,10 @@ msgstr "La fonction n'a pas retourné de résultat" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "La mission n'est pas terminée (appuyez sur \\key help; pour plus de détails)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Les deux opérandes ne sont pas de types compatibles" diff --git a/po/pl.po b/po/pl.po index 7c90d67f..9535ab56 100644 --- a/po/pl.po +++ b/po/pl.po @@ -439,6 +439,12 @@ msgstr "Kopiuj" msgid "Copy (Ctrl+C)" msgstr "Kopiuj (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "Nie udało się otworzyć przeglądarki plików!" + +msgid "Could not open the web browser!" +msgstr "Nie udało się otworzyć przeglądarki internetowej!" + msgid "Current mission saved" msgstr "Bieżąca misja zapisana" @@ -1554,6 +1560,10 @@ msgstr "Filtrowanie tekstur\\Filtrowanie tekstur" msgid "Textures" msgstr "Tekstury" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "Nie udało się otworzyć adresu %s w przeglądarce internetowej." + msgid "The battle has ended" msgstr "Bitwa zakończyła się" @@ -1566,6 +1576,10 @@ msgstr "Funkcja nie zwróciła żadnej wartości" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "Misja nie jest wypełniona (naciśnij \\key help; aby uzyskać szczegóły)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "Nie udało się otworzyć ścieżki %s w przeglądarce plików." + msgid "The types of the two operands are incompatible" msgstr "Niezgodne typy operatorów" diff --git a/po/pt.po b/po/pt.po index 9baba2a9..159dd313 100644 --- a/po/pt.po +++ b/po/pt.po @@ -438,6 +438,12 @@ msgstr "Copiar" msgid "Copy (Ctrl+C)" msgstr "Copiar (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Missão atual salva" @@ -843,12 +849,18 @@ msgstr "Lista das missões salvas" msgid "Load a saved mission" msgstr "Carregar uma missão salva" +msgid "Load\\Load Mod" +msgstr "" + msgid "Load\\Load a saved mission" msgstr "Carregar\\Carregar uma missão salva" msgid "Load\\Loads the selected mission" msgstr "Carregar\\Carrega a missão selecionada" +msgid "Loaded Mods:" +msgstr "" + msgid "Loading basic level settings" msgstr "Carregando configurações de nível básico" @@ -917,6 +929,9 @@ msgstr "Lista de missões neste planeta:" msgid "Missions\\Select mission" msgstr "Missões\\Selecione uma missão" +msgid "Mods\\Manage installed mods" +msgstr "" + msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Inversão de mouse X\\Inverte a direção da rolagem no eixo X" @@ -1085,6 +1100,9 @@ msgstr "Abrir" msgid "Open (Ctrl+O)" msgstr "Abrir (Ctrl+O)" +msgid "Open Directory\\Open Mods directory" +msgstr "" + msgid "Opening brace missing" msgstr "Chave de abertura ausente" @@ -1557,6 +1575,10 @@ msgstr "Filtragem de textura\\Filtragem de textura" msgid "Textures" msgstr "Texturas" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "A batalha acabou" @@ -1569,6 +1591,10 @@ msgstr "A função não retornou nenhum valor" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "A missão não foi completada ainda (pressione \\key help; para mais detalhes)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Os tipos dos dois operandos são incompativeis" @@ -1705,6 +1731,12 @@ msgstr "Sequência de escape desconhecidade" msgid "Unknown function" msgstr "Função desconhecida" +msgid "Unload\\Unload Mod" +msgstr "" + +msgid "Unloaded Mods:" +msgstr "" + msgid "Up (\\key gup;)" msgstr "Cima (\\key gup;)" @@ -1780,6 +1812,9 @@ msgstr "Farejador alado" msgid "Withdraw shield (\\key action;)" msgstr "Retirar escudo (\\key action;)" +msgid "Workshop\\Open Workshop to search Mods" +msgstr "" + msgid "Worm" msgstr "Verme" diff --git a/po/ru.po b/po/ru.po index c062a50b..04d9187a 100644 --- a/po/ru.po +++ b/po/ru.po @@ -446,6 +446,12 @@ msgstr "Копировать" msgid "Copy (Ctrl+C)" msgstr "Копировать (Ctrl+C)" +msgid "Could not open the file explorer!" +msgstr "" + +msgid "Could not open the web browser!" +msgstr "" + msgid "Current mission saved" msgstr "Текущая миссия сохранена" @@ -1585,6 +1591,10 @@ msgstr "Фильтрация текстур\\Фильтрация текстур msgid "Textures" msgstr "Текстуры" +#, c-format +msgid "The address %s could not be opened in a web browser." +msgstr "" + msgid "The battle has ended" msgstr "" @@ -1597,6 +1607,10 @@ msgstr "Функция не возвратила значения" msgid "The mission is not accomplished yet (press \\key help; for more details)" msgstr "Миссия еще не выполнена (нажмите \\key help; для более подробной информации)" +#, c-format +msgid "The path %s could not be opened in a file explorer." +msgstr "" + msgid "The types of the two operands are incompatible" msgstr "Типы операндов несовместимы" diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 74fb31ed..11270e46 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -111,6 +111,10 @@ void InitializeRestext() stringsText[RT_DIALOG_OK] = TR("OK"); stringsText[RT_DIALOG_NOUSRLVL_TITLE] = TR("No userlevels installed!"); stringsText[RT_DIALOG_NOUSRLVL_TEXT] = TR("This menu is for userlevels from mods, but you didn't install any"); + stringsText[RT_DIALOG_OPEN_PATH_FAILED_TITLE] = TR("Could not open the file explorer!"); + stringsText[RT_DIALOG_OPEN_PATH_FAILED_TEXT] = TR("The path %s could not be opened in a file explorer."); + stringsText[RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE] = TR("Could not open the web browser!"); + stringsText[RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT] = TR("The address %s could not be opened in a web browser."); stringsText[RT_STUDIO_LISTTT] = TR("Keyword help(\\key cbot;)"); stringsText[RT_STUDIO_COMPOK] = TR("Compilation ok (0 errors)"); diff --git a/src/common/restext.h b/src/common/restext.h index 824481d5..9edf4105 100644 --- a/src/common/restext.h +++ b/src/common/restext.h @@ -105,6 +105,10 @@ enum ResTextType RT_DIALOG_OK = 110, RT_DIALOG_NOUSRLVL_TITLE = 111, RT_DIALOG_NOUSRLVL_TEXT = 112, + RT_DIALOG_OPEN_PATH_FAILED_TITLE = 113, + RT_DIALOG_OPEN_PATH_FAILED_TEXT = 114, + RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE = 115, + RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT = 116, RT_STUDIO_LISTTT = 120, RT_STUDIO_COMPOK = 121, diff --git a/src/common/system/system.cpp b/src/common/system/system.cpp index f1ede192..b4893655 100644 --- a/src/common/system/system.cpp +++ b/src/common/system/system.cpp @@ -216,12 +216,12 @@ std::string CSystemUtils::GetEnvVar(const std::string& name) return ""; } -void CSystemUtils::OpenPath(std::string path) +bool CSystemUtils::OpenPath(const std::string& path) { - assert(false); + return false; } -void CSystemUtils::OpenWebsite(std::string website) +bool CSystemUtils::OpenWebsite(const std::string& url) { - assert(false); + return false; } diff --git a/src/common/system/system.h b/src/common/system/system.h index a5e01245..f20ee443 100644 --- a/src/common/system/system.h +++ b/src/common/system/system.h @@ -146,10 +146,12 @@ public: virtual std::string GetEnvVar(const std::string &name); //! Opens a path with default file browser - virtual void OpenPath(std::string path); + /** \returns true if successful */ + virtual bool OpenPath(const std::string& path); //! Opens a website with default web browser - virtual void OpenWebsite(std::string website); + /** \returns true if successful */ + virtual bool OpenWebsite(const std::string& url); //! Sleep for given amount of microseconds virtual void Usleep(int usecs) = 0; diff --git a/src/common/system/system_linux.cpp b/src/common/system/system_linux.cpp index a531e45e..2b7d99fa 100644 --- a/src/common/system/system_linux.cpp +++ b/src/common/system/system_linux.cpp @@ -150,26 +150,26 @@ std::string CSystemUtilsLinux::GetEnvVar(const std::string& name) return ""; } -void CSystemUtilsLinux::OpenPath(std::string path) +bool CSystemUtilsLinux::OpenPath(const std::string& path) { - int result; - - result = system(("xdg-open \""+path+"\"").c_str()); - if (result == -1) + int result = system(("xdg-open \"" + path + "\"").c_str()); + if (result != 0) { - GetLogger()->Error("Failed to open path: %s\n", path.c_str()); + GetLogger()->Error("Failed to open path: %s, error code: %i\n", path.c_str(), result); + return false; } + return true; } -void CSystemUtilsLinux::OpenWebsite(std::string website) +bool CSystemUtilsLinux::OpenWebsite(const std::string& url) { - int result; - - result = system(("xdg-open \""+website+"\"").c_str()); - if (result == -1) + int result = system(("xdg-open \"" + url + "\"").c_str()); + if (result != 0) { - GetLogger()->Error("Failed to open website: %s\n", website.c_str()); + GetLogger()->Error("Failed to open website: %s, error code: %i\n", url.c_str(), result); + return false; } + return true; } void CSystemUtilsLinux::Usleep(int usec) diff --git a/src/common/system/system_linux.h b/src/common/system/system_linux.h index e14da475..6607b150 100644 --- a/src/common/system/system_linux.h +++ b/src/common/system/system_linux.h @@ -48,8 +48,8 @@ public: std::string GetEnvVar(const std::string& name) override; - void OpenPath(std::string path) override; - void OpenWebsite(std::string website) override; + bool OpenPath(const std::string& path) override; + bool OpenWebsite(const std::string& url) override; void Usleep(int usec) override; diff --git a/src/common/system/system_macosx.cpp b/src/common/system/system_macosx.cpp index 761a68ea..d65fb6dc 100644 --- a/src/common/system/system_macosx.cpp +++ b/src/common/system/system_macosx.cpp @@ -119,26 +119,26 @@ std::string CSystemUtilsMacOSX::GetEnvVar(const std::string& str) return std::string(); } -void CSystemUtilsLinux::OpenPath(std::string path) +bool CSystemUtilsMacOSX::OpenPath(const std::string& path) { - int result; - - result = system(("open \""+path+"\"").c_str()); // TODO: Test on macOS - if (result == -1) + int result = system(("open \"" + path + "\"").c_str()); // TODO: Test on macOS + if (result != 0) { - GetLogger()->Error("Failed to open path: %s\n", path.c_str()); + GetLogger()->Error("Failed to open path: %s, error code: %i\n", path.c_str(), result); + return false; } + return true; } -void CSystemUtilsLinux::OpenWebsite(std::string website) +bool CSystemUtilsMacOSX::OpenWebsite(const std::string& url) { - int result; - - result = system(("open \""+website+"\"").c_str()); // TODO: Test on macOS - if (result == -1) + int result = system(("open \"" + url + "\"").c_str()); // TODO: Test on macOS + if (result != 0) { - GetLogger()->Error("Failed to open website: %s\n", website.c_str()); + GetLogger()->Error("Failed to open website: %s, error code: %i\n", website.c_str(), result); + return false; } + return true; } void CSystemUtilsMacOSX::Usleep(int usec) diff --git a/src/common/system/system_macosx.h b/src/common/system/system_macosx.h index 08b7b91e..523e52e8 100644 --- a/src/common/system/system_macosx.h +++ b/src/common/system/system_macosx.h @@ -38,8 +38,8 @@ public: std::string GetEnvVar(const std::string& name) override; - void OpenPath(std::string path) override; - void OpenWebsite(std::string website) override; + bool OpenPath(const std::string& path) override; + bool OpenWebsite(const std::string& url) override; void Usleep(int usec) override; diff --git a/src/common/system/system_other.cpp b/src/common/system/system_other.cpp index 549c9af1..865db847 100644 --- a/src/common/system/system_other.cpp +++ b/src/common/system/system_other.cpp @@ -44,16 +44,6 @@ long long int CSystemUtilsOther::TimeStampExactDiff(SystemTimeStamp* before, Sys return (after->sdlTicks - before->sdlTicks) * 1000000ll; } -void CSystemUtilsOther::OpenPath(std::string path) -{ - // TODO -} - -void CSystemUtilsOther::OpenWebsite(std::string website) -{ - // TODO -} - void CSystemUtilsOther::Usleep(int usec) { SDL_Delay(usec / 1000); // close enough diff --git a/src/common/system/system_other.h b/src/common/system/system_other.h index c27d7d52..5964b585 100644 --- a/src/common/system/system_other.h +++ b/src/common/system/system_other.h @@ -50,9 +50,6 @@ public: void GetCurrentTimeStamp(SystemTimeStamp *stamp) override; long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after) override; - void OpenPath(std::string path) override; - void OpenWebsite(std::string website) override; - void Usleep(int usec) override; }; diff --git a/src/common/system/system_windows.cpp b/src/common/system/system_windows.cpp index 21a77aa9..989d359b 100644 --- a/src/common/system/system_windows.cpp +++ b/src/common/system/system_windows.cpp @@ -21,6 +21,7 @@ #include "common/logger.h" +#include #include @@ -152,26 +153,26 @@ std::string CSystemUtilsWindows::GetEnvVar(const std::string& name) } } -void CSystemUtilsWindows::OpenPath(std::string path) +bool CSystemUtilsWindows::OpenPath(const std::string& path) { - int result; - - result = system(("explorer \""+path+"\"").c_str()); // TODO: Test on macOS - if (result == -1) + int result = system(("start explorer \"" + boost::filesystem::path(path).make_preferred().string() + "\"").c_str()); + if (result != 0) { - GetLogger()->Error("Failed to open path: %s\n", path.c_str()); + GetLogger()->Error("Failed to open path: %s, error code: %i\n", path.c_str(), result); + return false; } + return true; } -void CSystemUtilsWindows::OpenWebsite(std::string website) +bool CSystemUtilsWindows::OpenWebsite(const std::string& url) { - int result; - - result = system(("rundll32 url.dll,FileProtocolHandler \""+website+"\"").c_str()); // TODO: Test on macOS - if (result == -1) + int result = system(("rundll32 url.dll,FileProtocolHandler \"" + url + "\"").c_str()); + if (result != 0) { - GetLogger()->Error("Failed to open website: %s\n", website.c_str()); + GetLogger()->Error("Failed to open website: %s, error code: %i\n", url.c_str(), result); + return false; } + return true; } void CSystemUtilsWindows::Usleep(int usec) diff --git a/src/common/system/system_windows.h b/src/common/system/system_windows.h index e3b98f20..90ef6b91 100644 --- a/src/common/system/system_windows.h +++ b/src/common/system/system_windows.h @@ -46,8 +46,8 @@ public: std::string GetEnvVar(const std::string& name) override; - void OpenPath(std::string path) override; - void OpenWebsite(std::string website) override; + bool OpenPath(const std::string& path) override; + bool OpenWebsite(const std::string& url) override; void Usleep(int usec) override; diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp index ab737e12..14fc454d 100644 --- a/src/ui/mainui.cpp +++ b/src/ui/mainui.cpp @@ -85,7 +85,7 @@ CMainUserInterface::CMainUserInterface() m_screenSetupDisplay = MakeUnique(); m_screenSetupGame = MakeUnique(); m_screenSetupGraphics = MakeUnique(); - m_screenSetupMods = MakeUnique(); + m_screenSetupMods = MakeUnique(m_dialog.get()); m_screenSetupSound = MakeUnique(); m_screenMainMenu = MakeUnique(); m_screenPlayerSelect = MakeUnique(m_dialog.get()); diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp index 3779ee32..9415ad24 100644 --- a/src/ui/screen/screen_setup_mods.cpp +++ b/src/ui/screen/screen_setup_mods.cpp @@ -28,6 +28,7 @@ #include "common/config.h" #include "common/logger.h" #include "common/settings.h" +#include "common/stringutils.h" #include "common/resources/resourcemanager.h" #include "level/parser/parser.h" @@ -48,7 +49,8 @@ using namespace boost::filesystem; namespace Ui { -CScreenSetupMods::CScreenSetupMods() +CScreenSetupMods::CScreenSetupMods(CMainDialog* mainDialog) + : m_dialog(mainDialog) { } @@ -131,7 +133,9 @@ bool CScreenSetupMods::EventProcess(const Event &event) CWindow* pw; CButton* pb; CList* pl; - std::string modName, modPath, website = "https://www.moddb.com/games/colobot-gold-edition"; + std::string modName; + const std::string website = "https://www.moddb.com/games/colobot-gold-edition"; + const std::string modDir = CResourceManager::GetSaveLocation() + "/" + "mods"; auto systemUtils = CSystemUtils::Create(); // platform-specific utils if (!CScreenSetup::EventProcess(event)) return false; @@ -139,33 +143,28 @@ bool CScreenSetupMods::EventProcess(const Event &event) pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return false; - if (event.type == EVENT_INTERFACE_LOAD) - { - pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); - if ( pl == nullptr ) return false; - modName = pl->GetItemName(pl->GetSelect()); - LoadMod(modName); - - m_app->Reload(); - m_main->ChangePhase(PHASE_SETUPm); - } - if (event.type == EVENT_INTERFACE_UNLOAD) - { - pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); - if ( pl == nullptr ) return false; - modName = pl->GetItemName(pl->GetSelect()); - UnloadMod(modName); - - m_app->Reload(); - m_main->ChangePhase(PHASE_SETUPm); - } - if (event.type == EVENT_INTERFACE_MODS_DIR) - { - modPath = CResourceManager::GetSaveLocation() + "/" + "mods"; - systemUtils->OpenPath(modPath); - } switch (event.type) { + case EVENT_INTERFACE_LOAD: + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); + if (pl == nullptr) return false; + modName = pl->GetItemName(pl->GetSelect()); + LoadMod(modName); + + m_app->Reload(); + m_main->ChangePhase(PHASE_SETUPm); + break; + + case EVENT_INTERFACE_UNLOAD: + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); + if (pl == nullptr) return false; + modName = pl->GetItemName(pl->GetSelect()); + UnloadMod(modName); + + m_app->Reload(); + m_main->ChangePhase(PHASE_SETUPm); + break; + case EVENT_INTERFACE_MODS_UNLOADED: pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); if ( pl == nullptr ) break; @@ -194,9 +193,31 @@ bool CScreenSetupMods::EventProcess(const Event &event) pb->SetState(STATE_ENABLE); break; - case EVENT_INTERFACE_WORKSHOP: - systemUtils->OpenWebsite(website); + case EVENT_INTERFACE_MODS_DIR: + if (!systemUtils->OpenPath(modDir)) + { + std::string title, text; + GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TITLE, title); + GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TEXT, text); + + // Workaround for how labels treat the \\ character on Windows + std::string modDirWithoutBackSlashes = modDir; + std::replace(modDirWithoutBackSlashes.begin(), modDirWithoutBackSlashes.end(), '\\', '/'); + + m_dialog->StartInformation(title, title, StrUtils::Format(text.c_str(), modDirWithoutBackSlashes.c_str())); + } break; + + case EVENT_INTERFACE_WORKSHOP: + if (!systemUtils->OpenWebsite(website)) + { + std::string title, text; + GetResource(RES_TEXT, RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE, title); + GetResource(RES_TEXT, RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT, text); + m_dialog->StartInformation(title, title, StrUtils::Format(text.c_str(), website.c_str())); + } + break; + default: return true; } diff --git a/src/ui/screen/screen_setup_mods.h b/src/ui/screen/screen_setup_mods.h index e61d75bc..75b8c5a1 100644 --- a/src/ui/screen/screen_setup_mods.h +++ b/src/ui/screen/screen_setup_mods.h @@ -19,6 +19,7 @@ #pragma once +#include "ui/maindialog.h" #include "ui/screen/screen_setup.h" #include @@ -31,7 +32,7 @@ namespace Ui class CScreenSetupMods : public CScreenSetup { public: - CScreenSetupMods(); + CScreenSetupMods(CMainDialog* mainDialog); void SetActive() override; void CreateInterface() override; @@ -44,6 +45,8 @@ protected: void UpdateLoadedModList(); protected: + CMainDialog* m_dialog; + CPathManager* m_pathManager; std::vector m_unloadedModList; std::vector m_loadedModList; From 8390d85e4651cb599250436ed114ca3d11425f6a Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Sat, 18 Jul 2020 14:30:50 +0200 Subject: [PATCH 08/29] Refactor the mod manager Moved list of mods logic to a new CModManager class. The list of enabled mods is now managed by a flag instead of directory names of mods. Mods are now disabled by default. Also general cleanup, fixing issues from the code review in https://github.com/colobot/colobot/pull/1191 and fixing linter issues. Regression: the state of enabled/disabled mods is now not persistent. The plan is to use some kind of config file for this. --- src/CMakeLists.txt | 2 + src/app/app.cpp | 62 ++++++------ src/app/app.h | 12 ++- src/app/modman.cpp | 117 +++++++++++++++++++++++ src/app/modman.h | 75 +++++++++++++++ src/app/pathman.cpp | 99 +++++++------------ src/app/pathman.h | 12 +-- src/common/resources/resourcemanager.cpp | 6 ++ src/common/resources/resourcemanager.h | 2 + src/graphics/engine/text.cpp | 6 +- src/ui/mainui.cpp | 2 +- src/ui/screen/screen_setup_mods.cpp | 87 ++++++----------- src/ui/screen/screen_setup_mods.h | 15 ++- 13 files changed, 320 insertions(+), 177 deletions(-) create mode 100644 src/app/modman.cpp create mode 100644 src/app/modman.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2fa18acc..f70489ab 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,6 +145,8 @@ set(BASE_SOURCES app/controller.h app/input.cpp app/input.h + app/modman.cpp + app/modman.h app/pathman.cpp app/pathman.h app/pausemanager.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index d0cb9692..2bb6a805 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -21,6 +21,7 @@ #include "app/controller.h" #include "app/input.h" +#include "app/modman.h" #include "app/pathman.h" #include "common/config_file.h" @@ -113,7 +114,8 @@ CApplication::CApplication(CSystemUtils* systemUtils) m_private(MakeUnique()), m_configFile(MakeUnique()), m_input(MakeUnique()), - m_pathManager(MakeUnique(systemUtils)) + m_pathManager(MakeUnique(systemUtils)), + m_modManager(MakeUnique(this, m_pathManager.get())) { m_exitCode = 0; m_active = false; @@ -220,6 +222,11 @@ CSoundInterface* CApplication::GetSound() return m_sound.get(); } +CModManager* CApplication::GetModManager() +{ + return m_modManager.get(); +} + void CApplication::LoadEnvironmentVariables() { auto dataDir = m_systemUtils->GetEnvVar("COLOBOT_DATA_DIR"); @@ -513,6 +520,8 @@ bool CApplication::Create() GetLogger()->Warn("Config could not be loaded. Default values will be used!\n"); } + m_modManager->ReinitMods(); + // Create the sound instance. #ifdef OPENAL_SOUND if (!m_headless) @@ -698,21 +707,7 @@ bool CApplication::Create() // Create the robot application. m_controller = MakeUnique(); - CThread musicLoadThread([this]() - { - GetLogger()->Debug("Cache sounds...\n"); - SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); - m_systemUtils->GetCurrentTimeStamp(musicLoadStart); - - m_sound->CacheAll(); - - SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp(); - m_systemUtils->GetCurrentTimeStamp(musicLoadEnd); - float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC); - GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime); - }, - "Sound loading thread"); - musicLoadThread.Start(); + StartLoadingMusic(); if (m_runSceneCategory == LevelCategory::Max) m_controller->StartApp(); @@ -726,22 +721,11 @@ bool CApplication::Create() return true; } -void CApplication::Reload() +void CApplication::ReloadResources() { - m_sound->Create(); + GetLogger()->Info("Reloading resources\n"); m_engine->ReloadAllTextures(); - CThread musicLoadThread([this]() - { - SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); - m_systemUtils->GetCurrentTimeStamp(musicLoadStart); - m_sound->CacheAll(); - SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp(); - m_systemUtils->GetCurrentTimeStamp(musicLoadEnd); - float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC); - GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime); - }, - "Sound loading thread"); - musicLoadThread.Start(); + StartLoadingMusic(); m_controller->GetRobotMain()->UpdateCustomLevelList(); } @@ -1559,6 +1543,24 @@ void CApplication::InternalResumeSimulation() m_absTimeBase = m_exactAbsTime; } +void CApplication::StartLoadingMusic() +{ + CThread musicLoadThread([this]() + { + GetLogger()->Debug("Cache sounds...\n"); + SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); + m_systemUtils->GetCurrentTimeStamp(musicLoadStart); + + m_sound->CacheAll(); + + SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp(); + m_systemUtils->GetCurrentTimeStamp(musicLoadEnd); + float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC); + GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime); + }, "Sound loading thread"); + musicLoadThread.Start(); +} + bool CApplication::GetSimulationSuspended() const { return m_simulationSuspended; diff --git a/src/app/app.h b/src/app/app.h index c45e6320..af385cf2 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -42,6 +42,7 @@ class CEventQueue; class CController; class CSoundInterface; class CInput; +class CModManager; class CPathManager; class CConfigFile; class CSystemUtils; @@ -162,6 +163,8 @@ public: CEventQueue* GetEventQueue(); //! Returns the sound subsystem CSoundInterface* GetSound(); + //! Returns the mod manager + CModManager* GetModManager(); public: //! Loads some data from environment variables @@ -170,8 +173,8 @@ public: ParseArgsStatus ParseArguments(int argc, char *argv[]); //! Initializes the application bool Create(); - //! Reloads the application - void Reload(); + //! Reloads the application resources, e.g. mods + void ReloadResources(); //! Main event loop int Run(); //! Returns the code to be returned at main() exit @@ -303,6 +306,9 @@ protected: //! Internal procedure to reset time counters void InternalResumeSimulation(); + //! Loads music in a new thread + void StartLoadingMusic(); + protected: //! System utils instance CSystemUtils* m_systemUtils; @@ -324,6 +330,8 @@ protected: std::unique_ptr m_input; //! Path manager std::unique_ptr m_pathManager; + //! Mod manager + std::unique_ptr m_modManager; //! Code to return at exit int m_exitCode; diff --git a/src/app/modman.cpp b/src/app/modman.cpp new file mode 100644 index 00000000..e86edb5f --- /dev/null +++ b/src/app/modman.cpp @@ -0,0 +1,117 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#include "modman.h" + +//TODO: clean up includes +#include "common/config.h" + +#include "app/app.h" +#include "app/pathman.h" + +#include "common/restext.h" +#include "common/logger.h" +#include "common/settings.h" +#include "common/stringutils.h" + +#include "common/resources/resourcemanager.h" + +#include "common/system/system.h" + +#include +#include +#include + +using namespace boost::filesystem; + +CModManager::CModManager(CApplication* app, CPathManager* pathManager) + : m_app{app}, + m_pathManager{pathManager} +{ +} + +void CModManager::ReinitMods() +{ + m_mods.clear(); + const auto foundMods = m_pathManager->FindMods(); + for (const auto& modPath : foundMods) + { + Mod mod; + mod.name = boost::filesystem::path(modPath).stem().string(); + mod.path = modPath; + mod.enabled = m_pathManager->ModLoaded(mod.path); //TODO: load from some config file + m_mods.push_back(mod); + } +} + +void CModManager::EnableMod(const std::string& modName) +{ + Mod* mod = FindMod(modName); + if (!mod) + { + GetLogger()->Error("Could not enable mod: %s not found\n", modName.c_str()); + return; + } + mod->enabled = true; +} + +void CModManager::DisableMod(const std::string& modName) +{ + Mod* mod = FindMod(modName); + if (!mod) + { + GetLogger()->Error("Could not disable mod: %s not found\n", modName.c_str()); + return; + } + mod->enabled = false; +} + +void CModManager::ReloadMods() +{ + for (const auto& mod : m_mods) + { + bool loaded = m_pathManager->ModLoaded(mod.path); + if (mod.enabled && !loaded) + { + m_pathManager->AddMod(mod.path); + } + else if (!mod.enabled && loaded) + { + m_pathManager->RemoveMod(mod.path); + } + } + m_app->ReloadResources(); +} + +boost::optional CModManager::GetMod(const std::string& modName) +{ + Mod* mod = FindMod(modName); + return mod != nullptr ? *mod : boost::optional(); +} + +const std::vector& CModManager::GetMods() const +{ + return m_mods; +} + +Mod* CModManager::FindMod(const std::string& modName) +{ + auto it = std::find_if(m_mods.begin(), m_mods.end(), [&](Mod& mod) { return mod.name == modName; }); + return it != m_mods.end() ? &(*it) : nullptr; +} diff --git a/src/app/modman.h b/src/app/modman.h new file mode 100644 index 00000000..e1a47467 --- /dev/null +++ b/src/app/modman.h @@ -0,0 +1,75 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#pragma once + +#include "ui/maindialog.h" + +#include "ui/screen/screen_setup.h" + +#include +#include + +class CPathManager; + +struct Mod +{ + std::string name; + std::string path; + bool enabled = false; + //TODO: add metadata for UI +}; + +/** + * \class CApplication + * \brief Main application + * + * This class handles the list of currently loaded mods. + * + */ +class CModManager +{ +public: + CModManager(CApplication* app, CPathManager* pathManager); + + //! Finds all the mods along with their metadata + void ReinitMods(); + + //! Removes a mod from the list of loaded mods + void EnableMod(const std::string& modName); + + //! Adds a mod to the list of loaded mods + void DisableMod(const std::string& modName); + + //! Reloads application resources so the enabled mods are applied + void ReloadMods(); + + boost::optional GetMod(const std::string& modName); + const std::vector& GetMods() const; + +private: + Mod* FindMod(const std::string& modName); + +private: + CApplication* m_app; + CPathManager* m_pathManager; + + //TODO: better data structure? + std::vector m_mods; +}; diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index d007b0fd..4c4a1a71 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -41,8 +41,7 @@ CPathManager::CPathManager(CSystemUtils* systemUtils) : m_dataPath(systemUtils->GetDataPath()) , m_langPath(systemUtils->GetLangPath()) , m_savePath(systemUtils->GetSaveDir()) - , m_modAutoloadDir{} - , m_mods{} + , m_modSearchDirs{} { } @@ -65,35 +64,41 @@ void CPathManager::SetSavePath(const std::string &savePath) m_savePath = savePath; } -void CPathManager::AddModAutoloadDir(const std::string &modAutoloadDirPath) +void CPathManager::AddModSearchDir(const std::string &modSearchDirPath) { - m_modAutoloadDir.push_back(modAutoloadDirPath); + m_modSearchDirs.push_back(modSearchDirPath); } void CPathManager::AddMod(const std::string &modPath) { - std::string::size_type enabled; - enabled = modPath.find('~'); - if (enabled == std::string::npos) - { - GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); - CResourceManager::AddLocation(modPath, true); - } - else - { - GetLogger()->Info("Found excluded mod: '%s'\n", modPath.c_str()); - } + GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); + CResourceManager::AddLocation(modPath, true); } void CPathManager::RemoveMod(const std::string &modPath) { - std::string::size_type enabled; - enabled = modPath.find('~'); - if (enabled == std::string::npos) + GetLogger()->Info("Unloading mod: '%s'\n", modPath.c_str()); + CResourceManager::RemoveLocation(modPath); +} + +bool CPathManager::ModLoaded(const std::string& modPath) +{ + return CResourceManager::LocationExists(modPath); +} + +std::vector CPathManager::FindMods() const +{ + std::vector mods; + GetLogger()->Info("Found mods:\n"); + for (const auto &searchPath : m_modSearchDirs) { - GetLogger()->Info("Unloading mod: '%s'\n", modPath.c_str()); - CResourceManager::RemoveLocation(modPath); + for (const auto &modPath : FindModsInDir(searchPath)) + { + GetLogger()->Info(" * %s\n", modPath.c_str()); + mods.push_back(modPath); + } } + return mods; } const std::string& CPathManager::GetDataPath() @@ -152,58 +157,18 @@ void CPathManager::InitPaths() GetLogger()->Info("Data path: %s\n", m_dataPath.c_str()); GetLogger()->Info("Save path: %s\n", m_savePath.c_str()); - m_modAutoloadDir.push_back(m_dataPath + "/mods"); - m_modAutoloadDir.push_back(m_savePath + "/mods"); + m_modSearchDirs.push_back(m_dataPath + "/mods"); + m_modSearchDirs.push_back(m_savePath + "/mods"); - if (!m_modAutoloadDir.empty()) + if (!m_modSearchDirs.empty()) { - GetLogger()->Info("Mod autoload dirs:\n"); - for(const std::string& modAutoloadDir : m_modAutoloadDir) - GetLogger()->Info(" * %s\n", modAutoloadDir.c_str()); - } - if (!m_mods.empty()) - { - GetLogger()->Info("Mods:\n"); - for(const std::string& modPath : m_mods) - GetLogger()->Info(" * %s\n", modPath.c_str()); + GetLogger()->Info("Mod search dirs:\n"); + for(const std::string& modSearchDir : m_modSearchDirs) + GetLogger()->Info(" * %s\n", modSearchDir.c_str()); } CResourceManager::AddLocation(m_dataPath); - for (const std::string& modAutoloadDir : m_modAutoloadDir) - { - GetLogger()->Trace("Searching for mods in '%s'...\n", modAutoloadDir.c_str()); - for (const std::string& modPath : FindModsInDir(modAutoloadDir)) - { - std::string::size_type enabled; - enabled = modPath.find('~'); - if (enabled == std::string::npos) - { - GetLogger()->Info("Autoloading mod: '%s'\n", modPath.c_str()); - CResourceManager::AddLocation(modPath); - } - else - { - GetLogger()->Info("Found excluded mod: '%s'\n", modPath.c_str()); - } - } - } - - for (const std::string& modPath : m_mods) - { - std::string::size_type enabled; - enabled = modPath.find('~'); - if (enabled == std::string::npos) - { - GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); - CResourceManager::AddLocation(modPath); - } - else - { - GetLogger()->Info("Found excluded mod: '%s'\n", modPath.c_str()); - } - } - CResourceManager::SetSaveLocation(m_savePath); CResourceManager::AddLocation(m_savePath); @@ -213,7 +178,7 @@ void CPathManager::InitPaths() GetLogger()->Debug(" * %s\n", path.c_str()); } -std::vector CPathManager::FindModsInDir(const std::string &dir) +std::vector CPathManager::FindModsInDir(const std::string &dir) const { std::vector ret; try diff --git a/src/app/pathman.h b/src/app/pathman.h index 9cd9ffe1..3712d307 100644 --- a/src/app/pathman.h +++ b/src/app/pathman.h @@ -37,9 +37,11 @@ public: void SetDataPath(const std::string &dataPath); void SetLangPath(const std::string &langPath); void SetSavePath(const std::string &savePath); - void AddModAutoloadDir(const std::string &modAutoloadDirPath); + void AddModSearchDir(const std::string &modAutoloadDirPath); void AddMod(const std::string &modPath); void RemoveMod(const std::string &modPath); + bool ModLoaded(const std::string& modPath); + std::vector FindMods() const; const std::string& GetDataPath(); const std::string& GetLangPath(); @@ -52,7 +54,7 @@ public: private: //! Loads all mods from given directory - std::vector FindModsInDir(const std::string &dir); + std::vector FindModsInDir(const std::string &dir) const; private: //! Data path @@ -61,8 +63,6 @@ private: std::string m_langPath; //! Save path std::string m_savePath; - //! Mod autoload paths - std::vector m_modAutoloadDir; - //! Mod paths - std::vector m_mods; + //! Mod search paths + std::vector m_modSearchDirs; }; diff --git a/src/common/resources/resourcemanager.cpp b/src/common/resources/resourcemanager.cpp index b51325b8..bddb5236 100644 --- a/src/common/resources/resourcemanager.cpp +++ b/src/common/resources/resourcemanager.cpp @@ -95,6 +95,12 @@ std::vector CResourceManager::GetLocations() return ret; } +bool CResourceManager::LocationExists(const std::string& location) +{ + auto locations = GetLocations(); + auto it = std::find(locations.cbegin(), locations.cend(), location); + return it != locations.cend(); +} bool CResourceManager::SetSaveLocation(const std::string &location) { diff --git a/src/common/resources/resourcemanager.h b/src/common/resources/resourcemanager.h index 8c09b91a..0a1bca16 100644 --- a/src/common/resources/resourcemanager.h +++ b/src/common/resources/resourcemanager.h @@ -41,6 +41,8 @@ public: static bool RemoveLocation(const std::string &location); //! List all locations in the search path static std::vector GetLocations(); + //! Check if given location is in the search path + static bool LocationExists(const std::string &location); static bool SetSaveLocation(const std::string &location); static std::string GetSaveLocation(); diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index 4306dae8..a85bd5fd 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -259,10 +259,8 @@ void CText::FlushCache() } } - m_lastCachedFont = nullptr; - m_lastFontType = FONT_COMMON; - m_lastFontSize = 0; - + //TODO: fix this + Destroy(); Create(); } diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp index 14fc454d..57436f89 100644 --- a/src/ui/mainui.cpp +++ b/src/ui/mainui.cpp @@ -85,7 +85,7 @@ CMainUserInterface::CMainUserInterface() m_screenSetupDisplay = MakeUnique(); m_screenSetupGame = MakeUnique(); m_screenSetupGraphics = MakeUnique(); - m_screenSetupMods = MakeUnique(m_dialog.get()); + m_screenSetupMods = MakeUnique(m_dialog.get(), m_app->GetModManager()); m_screenSetupSound = MakeUnique(); m_screenMainMenu = MakeUnique(); m_screenPlayerSelect = MakeUnique(m_dialog.get()); diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp index 9415ad24..60e8a47f 100644 --- a/src/ui/screen/screen_setup_mods.cpp +++ b/src/ui/screen/screen_setup_mods.cpp @@ -1,6 +1,6 @@ /* * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2019, Daniel Roux, EPSITEC SA & TerranovaTeam + * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsitec.ch; http://colobot.info; http://github.com/colobot * * This program is free software: you can redistribute it and/or modify @@ -19,18 +19,20 @@ #include "ui/screen/screen_setup_mods.h" -#include "app/app.h" -#include "app/pathman.h" +#include "common/config.h" -#include "common/system/system.h" +#include "app/app.h" +#include "app/modman.h" #include "common/restext.h" -#include "common/config.h" #include "common/logger.h" #include "common/settings.h" #include "common/stringutils.h" #include "common/resources/resourcemanager.h" + +#include "common/system/system.h" + #include "level/parser/parser.h" #include "ui/controls/button.h" @@ -49,8 +51,9 @@ using namespace boost::filesystem; namespace Ui { -CScreenSetupMods::CScreenSetupMods(CMainDialog* mainDialog) - : m_dialog(mainDialog) +CScreenSetupMods::CScreenSetupMods(CMainDialog* dialog, CModManager* modManager) + : m_dialog(dialog), + m_modManager(modManager) { } @@ -128,6 +131,7 @@ void CScreenSetupMods::CreateInterface() pb->SetState(STATE_SHADOW); pb->ClearState(STATE_ENABLE); } + bool CScreenSetupMods::EventProcess(const Event &event) { CWindow* pw; @@ -135,7 +139,7 @@ bool CScreenSetupMods::EventProcess(const Event &event) CList* pl; std::string modName; const std::string website = "https://www.moddb.com/games/colobot-gold-edition"; - const std::string modDir = CResourceManager::GetSaveLocation() + "/" + "mods"; + const std::string modDir = CResourceManager::GetSaveLocation() + "/mods"; auto systemUtils = CSystemUtils::Create(); // platform-specific utils if (!CScreenSetup::EventProcess(event)) return false; @@ -149,9 +153,10 @@ bool CScreenSetupMods::EventProcess(const Event &event) pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); if (pl == nullptr) return false; modName = pl->GetItemName(pl->GetSelect()); - LoadMod(modName); - m_app->Reload(); + m_modManager->EnableMod(modName); + m_modManager->ReloadMods(); + m_main->ChangePhase(PHASE_SETUPm); break; @@ -159,9 +164,10 @@ bool CScreenSetupMods::EventProcess(const Event &event) pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); if (pl == nullptr) return false; modName = pl->GetItemName(pl->GetSelect()); - UnloadMod(modName); - m_app->Reload(); + m_modManager->DisableMod(modName); + m_modManager->ReloadMods(); + m_main->ChangePhase(PHASE_SETUPm); break; @@ -200,7 +206,7 @@ bool CScreenSetupMods::EventProcess(const Event &event) GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TITLE, title); GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TEXT, text); - // Workaround for how labels treat the \\ character on Windows + // Workaround for Windows: the label skips everything after the first \\ character std::string modDirWithoutBackSlashes = modDir; std::replace(modDirWithoutBackSlashes.begin(), modDirWithoutBackSlashes.end(), '\\', '/'); @@ -224,34 +230,11 @@ bool CScreenSetupMods::EventProcess(const Event &event) return false; } -void CScreenSetupMods::UnloadMod(std::string modName) -{ - std::string modPath, modPathRaw, disabled = "~"; - - modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; - modPath = modPathRaw.c_str(); - - m_pathManager->RemoveMod(modPath+modName); - boost::filesystem::rename(modPath+modName, modPath+disabled+modName); -} - -void CScreenSetupMods::LoadMod(std::string modName) -{ - std::string modPath, modPathRaw, disabled = "~"; - - modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; - modPath = modPathRaw.c_str(); - - boost::filesystem::rename(modPath+disabled+modName, modPath+modName); - m_pathManager->AddMod(modPath+modName); -} - void CScreenSetupMods::UpdateUnloadedModList() { CWindow* pw; CList* pl; int i = 0; - std::string modPath, modPathRaw; directory_iterator end_itr; pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); @@ -261,30 +244,22 @@ void CScreenSetupMods::UpdateUnloadedModList() if ( pl == nullptr ) return; pl->Flush(); - modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; - modPath = modPathRaw.c_str(); - - for (directory_iterator itr(modPath); itr != end_itr; ++itr) + for (const auto& mod : m_modManager->GetMods()) { - std::string modName = itr->path().string(); - boost::erase_all(modName, modPath); - std::string::size_type enabled; - enabled = modName.find('~'); - if (enabled != std::string::npos) + if (!mod.enabled) { - modName.erase(0,1); - pl->SetItemName(i++, modName); + pl->SetItemName(i++, mod.name); } } pl->ShowSelect(false); // shows the selected columns } + void CScreenSetupMods::UpdateLoadedModList() { CWindow* pw; CList* pl; int i = 0; - std::string modPath, modPathRaw; directory_iterator end_itr; pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); @@ -294,19 +269,15 @@ void CScreenSetupMods::UpdateLoadedModList() if ( pl == nullptr ) return; pl->Flush(); - modPathRaw = CResourceManager::GetSaveLocation() + "/" + "mods" + "/"; - modPath = modPathRaw.c_str(); - - for (directory_iterator itr(modPath); itr != end_itr; ++itr) + for (const auto& mod : m_modManager->GetMods()) { - std::string modName = itr->path().string(); - boost::erase_all(modName, modPath); - std::string::size_type enabled; - enabled = modName.find('~'); - if (enabled == std::string::npos) - pl->SetItemName(i++, modName); + if (mod.enabled) + { + pl->SetItemName(i++, mod.name); + } } pl->ShowSelect(false); // shows the selected columns } + } // namespace Ui diff --git a/src/ui/screen/screen_setup_mods.h b/src/ui/screen/screen_setup_mods.h index 75b8c5a1..fbe2c6eb 100644 --- a/src/ui/screen/screen_setup_mods.h +++ b/src/ui/screen/screen_setup_mods.h @@ -1,6 +1,6 @@ /* * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2019, Daniel Roux, EPSITEC SA & TerranovaTeam + * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsitec.ch; http://colobot.info; http://github.com/colobot * * This program is free software: you can redistribute it and/or modify @@ -20,11 +20,12 @@ #pragma once #include "ui/maindialog.h" + #include "ui/screen/screen_setup.h" #include -class CPathManager; +class CModManager; namespace Ui { @@ -32,24 +33,20 @@ namespace Ui class CScreenSetupMods : public CScreenSetup { public: - CScreenSetupMods(CMainDialog* mainDialog); + CScreenSetupMods(CMainDialog* dialog, CModManager* modManager); void SetActive() override; void CreateInterface() override; bool EventProcess(const Event &event) override; protected: - void UnloadMod(std::string ModName); - void LoadMod(std::string ModName); void UpdateUnloadedModList(); void UpdateLoadedModList(); protected: - CMainDialog* m_dialog; + CModManager* m_modManager; - CPathManager* m_pathManager; - std::vector m_unloadedModList; - std::vector m_loadedModList; + CMainDialog* m_dialog; }; } // namespace Ui From dc64b95406a05d211343f88329c636251d354447 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Sat, 18 Jul 2020 14:47:18 +0200 Subject: [PATCH 09/29] Fix build error --- src/app/pathman.h | 2 +- src/ui/screen/screen_setup_mods.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/pathman.h b/src/app/pathman.h index 3712d307..7726786d 100644 --- a/src/app/pathman.h +++ b/src/app/pathman.h @@ -37,7 +37,7 @@ public: void SetDataPath(const std::string &dataPath); void SetLangPath(const std::string &langPath); void SetSavePath(const std::string &savePath); - void AddModSearchDir(const std::string &modAutoloadDirPath); + void AddModSearchDir(const std::string &modSearchDirPath); void AddMod(const std::string &modPath); void RemoveMod(const std::string &modPath); bool ModLoaded(const std::string& modPath); diff --git a/src/ui/screen/screen_setup_mods.h b/src/ui/screen/screen_setup_mods.h index fbe2c6eb..9098d7d7 100644 --- a/src/ui/screen/screen_setup_mods.h +++ b/src/ui/screen/screen_setup_mods.h @@ -44,9 +44,9 @@ protected: void UpdateLoadedModList(); protected: - CModManager* m_modManager; - CMainDialog* m_dialog; + + CModManager* m_modManager; }; } // namespace Ui From a0635ae400b9a86898e100c7b78312cce5c4dd88 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Sat, 18 Jul 2020 15:01:36 +0200 Subject: [PATCH 10/29] Fix linter issues --- src/app/app.cpp | 3 ++- src/app/modman.cpp | 4 ++-- src/ui/screen/screen_setup_mods.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 2bb6a805..4f73ae16 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -1557,7 +1557,8 @@ void CApplication::StartLoadingMusic() m_systemUtils->GetCurrentTimeStamp(musicLoadEnd); float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC); GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime); - }, "Sound loading thread"); + }, + "Sound loading thread"); musicLoadThread.Start(); } diff --git a/src/app/modman.cpp b/src/app/modman.cpp index e86edb5f..b67853ce 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "modman.h" +#include "app/modman.h" //TODO: clean up includes #include "common/config.h" @@ -25,8 +25,8 @@ #include "app/app.h" #include "app/pathman.h" -#include "common/restext.h" #include "common/logger.h" +#include "common/restext.h" #include "common/settings.h" #include "common/stringutils.h" diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp index 60e8a47f..6fae8151 100644 --- a/src/ui/screen/screen_setup_mods.cpp +++ b/src/ui/screen/screen_setup_mods.cpp @@ -24,8 +24,8 @@ #include "app/app.h" #include "app/modman.h" -#include "common/restext.h" #include "common/logger.h" +#include "common/restext.h" #include "common/settings.h" #include "common/stringutils.h" From 69d2d39c36857a477664e3e94429bf32b4206cb7 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Sat, 18 Jul 2020 18:06:14 +0200 Subject: [PATCH 11/29] Add ability to use arrays in colobot.ini --- src/common/config_file.h | 76 +++++++++++++++++++++++++++ test/unit/common/colobot.ini | 5 ++ test/unit/common/config_file_test.cpp | 22 ++++++++ 3 files changed, 103 insertions(+) diff --git a/src/common/config_file.h b/src/common/config_file.h index 6fbeae71..6e95f481 100644 --- a/src/common/config_file.h +++ b/src/common/config_file.h @@ -26,9 +26,15 @@ #include "common/singleton.h" +#include "common/logger.h" + #include +#include #include +#include +#include +#include /** @@ -100,6 +106,76 @@ public: */ bool GetBoolProperty(std::string section, std::string key, bool &value); + /** Gets an array of values of type T in section under specified key + * The value separator is ','. + * \a array will only be changed if key exists + * \return return true on success + */ + template + bool SetArrayProperty(std::string section, std::string key, const std::vector& array) + { + try + { + std::string convertedValue = ArrayToString(array); + m_propertyTree.put(section + "." + key, convertedValue); + m_needsSave = true; + } + catch (std::exception & e) + { + GetLogger()->Error("Error on editing config file: %s\n", e.what()); + return false; + } + return true; + } + + /** Sets an array of values of type T in section under specified key. + * The value separator is ','. + * \a array will only be changed if key exists + * \return return true on success + */ + template + bool GetArrayProperty(std::string section, std::string key, std::vector& array) + { + try + { + std::string readValue = m_propertyTree.get(section + "." + key); + std::vector convertedValue = StringToArray(readValue); + array = std::move(convertedValue); + } + catch (std::exception & e) + { + GetLogger()->Log(m_loaded ? LOG_INFO : LOG_TRACE, "Error on parsing config file: %s\n", e.what()); + return false; + } + return true; + } + +private: + template + std::vector StringToArray(const std::string& s) + { + std::vector result; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, ',')) + { + result.push_back(boost::lexical_cast(item)); + } + return result; + } + + template + std::string ArrayToString(const std::vector &array) + { + std::ostringstream oss; + if (!array.empty()) + { + std::copy(array.begin(), array.end() - 1, std::ostream_iterator(oss, ",")); + oss << array.back(); + } + return oss.str(); + } + private: boost::property_tree::ptree m_propertyTree; bool m_needsSave; diff --git a/test/unit/common/colobot.ini b/test/unit/common/colobot.ini index c4d21623..7cc649b5 100644 --- a/test/unit/common/colobot.ini +++ b/test/unit/common/colobot.ini @@ -6,3 +6,8 @@ string_value=Hello world [test_int] int_value=42 + +[test_array] +string_array=AAA,Hello world,Gold Edition +int_array=2,3,1 +bool_array=1,0,1 diff --git a/test/unit/common/config_file_test.cpp b/test/unit/common/config_file_test.cpp index fcd8f036..762c3307 100644 --- a/test/unit/common/config_file_test.cpp +++ b/test/unit/common/config_file_test.cpp @@ -52,3 +52,25 @@ TEST_F(CConfigFileTest, ReadTest) ASSERT_TRUE(m_configFile.GetFloatProperty("test_float", "float_value", float_value)); ASSERT_FLOAT_EQ(1.5, float_value); } + +TEST_F(CConfigFileTest, ReadArrayTest) +{ + m_configFile.SetUseCurrentDirectory(true); + + ASSERT_TRUE(m_configFile.Init()); // load colobot.ini file + + std::vector expected_string_values = { "AAA", "Hello world", "Gold Edition" }; + std::vector string_values; + ASSERT_TRUE(m_configFile.GetArrayProperty("test_array", "string_array", string_values)); + ASSERT_EQ(expected_string_values, string_values); + + std::vector expected_int_values = { 2, 3, 1 }; + std::vector int_values; + ASSERT_TRUE(m_configFile.GetArrayProperty("test_array", "int_array", int_values)); + ASSERT_EQ(expected_int_values, int_values); + + std::vector expected_bool_values = { true, false, true }; + std::vector bool_values; + ASSERT_TRUE(m_configFile.GetArrayProperty("test_array", "bool_array", bool_values)); + ASSERT_EQ(expected_bool_values, bool_values); +} From 63bf6bed08c8ebe458999e2d544fc93ce1477dc9 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Sun, 19 Jul 2020 15:02:35 +0200 Subject: [PATCH 12/29] Add saving mods list in colobot.ini --- src/app/app.cpp | 4 +- src/app/modman.cpp | 99 +++++++++++++++++++++++------ src/app/modman.h | 15 +++-- src/app/pathman.cpp | 15 ++++- src/app/pathman.h | 3 + src/ui/screen/screen_setup_mods.cpp | 10 ++- 6 files changed, 118 insertions(+), 28 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 4f73ae16..54a14d07 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -520,7 +520,9 @@ bool CApplication::Create() GetLogger()->Warn("Config could not be loaded. Default values will be used!\n"); } - m_modManager->ReinitMods(); + m_modManager->FindMods(); + m_modManager->SaveMods(); + m_modManager->UpdatePaths(); // Create the sound instance. #ifdef OPENAL_SOUND diff --git a/src/app/modman.cpp b/src/app/modman.cpp index b67853ce..ef044a8a 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -19,22 +19,18 @@ #include "app/modman.h" -//TODO: clean up includes #include "common/config.h" #include "app/app.h" #include "app/pathman.h" +#include "common/config_file.h" #include "common/logger.h" -#include "common/restext.h" -#include "common/settings.h" -#include "common/stringutils.h" #include "common/resources/resourcemanager.h" -#include "common/system/system.h" - #include +#include #include #include @@ -46,16 +42,64 @@ CModManager::CModManager(CApplication* app, CPathManager* pathManager) { } -void CModManager::ReinitMods() +void CModManager::FindMods() { m_mods.clear(); - const auto foundMods = m_pathManager->FindMods(); - for (const auto& modPath : foundMods) + + // Load names from the config file + std::vector savedModNames; + GetConfigFile().GetArrayProperty("Mods", "Names", savedModNames); + std::vector savedEnabled; + GetConfigFile().GetArrayProperty("Mods", "Enabled", savedEnabled); + + // Transform the data into Mod structures + m_mods.reserve(savedModNames.size()); + for (int i = 0; i < savedModNames.size(); ++i) { - Mod mod; - mod.name = boost::filesystem::path(modPath).stem().string(); - mod.path = modPath; - mod.enabled = m_pathManager->ModLoaded(mod.path); //TODO: load from some config file + Mod mod{}; + mod.name = savedModNames[i]; + if (i < savedEnabled.size()) + { + mod.enabled = savedEnabled[i]; + } + mod.path = ""; // Find the path later + m_mods.push_back(mod); + } + + // Search the folders for mods + const auto rawPaths = m_pathManager->FindMods(); + std::map modPaths; + for (const auto& path : rawPaths) + { + auto modName = boost::filesystem::path(path).stem().string(); + modPaths.insert(std::make_pair(modName, path)); + } + + // Find paths for already saved mods + auto it = m_mods.begin(); + while (it != m_mods.end()) + { + auto& mod = *it; + const auto pathsIt = modPaths.find(mod.name); + if (pathsIt != modPaths.end()) + { + mod.path = (*pathsIt).second; + modPaths.erase(pathsIt); + ++it; + } + else + { + GetLogger()->Warn("Could not find mod %s, removing it from the list\n", mod.name.c_str()); + it = m_mods.erase(it); + } + } + + // Add the remaining found mods to the end of the list + for (const auto& newMod : modPaths) + { + Mod mod{}; + mod.name = newMod.first; + mod.path = newMod.second; m_mods.push_back(mod); } } @@ -82,23 +126,38 @@ void CModManager::DisableMod(const std::string& modName) mod->enabled = false; } -void CModManager::ReloadMods() +void CModManager::UpdatePaths() { + m_pathManager->RemoveAllMods(); for (const auto& mod : m_mods) { - bool loaded = m_pathManager->ModLoaded(mod.path); - if (mod.enabled && !loaded) + if (mod.enabled) { m_pathManager->AddMod(mod.path); } - else if (!mod.enabled && loaded) - { - m_pathManager->RemoveMod(mod.path); - } } +} + +void CModManager::ReloadResources() +{ m_app->ReloadResources(); } +void CModManager::SaveMods() +{ + std::vector savedNames; + savedNames.reserve(m_mods.size()); + std::transform(m_mods.begin(), m_mods.end(), std::back_inserter(savedNames), [](const Mod& mod) { return mod.name; }); + GetConfigFile().SetArrayProperty("Mods", "Names", savedNames); + + std::vector savedEnabled; + savedEnabled.reserve(m_mods.size()); + std::transform(m_mods.begin(), m_mods.end(), std::back_inserter(savedEnabled), [](const Mod& mod) { return mod.enabled; }); + GetConfigFile().SetArrayProperty("Mods", "Enabled", savedEnabled); + + GetConfigFile().Save(); +} + boost::optional CModManager::GetMod(const std::string& modName) { Mod* mod = FindMod(modName); diff --git a/src/app/modman.h b/src/app/modman.h index e1a47467..5ce3efa2 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -30,8 +30,8 @@ class CPathManager; struct Mod { - std::string name; - std::string path; + std::string name{}; + std::string path{}; bool enabled = false; //TODO: add metadata for UI }; @@ -41,6 +41,7 @@ struct Mod * \brief Main application * * This class handles the list of currently loaded mods. + * The order matters since the order in which files are loaded matters. * */ class CModManager @@ -49,7 +50,7 @@ public: CModManager(CApplication* app, CPathManager* pathManager); //! Finds all the mods along with their metadata - void ReinitMods(); + void FindMods(); //! Removes a mod from the list of loaded mods void EnableMod(const std::string& modName); @@ -58,7 +59,13 @@ public: void DisableMod(const std::string& modName); //! Reloads application resources so the enabled mods are applied - void ReloadMods(); + void ReloadResources(); + + //! Saves the current configuration of mods to the config file + void SaveMods(); + + //! Updates the paths in Path Manager according to the current mod configuration + void UpdatePaths(); boost::optional GetMod(const std::string& modName); const std::vector& GetMods() const; diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index 4c4a1a71..080c9b70 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -73,17 +73,30 @@ void CPathManager::AddMod(const std::string &modPath) { GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); CResourceManager::AddLocation(modPath, true); + m_mods.push_back(modPath); } void CPathManager::RemoveMod(const std::string &modPath) { GetLogger()->Info("Unloading mod: '%s'\n", modPath.c_str()); CResourceManager::RemoveLocation(modPath); + auto it = std::find(m_mods.cbegin(), m_mods.cend(), modPath); + if (it != m_mods.cend()) + m_mods.erase(it); +} + +void CPathManager::RemoveAllMods() +{ + for (const auto& modPath : m_mods) + { + CResourceManager::RemoveLocation(modPath); + } + m_mods.clear(); } bool CPathManager::ModLoaded(const std::string& modPath) { - return CResourceManager::LocationExists(modPath); + return std::find(m_mods.cbegin(), m_mods.cend(), modPath) != m_mods.end(); } std::vector CPathManager::FindMods() const diff --git a/src/app/pathman.h b/src/app/pathman.h index 7726786d..2fedfee6 100644 --- a/src/app/pathman.h +++ b/src/app/pathman.h @@ -40,6 +40,7 @@ public: void AddModSearchDir(const std::string &modSearchDirPath); void AddMod(const std::string &modPath); void RemoveMod(const std::string &modPath); + void RemoveAllMods(); bool ModLoaded(const std::string& modPath); std::vector FindMods() const; @@ -65,4 +66,6 @@ private: std::string m_savePath; //! Mod search paths std::vector m_modSearchDirs; + //! Mod paths + std::vector m_mods; }; diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp index 6fae8151..69cd13fe 100644 --- a/src/ui/screen/screen_setup_mods.cpp +++ b/src/ui/screen/screen_setup_mods.cpp @@ -71,6 +71,8 @@ void CScreenSetupMods::CreateInterface() Math::Point pos, ddim; std::string name; + m_modManager->FindMods(); + CScreenSetup::CreateInterface(); pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; @@ -155,7 +157,9 @@ bool CScreenSetupMods::EventProcess(const Event &event) modName = pl->GetItemName(pl->GetSelect()); m_modManager->EnableMod(modName); - m_modManager->ReloadMods(); + m_modManager->SaveMods(); + m_modManager->UpdatePaths(); + m_modManager->ReloadResources(); m_main->ChangePhase(PHASE_SETUPm); break; @@ -166,7 +170,9 @@ bool CScreenSetupMods::EventProcess(const Event &event) modName = pl->GetItemName(pl->GetSelect()); m_modManager->DisableMod(modName); - m_modManager->ReloadMods(); + m_modManager->SaveMods(); + m_modManager->UpdatePaths(); + m_modManager->ReloadResources(); m_main->ChangePhase(PHASE_SETUPm); break; From 56a8c5eb4862acbb1cb0cec6e48f98a7ca1a6ea9 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Sun, 19 Jul 2020 15:16:39 +0200 Subject: [PATCH 13/29] Fix build error on GCC --- src/app/modman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modman.cpp b/src/app/modman.cpp index ef044a8a..e2fb3468 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -54,7 +54,7 @@ void CModManager::FindMods() // Transform the data into Mod structures m_mods.reserve(savedModNames.size()); - for (int i = 0; i < savedModNames.size(); ++i) + for (size_t i = 0; i < savedModNames.size(); ++i) { Mod mod{}; mod.name = savedModNames[i]; From 7e6782a1beafb55b8226e397072494cd6ee8b685 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Sun, 19 Jul 2020 16:07:27 +0200 Subject: [PATCH 14/29] Try to improve font reloading --- src/graphics/engine/engine.cpp | 1 + src/graphics/engine/text.cpp | 34 ++++++++++++++++++++++++++-------- src/graphics/engine/text.h | 2 ++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 198e230d..c146d767 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -408,6 +408,7 @@ void CEngine::ReloadAllTextures() { FlushTextureCache(); m_text->FlushCache(); + m_text->ReloadFonts(); m_app->GetEventQueue()->AddEvent(Event(EVENT_RELOAD_TEXTURES)); UpdateGroundSpotTextures(); diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index a85bd5fd..bf8ac426 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -191,17 +191,32 @@ CText::~CText() bool CText::Create() { - CFontLoader fontLoader; - if (!fontLoader.Init()) - { - GetLogger()->Warn("Error on parsing fonts config file: failed to open file\n"); - } if (TTF_Init() != 0) { m_error = std::string("TTF_Init error: ") + std::string(TTF_GetError()); return false; } + if (!ReloadFonts()) + { + return false; + } + + return true; +} + +bool CText::ReloadFonts() +{ + CFontLoader fontLoader; + if (!fontLoader.Init()) + { + GetLogger()->Warn("Error on parsing fonts config file: failed to open file\n"); + } + + // Backup previous fonts + auto fonts = std::move(m_fonts); + m_fonts.clear(); + for (auto type : {FONT_COMMON, FONT_STUDIO, FONT_SATCOM}) { m_fonts[static_cast(type)] = MakeUnique(fontLoader.GetFont(type)); @@ -214,7 +229,10 @@ bool CText::Create() FontType type = (*it).first; CachedFont* cf = GetOrOpenFont(type, m_defaultSize); if (cf == nullptr || cf->font == nullptr) + { + m_fonts = std::move(fonts); return false; + } } return true; @@ -259,9 +277,9 @@ void CText::FlushCache() } } - //TODO: fix this - Destroy(); - Create(); + m_lastCachedFont = nullptr; + m_lastFontType = FONT_COMMON; + m_lastFontSize = 0; } int CText::GetTabSize() diff --git a/src/graphics/engine/text.h b/src/graphics/engine/text.h index 3a5bff88..34a2d832 100644 --- a/src/graphics/engine/text.h +++ b/src/graphics/engine/text.h @@ -256,6 +256,8 @@ public: //! Flushes cached textures void FlushCache(); + //! Try to load new font files + bool ReloadFonts(); //@{ //! Tab size management From 242477e3ee72bf5160d9af4907f8be0323bdd2a2 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Mon, 20 Jul 2020 22:12:51 +0200 Subject: [PATCH 15/29] Major UI change to mod manager Move the button to the sidebar. Add reordering option. General improvements and refactoring. --- CMakeLists.txt | 4 +- po/colobot.pot | 39 ++- po/cs.po | 58 +++- po/de.po | 58 +++- po/fr.po | 58 +++- po/pl.po | 58 +++- po/pt.po | 58 +++- po/ru.po | 58 +++- src/CMakeLists.txt | 4 +- src/app/modman.cpp | 61 ++-- src/app/modman.h | 35 +- src/common/event.cpp | 13 +- src/common/event.h | 18 +- src/common/restext.cpp | 23 +- src/common/restext.h | 10 +- src/level/robotmain.cpp | 7 +- src/level/robotmain.h | 3 +- src/ui/maindialog.cpp | 1 - src/ui/mainui.cpp | 16 +- src/ui/mainui.h | 4 +- src/ui/screen/screen_main_menu.cpp | 10 + src/ui/screen/screen_mod_list.cpp | 490 ++++++++++++++++++++++++++++ src/ui/screen/screen_mod_list.h | 96 ++++++ src/ui/screen/screen_setup.cpp | 23 +- src/ui/screen/screen_setup_mods.cpp | 289 ---------------- src/ui/screen/screen_setup_mods.h | 52 --- 26 files changed, 1007 insertions(+), 539 deletions(-) create mode 100644 src/ui/screen/screen_mod_list.cpp create mode 100644 src/ui/screen/screen_mod_list.h delete mode 100644 src/ui/screen/screen_setup_mods.cpp delete mode 100644 src/ui/screen/screen_setup_mods.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 63f1ace6..94c3a574 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,9 +192,9 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(DEBUG_CXX_FLAGS "/MTd /ZI") else(MSVC_STATIC) set(RELEASE_CXX_FLAGS "/MD /Ox") - set(DEBUG_CXX_FLAGS "/MDd /ZI") + set(DEBUG_CXX_FLAGS "/MDd /Od /ZI") endif() - set(TEST_CXX_FLAGS "") + set(TEST_CXX_FLAGS "${DEBUG_CXX_FLAGS}") add_definitions(-DNOEXCEPT= -DHAS_MSVC_EXCEPTION_BUG) # Needed for Debug information (it's set to "No" by default for some reason) diff --git a/po/colobot.pot b/po/colobot.pot index a6b61ea3..53bfda71 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -84,6 +84,9 @@ msgstr "" msgid "Load a saved mission" msgstr "" +msgid "Mods" +msgstr "" + msgid "Chapters:" msgstr "" @@ -120,10 +123,19 @@ msgstr "" msgid "2) Then press the key you want to use instead." msgstr "" -msgid "Unloaded Mods:" +msgid "Mods:" msgstr "" -msgid "Loaded Mods:" +msgid "Information:" +msgstr "" + +msgid "Description:" +msgstr "" + +msgid "Enable\\Enable the selected mod" +msgstr "" + +msgid "Disable\\Disable the selected mod" msgstr "" msgid "Face type:" @@ -186,6 +198,9 @@ msgstr "" msgid "The address %s could not be opened in a web browser." msgstr "" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "Keyword help(\\key cbot;)" msgstr "" @@ -336,6 +351,9 @@ msgstr "" msgid "SatCom" msgstr "" +msgid "Mods\\Mod manager" +msgstr "" + msgid "Change player\\Change player" msgstr "" @@ -363,16 +381,22 @@ msgstr "" msgid "Play\\Start mission!" msgstr "" -msgid "Workshop\\Open Workshop to search Mods" +msgid "Workshop\\Open the workshop to search for mods" msgstr "" -msgid "Open Directory\\Open Mods directory" +msgid "Open Directory\\Open the mods directory" msgstr "" -msgid "Load\\Load Mod" +msgid "Apply\\Apply the current mod configuration" msgstr "" -msgid "Unload\\Unload Mod" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + +msgid "Refresh\\Refresh the list of currently installed mods" msgstr "" msgid "Device\\Driver and resolution settings" @@ -390,9 +414,6 @@ msgstr "" msgid "Sound\\Music and game sound volume" msgstr "" -msgid "Mods\\Manage installed mods" -msgstr "" - msgid "Unit" msgstr "" diff --git a/po/cs.po b/po/cs.po index 7fff13fe..532510ea 100644 --- a/po/cs.po +++ b/po/cs.po @@ -125,6 +125,9 @@ msgstr "Vzhled\\Upravte svůj vzhled" msgid "Apply changes\\Activates the changed settings" msgstr "Uložit změny\\Aktivovat změny nastavení" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Chybí vhodný konstruktor" @@ -479,6 +482,10 @@ msgstr "Vrtná věž" msgid "Descend\\Reduces the power of the jet" msgstr "Klesat\\Snížit tah tryskového motoru" +#, fuzzy +msgid "Description:" +msgstr "Rozlišení:" + msgid "Destroy" msgstr "Zbourat" @@ -491,6 +498,10 @@ msgstr "Drtič" msgid "Device\\Driver and resolution settings" msgstr "Obrazovka\\Nastavení grafické karty a rozlišení" +#, fuzzy +msgid "Disable\\Disable the selected mod" +msgstr "Smazat\\Smaže vybraný soubor" + msgid "Dividing by zero" msgstr "Dělení nulou" @@ -507,6 +518,9 @@ msgstr "Dveře blokuje robot nebo jiný objekt" msgid "Down (\\key gdown;)" msgstr "Dolů (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Tužkobot" @@ -534,6 +548,10 @@ msgstr "Vejce" msgid "Empty character constant" msgstr "" +#, fuzzy +msgid "Enable\\Enable the selected mod" +msgstr "Nahrát\\Nahraje vybranou misi" + msgid "End of block missing" msgstr "Chybí konec bloku" @@ -760,6 +778,9 @@ msgstr "Infikováno virem; dočasně mimo provoz" msgid "Information exchange post" msgstr "Komunikační stanice" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Příkaz \"break\" mimo cyklus" @@ -850,18 +871,12 @@ msgstr "Seznam uložených misí" msgid "Load a saved mission" msgstr "Nahrát uloženou misi" -msgid "Load\\Load Mod" -msgstr "" - msgid "Load\\Load a saved mission" msgstr "Nahrát\\Nahrát uloženou misi" msgid "Load\\Loads the selected mission" msgstr "Nahrát\\Nahraje vybranou misi" -msgid "Loaded Mods:" -msgstr "" - msgid "Loading basic level settings" msgstr "Načítám základní nastavení mapy" @@ -916,7 +931,13 @@ msgstr "Mise na této planetě:" msgid "Missions\\Select mission" msgstr "Mise\\Vyberte misi" -msgid "Mods\\Manage installed mods" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" msgstr "" msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" @@ -1087,7 +1108,7 @@ msgstr "Otevřít" msgid "Open (Ctrl+O)" msgstr "Otevřít (Ctrl+O)" -msgid "Open Directory\\Open Mods directory" +msgid "Open Directory\\Open the mods directory" msgstr "" msgid "Opening brace missing" @@ -1300,6 +1321,9 @@ msgstr "Červená vlajka" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Odlesky na tlačítkách\\Blyštivá tlačítka" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Pozůstatky mise Apollo" @@ -1584,6 +1608,9 @@ msgstr "" msgid "The types of the two operands are incompatible" msgstr "Operaci nelze provést s operandy těchto dvou typů" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Tato třída již existuje" @@ -1717,15 +1744,12 @@ msgstr "Neznámá zástupná sekvence" msgid "Unknown function" msgstr "Neznámá funkce" -msgid "Unload\\Unload Mod" -msgstr "" - -msgid "Unloaded Mods:" -msgstr "" - msgid "Up (\\key gup;)" msgstr "Vzhůru (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Uranové ložisko (místo pro vrtnou věž)" @@ -1798,7 +1822,7 @@ msgstr "Létající detektor" msgid "Withdraw shield (\\key action;)" msgstr "Vypnout štít (\\key action;)" -msgid "Workshop\\Open Workshop to search Mods" +msgid "Workshop\\Open the workshop to search for mods" msgstr "" msgid "Worm" @@ -1956,3 +1980,7 @@ msgstr "colobot.info" msgid "epsitec.com" msgstr "epsitec.com" + +#, fuzzy +#~ msgid "No mods installed!" +#~ msgstr "Žádné uživatelské mapy nejsou nainstalovány!" diff --git a/po/de.po b/po/de.po index 702bab19..15f4c336 100644 --- a/po/de.po +++ b/po/de.po @@ -126,6 +126,9 @@ msgstr "Aussehen\\Erscheinungsbild des Astronauten einstellen" msgid "Apply changes\\Activates the changed settings" msgstr "Änderungen anwenden\\Getätigte Einstellungen anwenden" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Es gibt keinen geeigneten Konstruktor" @@ -480,6 +483,10 @@ msgstr "Bohrturm" msgid "Descend\\Reduces the power of the jet" msgstr "Sinken\\Leistung des Triebwerks drosseln" +#, fuzzy +msgid "Description:" +msgstr "Auflösung:" + msgid "Destroy" msgstr "Zerstören" @@ -492,6 +499,10 @@ msgstr "Einstampfer" msgid "Device\\Driver and resolution settings" msgstr "Bildschirm\\Driver und Bildschirmauflösung" +#, fuzzy +msgid "Disable\\Disable the selected mod" +msgstr "Löschen\\Löscht die gespeicherte Mission" + msgid "Dividing by zero" msgstr "Division durch Null" @@ -508,6 +519,9 @@ msgstr "Die Türen werden von einem Gegenstand blockiert" msgid "Down (\\key gdown;)" msgstr "Sinkt (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Zeichner" @@ -535,6 +549,10 @@ msgstr "Ei" msgid "Empty character constant" msgstr "" +#, fuzzy +msgid "Enable\\Enable the selected mod" +msgstr "Laden\\Öffnet eine gespeicherte Mission" + msgid "End of block missing" msgstr "Es fehlt eine geschlossene geschweifte Klammer \"}\" (Ende des Blocks)" @@ -762,6 +780,9 @@ msgstr "Von Virus infiziert, zeitweise außer Betrieb" msgid "Information exchange post" msgstr "Infoserver" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Anweisung \"break\" außerhalb einer Schleife" @@ -852,18 +873,12 @@ msgstr "Liste der gespeicherten Missionen" msgid "Load a saved mission" msgstr "Gespeicherte Mission laden" -msgid "Load\\Load Mod" -msgstr "" - msgid "Load\\Load a saved mission" msgstr "Laden\\Eine gespeicherte Mission öffnen" msgid "Load\\Loads the selected mission" msgstr "Laden\\Öffnet eine gespeicherte Mission" -msgid "Loaded Mods:" -msgstr "" - msgid "Loading basic level settings" msgstr "Lade Level-Grundeinstellungen" @@ -932,7 +947,13 @@ msgstr "Liste der Missionen des Planeten:" msgid "Missions\\Select mission" msgstr "Missionen\\Aufbruch ins Weltall" -msgid "Mods\\Manage installed mods" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" msgstr "" msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" @@ -1103,7 +1124,7 @@ msgstr "Öffnen" msgid "Open (Ctrl+O)" msgstr "Öffnen (Ctrl+O)" -msgid "Open Directory\\Open Mods directory" +msgid "Open Directory\\Open the mods directory" msgstr "" msgid "Opening brace missing" @@ -1317,6 +1338,9 @@ msgstr "Rote Fahne" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Glänzende Tasten\\Glänzende Tasten in den Menüs" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Überreste einer Apollo-Mission" @@ -1601,6 +1625,9 @@ msgstr "" msgid "The types of the two operands are incompatible" msgstr "Die zwei Operanden sind nicht kompatibel" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Diese Klasse gibt es schon" @@ -1734,15 +1761,12 @@ msgstr "" msgid "Unknown function" msgstr "Unbekannte Funktion" -msgid "Unload\\Unload Mod" -msgstr "" - -msgid "Unloaded Mods:" -msgstr "" - msgid "Up (\\key gup;)" msgstr "Steigt (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Markierung für unterirdisches Platinvorkommen" @@ -1815,7 +1839,7 @@ msgstr "Schnüffler" msgid "Withdraw shield (\\key action;)" msgstr "Schutzschild einholen (\\key action;)" -msgid "Workshop\\Open Workshop to search Mods" +msgid "Workshop\\Open the workshop to search for mods" msgstr "" msgid "Worm" @@ -2051,6 +2075,10 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Schatten unter der Maus\\Ein Schatten erscheint unter der Maus" +#, fuzzy +#~ msgid "No mods installed!" +#~ msgstr "Keine benutzerdefinierten Level vorhanden" + #~ msgid "No other robot" #~ msgstr "Kein anderer Roboter" diff --git a/po/fr.po b/po/fr.po index dcb5d42e..1b07f1fe 100644 --- a/po/fr.po +++ b/po/fr.po @@ -125,6 +125,9 @@ msgstr "Aspect\\Choisir votre aspect" msgid "Apply changes\\Activates the changed settings" msgstr "Appliquer les changements\\Active les changements effectués" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Constructeur approprié manquant" @@ -482,6 +485,10 @@ msgstr "Derrick" msgid "Descend\\Reduces the power of the jet" msgstr "Descendre\\Diminuer la puissance du réacteur" +#, fuzzy +msgid "Description:" +msgstr "Résolutions :" + msgid "Destroy" msgstr "Détruire" @@ -494,6 +501,10 @@ msgstr "Destructeur" msgid "Device\\Driver and resolution settings" msgstr "Affichage\\Pilote et résolution d'affichage" +#, fuzzy +msgid "Disable\\Disable the selected mod" +msgstr "Supprimer\\Supprime l'enregistrement sélectionné" + msgid "Dividing by zero" msgstr "Division par zéro" @@ -510,6 +521,9 @@ msgstr "Portes bloquées par un robot ou un objet" msgid "Down (\\key gdown;)" msgstr "Descend (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Robot dessinateur" @@ -537,6 +551,10 @@ msgstr "Oeuf" msgid "Empty character constant" msgstr "" +#, fuzzy +msgid "Enable\\Enable the selected mod" +msgstr "Charger\\Charger la mission sélectionnée" + msgid "End of block missing" msgstr "Il manque la fin du bloc" @@ -764,6 +782,9 @@ msgstr "Infecté par un virus; ne fonctionne plus temporairement" msgid "Information exchange post" msgstr "Station relais" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Instruction \"break\" en dehors d'une boucle" @@ -854,18 +875,12 @@ msgstr "Liste des missions enregistrées" msgid "Load a saved mission" msgstr "Chargement d'une mission enregistrée" -msgid "Load\\Load Mod" -msgstr "" - msgid "Load\\Load a saved mission" msgstr "Charger\\Charger une mission enregistrée" msgid "Load\\Loads the selected mission" msgstr "Charger\\Charger la mission sélectionnée" -msgid "Loaded Mods:" -msgstr "" - msgid "Loading basic level settings" msgstr "Chargement des configurations de base du niveau" @@ -934,7 +949,13 @@ msgstr "Liste des missions du chapitre :" msgid "Missions\\Select mission" msgstr "Missions\\La grande aventure" -msgid "Mods\\Manage installed mods" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" msgstr "" msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" @@ -1105,7 +1126,7 @@ msgstr "Ouvrir" msgid "Open (Ctrl+O)" msgstr "Ouvrir (Ctrl+O)" -msgid "Open Directory\\Open Mods directory" +msgid "Open Directory\\Open the mods directory" msgstr "" msgid "Opening brace missing" @@ -1319,6 +1340,9 @@ msgstr "Drapeau rouge" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Reflets sur les boutons\\Boutons brillants" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Vestige d'une mission Apollo" @@ -1603,6 +1627,9 @@ msgstr "" msgid "The types of the two operands are incompatible" msgstr "Les deux opérandes ne sont pas de types compatibles" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Cette classe existe déjà" @@ -1736,15 +1763,12 @@ msgstr "Séquence d'échappement inconnue" msgid "Unknown function" msgstr "Routine inconnue" -msgid "Unload\\Unload Mod" -msgstr "" - -msgid "Unloaded Mods:" -msgstr "" - msgid "Up (\\key gup;)" msgstr "Monte (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Emplacement pour un derrick (minerai d'uranium)" @@ -1817,7 +1841,7 @@ msgstr "Robot renifleur volant" msgid "Withdraw shield (\\key action;)" msgstr "Refermer le bouclier (\\key action;)" -msgid "Workshop\\Open Workshop to search Mods" +msgid "Workshop\\Open the workshop to search for mods" msgstr "" msgid "Worm" @@ -2055,6 +2079,10 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Souris ombrée\\Jolie souris avec une ombre" +#, fuzzy +#~ msgid "No mods installed!" +#~ msgstr "Pas de niveaux spéciaux installés !" + #~ msgid "No other robot" #~ msgstr "Pas d'autre robot" diff --git a/po/pl.po b/po/pl.po index 9535ab56..251de7fe 100644 --- a/po/pl.po +++ b/po/pl.po @@ -124,6 +124,9 @@ msgstr "Wygląd\\Wybierz swoją postać" msgid "Apply changes\\Activates the changed settings" msgstr "Zastosuj zmiany\\Aktywuje zmienione ustawienia" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Brak odpowiedniego konstruktora" @@ -478,6 +481,10 @@ msgstr "Kopalnia" msgid "Descend\\Reduces the power of the jet" msgstr "W dół\\Zmniejsza moc silnika" +#, fuzzy +msgid "Description:" +msgstr "Rozdzielczość:" + msgid "Destroy" msgstr "Zniszcz" @@ -490,6 +497,10 @@ msgstr "Destroyer" msgid "Device\\Driver and resolution settings" msgstr "Urządzenie\\Ustawienia sterownika i rozdzielczości" +#, fuzzy +msgid "Disable\\Disable the selected mod" +msgstr "Usuń\\Usuwa zaznaczony plik" + msgid "Dividing by zero" msgstr "Dzielenie przez zero" @@ -506,6 +517,9 @@ msgstr "Drzwi zablokowane przez robota lub inny obiekt" msgid "Down (\\key gdown;)" msgstr "Dół (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Robot rysownik" @@ -533,6 +547,10 @@ msgstr "Jajo" msgid "Empty character constant" msgstr "" +#, fuzzy +msgid "Enable\\Enable the selected mod" +msgstr "Wczytaj\\Wczytuje zaznaczoną misję" + msgid "End of block missing" msgstr "Brak końca bloku" @@ -759,6 +777,9 @@ msgstr "Zainfekowane wirusem, chwilowo niesprawne" msgid "Information exchange post" msgstr "Stacja przekaźnikowa informacji" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Polecenie \"break\" na zewnątrz pętli" @@ -849,18 +870,12 @@ msgstr "Lista zapisanych misji" msgid "Load a saved mission" msgstr "Wczytaj zapisaną misję" -msgid "Load\\Load Mod" -msgstr "" - msgid "Load\\Load a saved mission" msgstr "Wczytaj\\Wczytuje zapisaną misję" msgid "Load\\Loads the selected mission" msgstr "Wczytaj\\Wczytuje zaznaczoną misję" -msgid "Loaded Mods:" -msgstr "" - msgid "Loading basic level settings" msgstr "Wczytywanie ustawień poziomu" @@ -915,7 +930,13 @@ msgstr "Misje na tej planecie:" msgid "Missions\\Select mission" msgstr "Misje\\Wybierz misję" -msgid "Mods\\Manage installed mods" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" msgstr "" msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" @@ -1086,7 +1107,7 @@ msgstr "Otwórz" msgid "Open (Ctrl+O)" msgstr "Otwórz (Ctrl+O)" -msgid "Open Directory\\Open Mods directory" +msgid "Open Directory\\Open the mods directory" msgstr "" msgid "Opening brace missing" @@ -1299,6 +1320,9 @@ msgstr "Czerwona flaga" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Odbicia na przyciskach \\Świecące przyciski" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Pozostałości z misji Apollo" @@ -1583,6 +1607,9 @@ msgstr "Nie udało się otworzyć ścieżki %s w przeglądarce plików." msgid "The types of the two operands are incompatible" msgstr "Niezgodne typy operatorów" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Taka klasa już istnieje" @@ -1716,15 +1743,12 @@ msgstr "" msgid "Unknown function" msgstr "Funkcja nieznana" -msgid "Unload\\Unload Mod" -msgstr "" - -msgid "Unloaded Mods:" -msgstr "" - msgid "Up (\\key gup;)" msgstr "Góra (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Złoże uranu (miejsce na kopalnię)" @@ -1797,7 +1821,7 @@ msgstr "Szperacz latający" msgid "Withdraw shield (\\key action;)" msgstr "Wyłącz osłonę (\\key action;)" -msgid "Workshop\\Open Workshop to search Mods" +msgid "Workshop\\Open the workshop to search for mods" msgstr "" msgid "Worm" @@ -2034,6 +2058,10 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Cień kursora myszy\\Dodaje cień kursorowi myszy" +#, fuzzy +#~ msgid "No mods installed!" +#~ msgstr "Brak zainstalowanych poziomów użytkownika!" + #~ msgid "No other robot" #~ msgstr "Brak innego robota" diff --git a/po/pt.po b/po/pt.po index 159dd313..aed2d681 100644 --- a/po/pt.po +++ b/po/pt.po @@ -122,6 +122,9 @@ msgstr "Aparência\\Escolha sua aparência" msgid "Apply changes\\Activates the changed settings" msgstr "Aplicar mudanças\\Ativa as configurações alteradas" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Construtor apropriado faltando" @@ -477,6 +480,10 @@ msgstr "Extrator" msgid "Descend\\Reduces the power of the jet" msgstr "Descer\\Diminui o poder do jato" +#, fuzzy +msgid "Description:" +msgstr "Resolução:" + msgid "Destroy" msgstr "Destruir" @@ -489,6 +496,10 @@ msgstr "Destruidor" msgid "Device\\Driver and resolution settings" msgstr "Dispositivo\\Configurações de driver e resolução" +#, fuzzy +msgid "Disable\\Disable the selected mod" +msgstr "Excluir\\Exclui o arquivo selecionado" + msgid "Dividing by zero" msgstr "Dividindo por zero" @@ -505,6 +516,9 @@ msgstr "Portas bloqueadas por um robô ou outro objeto" msgid "Down (\\key gdown;)" msgstr "Baixo (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Robô cartoonista" @@ -532,6 +546,10 @@ msgstr "Ovo" msgid "Empty character constant" msgstr "" +#, fuzzy +msgid "Enable\\Enable the selected mod" +msgstr "Carregar\\Carrega a missão selecionada" + msgid "End of block missing" msgstr "Fim do bloco ausente" @@ -759,6 +777,9 @@ msgstr "Infectado por vírus; temporariamento fora de serviço" msgid "Information exchange post" msgstr "Posto de troca de informação" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Intrução \"break\" fora de um laço" @@ -849,18 +870,12 @@ msgstr "Lista das missões salvas" msgid "Load a saved mission" msgstr "Carregar uma missão salva" -msgid "Load\\Load Mod" -msgstr "" - msgid "Load\\Load a saved mission" msgstr "Carregar\\Carregar uma missão salva" msgid "Load\\Loads the selected mission" msgstr "Carregar\\Carrega a missão selecionada" -msgid "Loaded Mods:" -msgstr "" - msgid "Loading basic level settings" msgstr "Carregando configurações de nível básico" @@ -929,7 +944,13 @@ msgstr "Lista de missões neste planeta:" msgid "Missions\\Select mission" msgstr "Missões\\Selecione uma missão" -msgid "Mods\\Manage installed mods" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" msgstr "" msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" @@ -1100,7 +1121,7 @@ msgstr "Abrir" msgid "Open (Ctrl+O)" msgstr "Abrir (Ctrl+O)" -msgid "Open Directory\\Open Mods directory" +msgid "Open Directory\\Open the mods directory" msgstr "" msgid "Opening brace missing" @@ -1314,6 +1335,9 @@ msgstr "Bandeira vermelha" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Reflexões nos botões\\Botões brilhantes" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Restos da missão Apollo" @@ -1598,6 +1622,9 @@ msgstr "" msgid "The types of the two operands are incompatible" msgstr "Os tipos dos dois operandos são incompativeis" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Esta classe já existe" @@ -1731,15 +1758,12 @@ msgstr "Sequência de escape desconhecidade" msgid "Unknown function" msgstr "Função desconhecida" -msgid "Unload\\Unload Mod" -msgstr "" - -msgid "Unloaded Mods:" -msgstr "" - msgid "Up (\\key gup;)" msgstr "Cima (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Depósito de urânio (local para extrator)" @@ -1812,7 +1836,7 @@ msgstr "Farejador alado" msgid "Withdraw shield (\\key action;)" msgstr "Retirar escudo (\\key action;)" -msgid "Workshop\\Open Workshop to search Mods" +msgid "Workshop\\Open the workshop to search for mods" msgstr "" msgid "Worm" @@ -2053,6 +2077,10 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Souris ombrée\\Jolie souris avec une ombre" +#, fuzzy +#~ msgid "No mods installed!" +#~ msgstr "Nenhum nível de usuário instalado!" + #~ msgid "No other robot" #~ msgstr "Pas d'autre robot" diff --git a/po/ru.po b/po/ru.po index 04d9187a..d450ccf4 100644 --- a/po/ru.po +++ b/po/ru.po @@ -124,6 +124,9 @@ msgstr "Внешность\\Настройка внешности" msgid "Apply changes\\Activates the changed settings" msgstr "Принять\\Принять изменения настроек" +msgid "Apply\\Apply the current mod configuration" +msgstr "" + msgid "Appropriate constructor missing" msgstr "Соответствующий конструктор отсутствует" @@ -486,6 +489,10 @@ msgstr "Космический корабль" msgid "Descend\\Reduces the power of the jet" msgstr "Снижение и посадка\\Понижение мощности реактивного двигателя" +#, fuzzy +msgid "Description:" +msgstr "Разрешение:" + msgid "Destroy" msgstr "Уничтожить" @@ -498,6 +505,10 @@ msgstr "Уничтожитель" msgid "Device\\Driver and resolution settings" msgstr "Устройство\\Драйвер и настройки разрешения" +#, fuzzy +msgid "Disable\\Disable the selected mod" +msgstr "Удалить\\Удаление выбранного файла" + msgid "Dividing by zero" msgstr "Деление на ноль (запрещено!)" @@ -514,6 +525,9 @@ msgstr "Двери заблокированы роботом или другим msgid "Down (\\key gdown;)" msgstr "Вниз (\\key gdown;)" +msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Drawer bot" msgstr "Рисовальщик" @@ -541,6 +555,10 @@ msgstr "Яйцо" msgid "Empty character constant" msgstr "" +#, fuzzy +msgid "Enable\\Enable the selected mod" +msgstr "Загрузить\\Загрузить выбранную миссию" + msgid "End of block missing" msgstr "Отсутствует конец блока" @@ -768,6 +786,9 @@ msgstr "Заражено вирусом. Временно вышел из стр msgid "Information exchange post" msgstr "Пост обмена информацией" +msgid "Information:" +msgstr "" + msgid "Instruction \"break\" outside a loop" msgstr "Инструкция \"break\" вне цикла" @@ -858,18 +879,12 @@ msgstr "Список сохраненных миссий" msgid "Load a saved mission" msgstr "Загрузить" -msgid "Load\\Load Mod" -msgstr "" - msgid "Load\\Load a saved mission" msgstr "Загрузить\\Загрузить сохраненную миссию" msgid "Load\\Loads the selected mission" msgstr "Загрузить\\Загрузить выбранную миссию" -msgid "Loaded Mods:" -msgstr "" - msgid "Loading basic level settings" msgstr "Загрузка основных настроек уровня" @@ -938,7 +953,13 @@ msgstr "Миссии на этой планете:" msgid "Missions\\Select mission" msgstr "Миссии\\Выбор миссии" -msgid "Mods\\Manage installed mods" +msgid "Mods" +msgstr "" + +msgid "Mods:" +msgstr "" + +msgid "Mods\\Mod manager" msgstr "" msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" @@ -1111,7 +1132,7 @@ msgstr "Открыть" msgid "Open (Ctrl+O)" msgstr "Открыть (Ctrl+O)" -msgid "Open Directory\\Open Mods directory" +msgid "Open Directory\\Open the mods directory" msgstr "" msgid "Opening brace missing" @@ -1326,6 +1347,9 @@ msgstr "Красный флаг" msgid "Reflections on the buttons \\Shiny buttons" msgstr "Отражения на кнопках \\Блестящие кнопки" +msgid "Refresh\\Refresh the list of currently installed mods" +msgstr "" + msgid "Remains of Apollo mission" msgstr "Остатки миссии Аполлон" @@ -1614,6 +1638,9 @@ msgstr "" msgid "The types of the two operands are incompatible" msgstr "Типы операндов несовместимы" +msgid "There are unsaved changes. Do you want to save them before leaving?" +msgstr "" + msgid "This class already exists" msgstr "Этот класс уже существует" @@ -1747,15 +1774,12 @@ msgstr "" msgid "Unknown function" msgstr "Неизвестная функция" -msgid "Unload\\Unload Mod" -msgstr "" - -msgid "Unloaded Mods:" -msgstr "" - msgid "Up (\\key gup;)" msgstr "Вверх (\\key gup;)" +msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" +msgstr "" + msgid "Uranium deposit (site for derrick)" msgstr "Запасы урана (место для буровой вышки)" @@ -1828,7 +1852,7 @@ msgstr "Летающий искатель" msgid "Withdraw shield (\\key action;)" msgstr "Снять щит (\\key action;)" -msgid "Workshop\\Open Workshop to search Mods" +msgid "Workshop\\Open the workshop to search for mods" msgstr "" msgid "Worm" @@ -2063,6 +2087,10 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Тень мыши\\Мышь отбрасывает тень" +#, fuzzy +#~ msgid "No mods installed!" +#~ msgstr "Не установленны пользовательские уровни!" + #~ msgid "No other robot" #~ msgstr "Нет робота" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f70489ab..df752615 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -569,6 +569,8 @@ set(BASE_SOURCES ui/screen/screen_loading.h ui/screen/screen_main_menu.cpp ui/screen/screen_main_menu.h + ui/screen/screen_mod_list.cpp + ui/screen/screen_mod_list.h ui/screen/screen_player_select.cpp ui/screen/screen_player_select.h ui/screen/screen_quit.cpp @@ -583,8 +585,6 @@ set(BASE_SOURCES ui/screen/screen_setup_game.h ui/screen/screen_setup_graphics.cpp ui/screen/screen_setup_graphics.h - ui/screen/screen_setup_mods.cpp - ui/screen/screen_setup_mods.h ui/screen/screen_setup_sound.cpp ui/screen/screen_setup_sound.h ui/screen/screen_welcome.cpp diff --git a/src/app/modman.cpp b/src/app/modman.cpp index e2fb3468..cf552641 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -30,9 +30,10 @@ #include "common/resources/resourcemanager.h" #include -#include +#include #include #include +#include "modman.h" using namespace boost::filesystem; @@ -104,26 +105,40 @@ void CModManager::FindMods() } } -void CModManager::EnableMod(const std::string& modName) +void CModManager::EnableMod(size_t i) { - Mod* mod = FindMod(modName); - if (!mod) - { - GetLogger()->Error("Could not enable mod: %s not found\n", modName.c_str()); - return; - } - mod->enabled = true; + m_mods[i].enabled = true; } -void CModManager::DisableMod(const std::string& modName) +void CModManager::DisableMod(size_t i) { - Mod* mod = FindMod(modName); - if (!mod) + m_mods[i].enabled = false; +} + +size_t CModManager::MoveUp(size_t i) +{ + if (i != 0) { - GetLogger()->Error("Could not disable mod: %s not found\n", modName.c_str()); - return; + std::swap(m_mods[i - 1], m_mods[i]); + return i - 1; + } + else + { + return i; + } +} + +size_t CModManager::MoveDown(size_t i) +{ + if (i != m_mods.size() - 1) + { + std::swap(m_mods[i], m_mods[i + 1]); + return i + 1; + } + else + { + return i; } - mod->enabled = false; } void CModManager::UpdatePaths() @@ -158,19 +173,17 @@ void CModManager::SaveMods() GetConfigFile().Save(); } -boost::optional CModManager::GetMod(const std::string& modName) +size_t CModManager::CountMods() const { - Mod* mod = FindMod(modName); - return mod != nullptr ? *mod : boost::optional(); + return m_mods.size(); +} + +const Mod& CModManager::GetMod(size_t i) const +{ + return m_mods[i]; } const std::vector& CModManager::GetMods() const { return m_mods; } - -Mod* CModManager::FindMod(const std::string& modName) -{ - auto it = std::find_if(m_mods.begin(), m_mods.end(), [&](Mod& mod) { return mod.name == modName; }); - return it != m_mods.end() ? &(*it) : nullptr; -} diff --git a/src/app/modman.h b/src/app/modman.h index 5ce3efa2..984d54ea 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -37,11 +37,17 @@ struct Mod }; /** - * \class CApplication - * \brief Main application + * \class CModManager + * \brief This class handles the list of mods. * - * This class handles the list of currently loaded mods. - * The order matters since the order in which files are loaded matters. + * The order matters since the order in which files are loaded matters, + * because some files can be overwritten. + * + * The list can be kept in the config file with the \ref SaveMods function. + * + * The changes in the list do not immediately apply. + * Separate calls to \ref UpdatePaths and \ref ReloadResources, probably in this order, + * need to be done for the changes to apply. * */ class CModManager @@ -53,10 +59,16 @@ public: void FindMods(); //! Removes a mod from the list of loaded mods - void EnableMod(const std::string& modName); + void EnableMod(size_t i); //! Adds a mod to the list of loaded mods - void DisableMod(const std::string& modName); + void DisableMod(size_t i); + + //! Moves the selected mod up in the list so that it's loaded sooner than others, returns the new index + size_t MoveUp(size_t i); + + //! Moves the selected mod down in the list so that it's loaded later than others, returns the new index + size_t MoveDown(size_t i); //! Reloads application resources so the enabled mods are applied void ReloadResources(); @@ -67,11 +79,14 @@ public: //! Updates the paths in Path Manager according to the current mod configuration void UpdatePaths(); - boost::optional GetMod(const std::string& modName); - const std::vector& GetMods() const; + //! Number of mods loaded + size_t CountMods() const; -private: - Mod* FindMod(const std::string& modName); + //! Returns the reference to the mod in given position + const Mod& GetMod(size_t i) const; + + //! Returns the list of mods + const std::vector& GetMods() const; private: CApplication* m_app; diff --git a/src/common/event.cpp b/src/common/event.cpp index 3de67fc1..5bae84f2 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -190,6 +190,7 @@ void InitializeEventTypeTexts() EVENT_TYPE_TEXT[EVENT_INTERFACE_ABORT] = "EVENT_INTERFACE_ABORT"; EVENT_TYPE_TEXT[EVENT_INTERFACE_USER] = "EVENT_INTERFACE_USER"; EVENT_TYPE_TEXT[EVENT_INTERFACE_SATCOM] = "EVENT_INTERFACE_SATCOM"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS] = "EVENT_INTERFACE_MODS"; EVENT_TYPE_TEXT[EVENT_INTERFACE_CHAP] = "EVENT_INTERFACE_CHAP"; EVENT_TYPE_TEXT[EVENT_INTERFACE_LIST] = "EVENT_INTERFACE_LIST"; @@ -201,7 +202,6 @@ void InitializeEventTypeTexts() EVENT_TYPE_TEXT[EVENT_INTERFACE_SETUPp] = "EVENT_INTERFACE_SETUPp"; EVENT_TYPE_TEXT[EVENT_INTERFACE_SETUPc] = "EVENT_INTERFACE_SETUPc"; EVENT_TYPE_TEXT[EVENT_INTERFACE_SETUPs] = "EVENT_INTERFACE_SETUPs"; - EVENT_TYPE_TEXT[EVENT_INTERFACE_SETUPm] = "EVENT_INTERFACE_SETUPm"; EVENT_TYPE_TEXT[EVENT_INTERFACE_DEVICE] = "EVENT_INTERFACE_DEVICE"; EVENT_TYPE_TEXT[EVENT_INTERFACE_RESOL] = "EVENT_INTERFACE_RESOL"; @@ -273,12 +273,15 @@ void InitializeEventTypeTexts() EVENT_TYPE_TEXT[EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT]= "EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT"; EVENT_TYPE_TEXT[EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT]= "EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT"; - EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_UNLOADED] = "EVENT_INTERFACE_MODS_UNLOADED"; - EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_LOADED] = "EVENT_INTERFACE_MODS_LOADED"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_LIST] = "EVENT_INTERFACE_MOD_LIST"; EVENT_TYPE_TEXT[EVENT_INTERFACE_WORKSHOP] = "EVENT_INTERFACE_WORKSHOP"; EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_DIR] = "EVENT_INTERFACE_MODS_DIR"; - EVENT_TYPE_TEXT[EVENT_INTERFACE_LOAD] = "EVENT_INTERFACE_LOAD"; - EVENT_TYPE_TEXT[EVENT_INTERFACE_UNLOAD] = "EVENT_INTERFACE_UNLOAD"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE] = "EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_APPLY] = "EVENT_INTERFACE_MODS_APPLY"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_DETAILS] = "EVENT_INTERFACE_MOD_DETAILS"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_MOVE_UP] = "EVENT_INTERFACE_MOD_MOVE_UP"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MOD_MOVE_DOWN] = "EVENT_INTERFACE_MOD_MOVE_DOWN"; + EVENT_TYPE_TEXT[EVENT_INTERFACE_MODS_REFRESH] = "EVENT_INTERFACE_MODS_REFRESH"; EVENT_TYPE_TEXT[EVENT_INTERFACE_GLINTl] = "EVENT_INTERFACE_GLINTl"; EVENT_TYPE_TEXT[EVENT_INTERFACE_GLINTr] = "EVENT_INTERFACE_GLINTr"; diff --git a/src/common/event.h b/src/common/event.h index 91d79439..d55c9664 100644 --- a/src/common/event.h +++ b/src/common/event.h @@ -225,6 +225,7 @@ enum EventType EVENT_INTERFACE_ABORT = 412, EVENT_INTERFACE_USER = 413, EVENT_INTERFACE_SATCOM = 414, + EVENT_INTERFACE_MODS = 416, EVENT_INTERFACE_CHAP = 420, EVENT_INTERFACE_LIST = 421, @@ -236,7 +237,6 @@ enum EventType EVENT_INTERFACE_SETUPp = 432, EVENT_INTERFACE_SETUPc = 433, EVENT_INTERFACE_SETUPs = 434, - EVENT_INTERFACE_SETUPm = 435, EVENT_INTERFACE_DEVICE = 440, EVENT_INTERFACE_RESOL = 441, @@ -312,12 +312,16 @@ enum EventType EVENT_INTERFACE_JOYSTICK_CAM_Y_INVERT = 573, EVENT_INTERFACE_JOYSTICK_CAM_Z_INVERT = 574, - EVENT_INTERFACE_MODS_UNLOADED = 580, - EVENT_INTERFACE_MODS_LOADED = 581, - EVENT_INTERFACE_WORKSHOP = 582, - EVENT_INTERFACE_MODS_DIR = 583, - EVENT_INTERFACE_LOAD = 584, - EVENT_INTERFACE_UNLOAD = 585, + EVENT_INTERFACE_MOD_LIST = 580, + EVENT_INTERFACE_WORKSHOP = 581, + EVENT_INTERFACE_MODS_DIR = 582, + EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE = 583, + EVENT_INTERFACE_MODS_APPLY = 584, + EVENT_INTERFACE_MOD_SUMMARY = 585, + EVENT_INTERFACE_MOD_DETAILS = 586, + EVENT_INTERFACE_MOD_MOVE_UP = 587, + EVENT_INTERFACE_MOD_MOVE_DOWN = 888, + EVENT_INTERFACE_MODS_REFRESH = 589, EVENT_INTERFACE_GLINTl = 590, EVENT_INTERFACE_GLINTr = 591, diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 11270e46..664b8ac3 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -71,12 +71,13 @@ void InitializeRestext() stringsText[RT_TITLE_MISSION] = TR("Missions"); stringsText[RT_TITLE_FREE] = TR("Free game"); stringsText[RT_TITLE_USER] = TR("User levels"); - stringsText[RT_TITLE_CODE_BATTLES]=TR("Code battles"); + stringsText[RT_TITLE_CODE_BATTLES] = TR("Code battles"); stringsText[RT_TITLE_SETUP] = TR("Options"); stringsText[RT_TITLE_NAME] = TR("Player's name"); stringsText[RT_TITLE_PERSO] = TR("Customize your appearance"); stringsText[RT_TITLE_WRITE] = TR("Save the current mission"); stringsText[RT_TITLE_READ] = TR("Load a saved mission"); + stringsText[RT_TITLE_MODS] = TR("Mods"); stringsText[RT_PLAY_CHAP_CHAPTERS] = TR("Chapters:"); stringsText[RT_PLAY_CHAP_PLANETS] = TR("Planets:"); @@ -92,8 +93,11 @@ void InitializeRestext() stringsText[RT_SETUP_KEY1] = TR("1) First click on the key you want to redefine."); stringsText[RT_SETUP_KEY2] = TR("2) Then press the key you want to use instead."); - stringsText[RT_MODS_UNLOADED] = TR("Unloaded Mods:"); - stringsText[RT_MODS_LOADED] = TR("Loaded Mods:"); + stringsText[RT_MOD_LIST] = TR("Mods:"); + stringsText[RT_MOD_DETAILS] = TR("Information:"); + stringsText[RT_MOD_SUMMARY] = TR("Description:"); + stringsText[RT_MOD_ENABLE] = TR("Enable\\Enable the selected mod"); + stringsText[RT_MOD_DISABLE] = TR("Disable\\Disable the selected mod"); stringsText[RT_PERSO_FACE] = TR("Face type:"); stringsText[RT_PERSO_GLASSES] = TR("Eyeglasses:"); @@ -115,6 +119,7 @@ void InitializeRestext() stringsText[RT_DIALOG_OPEN_PATH_FAILED_TEXT] = TR("The path %s could not be opened in a file explorer."); stringsText[RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE] = TR("Could not open the web browser!"); stringsText[RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT] = TR("The address %s could not be opened in a web browser."); + stringsText[RT_DIALOG_CHANGES_QUESTION] = TR("There are unsaved changes. Do you want to save them before leaving?"); stringsText[RT_STUDIO_LISTTT] = TR("Keyword help(\\key cbot;)"); stringsText[RT_STUDIO_COMPOK] = TR("Compilation ok (0 errors)"); @@ -180,6 +185,7 @@ void InitializeRestext() stringsEvent[EVENT_INTERFACE_CODE_BATTLES] = TR("Code battles\\Program your robot to be the best of them all!"); stringsEvent[EVENT_INTERFACE_USER] = TR("Custom levels\\Levels from mods created by the users"); stringsEvent[EVENT_INTERFACE_SATCOM] = TR("SatCom"); + stringsEvent[EVENT_INTERFACE_MODS] = TR("Mods\\Mod manager"); stringsEvent[EVENT_INTERFACE_NAME] = TR("Change player\\Change player"); stringsEvent[EVENT_INTERFACE_SETUP] = TR("Options\\Preferences"); stringsEvent[EVENT_INTERFACE_AGAIN] = TR("Restart\\Restart the mission from the beginning"); @@ -189,16 +195,17 @@ void InitializeRestext() stringsEvent[EVENT_INTERFACE_QUIT] = TR("Quit\\Quit Colobot: Gold Edition"); stringsEvent[EVENT_INTERFACE_BACK] = TR("<< Back \\Back to the previous screen"); stringsEvent[EVENT_INTERFACE_PLAY] = TR("Play\\Start mission!"); - stringsEvent[EVENT_INTERFACE_WORKSHOP] = TR("Workshop\\Open Workshop to search Mods"); - stringsEvent[EVENT_INTERFACE_MODS_DIR] = TR("Open Directory\\Open Mods directory"); - stringsEvent[EVENT_INTERFACE_LOAD] = TR("Load\\Load Mod"); - stringsEvent[EVENT_INTERFACE_UNLOAD] = TR("Unload\\Unload Mod"); + stringsEvent[EVENT_INTERFACE_WORKSHOP] = TR("Workshop\\Open the workshop to search for mods"); + stringsEvent[EVENT_INTERFACE_MODS_DIR] = TR("Open Directory\\Open the mods directory"); + stringsEvent[EVENT_INTERFACE_MODS_APPLY] = TR("Apply\\Apply the current mod configuration"); + stringsEvent[EVENT_INTERFACE_MOD_MOVE_UP] = TR("Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)"); + stringsEvent[EVENT_INTERFACE_MOD_MOVE_DOWN] = TR("Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)"); + stringsEvent[EVENT_INTERFACE_MODS_REFRESH] = TR("Refresh\\Refresh the list of currently installed mods"); stringsEvent[EVENT_INTERFACE_SETUPd] = TR("Device\\Driver and resolution settings"); stringsEvent[EVENT_INTERFACE_SETUPg] = TR("Graphics\\Graphics settings"); stringsEvent[EVENT_INTERFACE_SETUPp] = TR("Game\\Game settings"); stringsEvent[EVENT_INTERFACE_SETUPc] = TR("Controls\\Keyboard, joystick and mouse settings"); stringsEvent[EVENT_INTERFACE_SETUPs] = TR("Sound\\Music and game sound volume"); - stringsEvent[EVENT_INTERFACE_SETUPm] = TR("Mods\\Manage installed mods"); stringsEvent[EVENT_INTERFACE_DEVICE] = TR("Unit"); stringsEvent[EVENT_INTERFACE_RESOL] = TR("Resolution"); stringsEvent[EVENT_INTERFACE_FULL] = TR("Full screen\\Full screen or window mode"); diff --git a/src/common/restext.h b/src/common/restext.h index 9edf4105..241b6c38 100644 --- a/src/common/restext.h +++ b/src/common/restext.h @@ -71,6 +71,7 @@ enum ResTextType RT_TITLE_WRITE = 50, RT_TITLE_READ = 51, RT_TITLE_USER = 52, + RT_TITLE_MODS = 54, RT_PLAY_CHAP_CHAPTERS = 60, RT_PLAY_CHAP_PLANETS = 61, @@ -86,9 +87,6 @@ enum ResTextType RT_SETUP_KEY1 = 82, RT_SETUP_KEY2 = 83, - RT_MODS_UNLOADED = 85, - RT_MODS_LOADED = 86, - RT_PERSO_FACE = 90, RT_PERSO_GLASSES = 91, RT_PERSO_HAIR = 92, @@ -109,6 +107,7 @@ enum ResTextType RT_DIALOG_OPEN_PATH_FAILED_TEXT = 114, RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE = 115, RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT = 116, + RT_DIALOG_CHANGES_QUESTION = 117, RT_STUDIO_LISTTT = 120, RT_STUDIO_COMPOK = 121, @@ -154,6 +153,11 @@ enum ResTextType RT_SCOREBOARD_RESULTS_TIME= 232, RT_SCOREBOARD_RESULTS_LINE= 233, + RT_MOD_LIST = 234, + RT_MOD_DETAILS = 235, + RT_MOD_SUMMARY = 236, + RT_MOD_ENABLE = 237, + RT_MOD_DISABLE = 238, RT_MAX //! < number of values }; diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index b9e3633f..7092b32b 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -320,19 +320,18 @@ std::string PhaseToString(Phase phase) if (phase == PHASE_APPERANCE) return "PHASE_APPERANCE"; if (phase == PHASE_MAIN_MENU) return "PHASE_MAIN_MENU"; if (phase == PHASE_LEVEL_LIST) return "PHASE_LEVEL_LIST"; + if (phase == PHASE_MOD_LIST) return "PHASE_MOD_LIST"; if (phase == PHASE_SIMUL) return "PHASE_SIMUL"; if (phase == PHASE_SETUPd) return "PHASE_SETUPd"; if (phase == PHASE_SETUPg) return "PHASE_SETUPg"; if (phase == PHASE_SETUPp) return "PHASE_SETUPp"; if (phase == PHASE_SETUPc) return "PHASE_SETUPc"; if (phase == PHASE_SETUPs) return "PHASE_SETUPs"; - if (phase == PHASE_SETUPm) return "PHASE_SETUPm"; if (phase == PHASE_SETUPds) return "PHASE_SETUPds"; if (phase == PHASE_SETUPgs) return "PHASE_SETUPgs"; if (phase == PHASE_SETUPps) return "PHASE_SETUPps"; if (phase == PHASE_SETUPcs) return "PHASE_SETUPcs"; if (phase == PHASE_SETUPss) return "PHASE_SETUPss"; - if (phase == PHASE_SETUPms) return "PHASE_SETUPms"; if (phase == PHASE_WRITEs) return "PHASE_WRITEs"; if (phase == PHASE_READ) return "PHASE_READ"; if (phase == PHASE_READs) return "PHASE_READs"; @@ -345,7 +344,7 @@ std::string PhaseToString(Phase phase) bool IsInSimulationConfigPhase(Phase phase) { - return (phase >= PHASE_SETUPds && phase <= PHASE_SETUPms) || phase == PHASE_READs || phase == PHASE_WRITEs; + return (phase >= PHASE_SETUPds && phase <= PHASE_SETUPss) || phase == PHASE_READs || phase == PHASE_WRITEs; } bool IsPhaseWithWorld(Phase phase) @@ -3913,7 +3912,7 @@ void CRobotMain::ChangeColor() m_phase != PHASE_SETUPps && m_phase != PHASE_SETUPcs && m_phase != PHASE_SETUPss && - m_phase != PHASE_SETUPms && + m_phase != PHASE_MOD_LIST && m_phase != PHASE_WIN && m_phase != PHASE_LOST && m_phase != PHASE_APPERANCE ) return; diff --git a/src/level/robotmain.h b/src/level/robotmain.h index b339cfdc..731d7a1a 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -55,19 +55,18 @@ enum Phase PHASE_APPERANCE, PHASE_MAIN_MENU, PHASE_LEVEL_LIST, + PHASE_MOD_LIST, PHASE_SIMUL, PHASE_SETUPd, PHASE_SETUPg, PHASE_SETUPp, PHASE_SETUPc, PHASE_SETUPs, - PHASE_SETUPm, PHASE_SETUPds, PHASE_SETUPgs, PHASE_SETUPps, PHASE_SETUPcs, PHASE_SETUPss, - PHASE_SETUPms, PHASE_WRITEs, PHASE_READ, PHASE_READs, diff --git a/src/ui/maindialog.cpp b/src/ui/maindialog.cpp index b21070fb..5cdfdd47 100644 --- a/src/ui/maindialog.cpp +++ b/src/ui/maindialog.cpp @@ -107,7 +107,6 @@ bool CMainDialog::EventProcess(const Event &event) if ( CScreenSetup::GetTab() == PHASE_SETUPp ) m_main->ChangePhase(PHASE_SETUPps); if ( CScreenSetup::GetTab() == PHASE_SETUPc ) m_main->ChangePhase(PHASE_SETUPcs); if ( CScreenSetup::GetTab() == PHASE_SETUPs ) m_main->ChangePhase(PHASE_SETUPss); - if ( CScreenSetup::GetTab() == PHASE_SETUPm ) m_main->ChangePhase(PHASE_SETUPss); } if ( pressedButton == EVENT_INTERFACE_WRITE ) diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp index 57436f89..daca36c7 100644 --- a/src/ui/mainui.cpp +++ b/src/ui/mainui.cpp @@ -49,13 +49,13 @@ #include "ui/screen/screen_level_list.h" #include "ui/screen/screen_loading.h" #include "ui/screen/screen_main_menu.h" +#include "ui/screen/screen_mod_list.h" #include "ui/screen/screen_player_select.h" #include "ui/screen/screen_quit.h" #include "ui/screen/screen_setup_controls.h" #include "ui/screen/screen_setup_display.h" #include "ui/screen/screen_setup_game.h" #include "ui/screen/screen_setup_graphics.h" -#include "ui/screen/screen_setup_mods.h" #include "ui/screen/screen_setup_sound.h" #include "ui/screen/screen_welcome.h" @@ -81,11 +81,11 @@ CMainUserInterface::CMainUserInterface() m_screenIORead = MakeUnique(m_screenLevelList.get()); m_screenIOWrite = MakeUnique(m_screenLevelList.get()); m_screenLoading = MakeUnique(); + m_screenModList = MakeUnique(m_dialog.get(), m_app->GetModManager()); m_screenSetupControls = MakeUnique(); m_screenSetupDisplay = MakeUnique(); m_screenSetupGame = MakeUnique(); m_screenSetupGraphics = MakeUnique(); - m_screenSetupMods = MakeUnique(m_dialog.get(), m_app->GetModManager()); m_screenSetupSound = MakeUnique(); m_screenMainMenu = MakeUnique(); m_screenPlayerSelect = MakeUnique(m_dialog.get()); @@ -146,7 +146,6 @@ CScreenSetup* CMainUserInterface::GetSetupScreen(Phase phase) if(phase == PHASE_SETUPp) return m_screenSetupGame.get(); if(phase == PHASE_SETUPc) return m_screenSetupControls.get(); if(phase == PHASE_SETUPs) return m_screenSetupSound.get(); - if(phase == PHASE_SETUPm) return m_screenSetupMods.get(); assert(false); return nullptr; } @@ -187,14 +186,18 @@ void CMainUserInterface::ChangePhase(Phase phase) m_screenLevelList->SetLevelCategory(m_main->GetLevelCategory()); m_currentScreen = m_screenLevelList.get(); } - if (m_phase >= PHASE_SETUPd && m_phase <= PHASE_SETUPm) + if (m_phase == PHASE_MOD_LIST) + { + m_currentScreen = m_screenModList.get(); + } + if (m_phase >= PHASE_SETUPd && m_phase <= PHASE_SETUPs) { CScreenSetup* screenSetup = GetSetupScreen(m_phase); screenSetup->SetInSimulation(false); screenSetup->SetActive(); m_currentScreen = screenSetup; } - if (m_phase >= PHASE_SETUPds && m_phase <= PHASE_SETUPms) + if (m_phase >= PHASE_SETUPds && m_phase <= PHASE_SETUPss) { CScreenSetup* screenSetup = GetSetupScreen(static_cast(m_phase - PHASE_SETUPds + PHASE_SETUPd)); screenSetup->SetInSimulation(true); @@ -350,7 +353,6 @@ void CMainUserInterface::GlintMove() m_phase == PHASE_SETUPp || m_phase == PHASE_SETUPc || m_phase == PHASE_SETUPs || - m_phase == PHASE_SETUPm || m_phase == PHASE_SETUPds || m_phase == PHASE_SETUPgs || m_phase == PHASE_SETUPps || @@ -535,12 +537,12 @@ void CMainUserInterface::FrameParticle(float rTime) } else if ( m_phase == PHASE_PLAYER_SELECT || m_phase == PHASE_LEVEL_LIST || + m_phase == PHASE_MOD_LIST || m_phase == PHASE_SETUPd || m_phase == PHASE_SETUPg || m_phase == PHASE_SETUPp || m_phase == PHASE_SETUPc || m_phase == PHASE_SETUPs || - m_phase == PHASE_SETUPm || m_phase == PHASE_READ ) { pParti = partiPosBig; diff --git a/src/ui/mainui.h b/src/ui/mainui.h index 76440a40..2cae1d18 100644 --- a/src/ui/mainui.h +++ b/src/ui/mainui.h @@ -50,6 +50,7 @@ class CScreenIOWrite; class CScreenLevelList; class CScreenLoading; class CScreenMainMenu; +class CScreenModList; class CScreenPlayerSelect; class CScreenQuit; class CScreenSetup; @@ -57,7 +58,6 @@ class CScreenSetupControls; class CScreenSetupDisplay; class CScreenSetupGame; class CScreenSetupGraphics; -class CScreenSetupMods; class CScreenSetupSound; class CScreenWelcome; @@ -115,13 +115,13 @@ protected: std::unique_ptr m_screenLevelList; std::unique_ptr m_screenLoading; std::unique_ptr m_screenMainMenu; + std::unique_ptr m_screenModList; std::unique_ptr m_screenPlayerSelect; std::unique_ptr m_screenQuit; std::unique_ptr m_screenSetupControls; std::unique_ptr m_screenSetupDisplay; std::unique_ptr m_screenSetupGame; std::unique_ptr m_screenSetupGraphics; - std::unique_ptr m_screenSetupMods; std::unique_ptr m_screenSetupSound; std::unique_ptr m_screenWelcome; diff --git a/src/ui/screen/screen_main_menu.cpp b/src/ui/screen/screen_main_menu.cpp index 2d69ae1c..30c771a0 100644 --- a/src/ui/screen/screen_main_menu.cpp +++ b/src/ui/screen/screen_main_menu.cpp @@ -170,6 +170,13 @@ void CScreenMainMenu::CreateInterface() pb = pw->CreateButton(pos, ddim, 128+60, EVENT_INTERFACE_SATCOM); pb->SetState(STATE_SHADOW); + // Mods button + pos.x = 447.0f/640.0f; + pos.y = 313.0f/480.0f; + ddim.x = 0.09f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MODS); + pb->SetState(STATE_SHADOW); + SetBackground("textures/interface/interface.png"); CreateVersionDisplay(); } @@ -235,6 +242,9 @@ bool CScreenMainMenu::EventProcess(const Event &event) m_main->ChangePhase(PHASE_SATCOM); break; + case EVENT_INTERFACE_MODS: + m_main->ChangePhase(PHASE_MOD_LIST); + default: return true; } diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp new file mode 100644 index 00000000..b8558f81 --- /dev/null +++ b/src/ui/screen/screen_mod_list.cpp @@ -0,0 +1,490 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#include "ui/screen/screen_mod_list.h" + +#include "common/config.h" + +#include "app/app.h" +#include "app/modman.h" + +#include "common/logger.h" +#include "common/restext.h" +#include "common/stringutils.h" + +#include "common/resources/resourcemanager.h" + +#include "common/system/system.h" + +#include "math/func.h" + +#include "ui/controls/button.h" +#include "ui/controls/edit.h" +#include "ui/controls/interface.h" +#include "ui/controls/label.h" +#include "ui/controls/list.h" +#include "ui/controls/window.h" + +namespace Ui +{ + +CScreenModList::CScreenModList(CMainDialog* dialog, CModManager* modManager) + : m_dialog(dialog), + m_modManager(modManager) +{ +} + +void CScreenModList::CreateInterface() +{ + CWindow* pw; + CEdit* pe; + CLabel* pl; + CButton* pb; + CList* pli; + Math::Point pos, ddim; + std::string name; + + m_changes = false; + ApplyChanges(); + + // Display the window + pos.x = 0.10f; + pos.y = 0.10f; + ddim.x = 0.80f; + ddim.y = 0.80f; + pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5); + pw->SetClosable(true); + GetResource(RES_TEXT, RT_TITLE_MODS, name); + pw->SetName(name); + + pos.x = 0.10f; + pos.y = 0.40f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // orange corner + pos.x = 0.40f; + pos.y = 0.10f; + ddim.x = 0.50f; + ddim.y = 0.50f; + pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // blue corner + + // Display the list of mods + pos.x = ox+sx*3; + pos.y = oy+sy*10.5f; + ddim.x = dim.x*7.5f; + ddim.y = dim.y*0.6f; + GetResource(RES_TEXT, RT_MOD_LIST, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, name); + pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); + + pos.y = oy+sy*6.7f; + ddim.y = dim.y*4.5f; + ddim.x = dim.x*6.5f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_MOD_LIST); + pli->SetState(STATE_SHADOW); + pli->SetState(STATE_EXTEND); + + UpdateModList(); + + // Displays the mod details + pos.x = ox+sx*9.5f; + pos.y = oy+sy*10.5f; + ddim.x = dim.x*7.5f; + ddim.y = dim.y*0.6f; + GetResource(RES_TEXT, RT_MOD_DETAILS, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, name); + pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); + + pos.y = oy+sy*6.7f; + ddim.y = dim.y*4.5f; + ddim.x = dim.x*6.5f; + pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_LIST); + pli->SetState(STATE_SHADOW); + + UpdateModDetails(); + + pos = pli->GetPos(); + ddim = pli->GetDim(); + + // Displays the mod summary + pos.x = ox+sx*3; + pos.y = oy+sy*5.4f; + ddim.x = dim.x*6.5f; + ddim.y = dim.y*0.6f; + GetResource(RES_TEXT, RT_MOD_SUMMARY, name); + pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL13, name); + pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); + + pos.x = ox+sx*3; + pos.y = oy+sy*3.6f; + ddim.x = dim.x*13.4f; + ddim.y = dim.y*1.9f; + pe = pw->CreateEdit(pos, ddim, 0, EVENT_INTERFACE_MOD_SUMMARY); + pe->SetState(STATE_SHADOW); + pe->SetMaxChar(500); + pe->SetEditCap(false); // just to see + pe->SetHighlightCap(false); + + UpdateModSummary(); + + // Apply button + pos.x = ox+sx*13.75f; + pos.y = oy+sy*2; + ddim.x = dim.x*2.0f; + ddim.y = dim.y*1; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MODS_APPLY); + pb->SetState(STATE_SHADOW); + + UpdateApplyButton(); + + // Display the enable/disable button + pos.x -= dim.x*2.3f; + ddim.x = dim.x*2.0f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE); + pb->SetState(STATE_SHADOW); + + UpdateEnableDisableButton(); + + // Display the move up button + pos.x -= dim.x*1.3f; + ddim.x = dim.x*1; + pb = pw->CreateButton(pos, ddim, 49, EVENT_INTERFACE_MOD_MOVE_UP); + pb->SetState(STATE_SHADOW); + + // Display the move down button + pos.x -= dim.x*1.3f; + pb = pw->CreateButton(pos, ddim, 50, EVENT_INTERFACE_MOD_MOVE_DOWN); + pb->SetState(STATE_SHADOW); + + UpdateUpDownButtons(); + + // Display the refresh button + pos.x -= dim.x*1.3f; + pb = pw->CreateButton(pos, ddim, 87, EVENT_INTERFACE_MODS_REFRESH); + pb->SetState(STATE_SHADOW); + + // Display the open website button + pos.x -= dim.x*1.3f; + pb = pw->CreateButton(pos, ddim, 40, EVENT_INTERFACE_WORKSHOP); + pb->SetState(STATE_SHADOW); + + // Display the open directory button + pos.x -= dim.x*1.3f; + pb = pw->CreateButton(pos, ddim, 57, EVENT_INTERFACE_MODS_DIR); + pb->SetState(STATE_SHADOW); + + // Back button + pos.x = ox+sx*3; + ddim.x = dim.x*2.3f; + pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK); + pb->SetState(STATE_SHADOW); + + // Background + SetBackground("textures/interface/interface.png"); + CreateVersionDisplay(); +} + +bool CScreenModList::EventProcess(const Event &event) +{ + CWindow* pw; + CList* pl; + + const std::string workshopUrl = "https://www.moddb.com/games/colobot-gold-edition"; + const std::string modDir = CResourceManager::GetSaveLocation() + "/mods"; + + auto systemUtils = CSystemUtils::Create(); // platform-specific utils + + Mod const * mod; + + pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return false; + + if (event.type == pw->GetEventTypeClose() || + event.type == EVENT_INTERFACE_BACK || + (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(ESCAPE))) + { + if (m_changes) + { + m_dialog->StartQuestion(RT_DIALOG_CHANGES_QUESTION, true, true, false, + [this]() + { + ApplyChanges(); + CloseWindow(); + }, + [this]() + { + m_changes = false; // do not save changes on "No" + ApplyChanges(); + CloseWindow(); + }); + } + else + { + ApplyChanges(); + CloseWindow(); + } + return false; + } + + switch( event.type ) + { + case EVENT_INTERFACE_MOD_LIST: + pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_LIST)); + if (pl == nullptr) break; + m_modSelectedIndex = pl->GetSelect(); + UpdateModSummary(); + UpdateModDetails(); + UpdateEnableDisableButton(); + UpdateUpDownButtons(); + break; + + case EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE: + mod = &m_modManager->GetMod(m_modSelectedIndex); + if (mod->enabled) + { + m_modManager->DisableMod(m_modSelectedIndex); + } + else + { + m_modManager->EnableMod(m_modSelectedIndex); + } + UpdateModList(); + UpdateEnableDisableButton(); + m_changes = true; + UpdateApplyButton(); + break; + + case EVENT_INTERFACE_MOD_MOVE_UP: + m_modSelectedIndex = m_modManager->MoveUp(m_modSelectedIndex); + UpdateModList(); + UpdateUpDownButtons(); + m_changes = true; + UpdateApplyButton(); + break; + + case EVENT_INTERFACE_MOD_MOVE_DOWN: + m_modSelectedIndex = m_modManager->MoveDown(m_modSelectedIndex); + UpdateModList(); + UpdateUpDownButtons(); + m_changes = true; + UpdateApplyButton(); + break; + + case EVENT_INTERFACE_MODS_REFRESH: + case EVENT_INTERFACE_MODS_APPLY: + ApplyChanges(); + // Update the whole UI + UpdateModList(); + UpdateModSummary(); + UpdateModDetails(); + UpdateEnableDisableButton(); + UpdateApplyButton(); + UpdateUpDownButtons(); + break; + + case EVENT_INTERFACE_MODS_DIR: + if (!systemUtils->OpenPath(modDir)) + { + std::string title, text; + GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TITLE, title); + GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TEXT, text); + + // Workaround for Windows: the label skips everything after the first \\ character + std::string modDirWithoutBackSlashes = modDir; + std::replace(modDirWithoutBackSlashes.begin(), modDirWithoutBackSlashes.end(), '\\', '/'); + + m_dialog->StartInformation(title, title, StrUtils::Format(text.c_str(), modDirWithoutBackSlashes.c_str())); + } + break; + + case EVENT_INTERFACE_WORKSHOP: + if (!systemUtils->OpenWebsite(workshopUrl)) + { + std::string title, text; + GetResource(RES_TEXT, RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE, title); + GetResource(RES_TEXT, RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT, text); + m_dialog->StartInformation(title, title, StrUtils::Format(text.c_str(), workshopUrl.c_str())); + } + break; + + default: + return true; + } + return false; +} + +void CScreenModList::ApplyChanges() +{ + if (m_changes) + { + m_changes = false; + m_modManager->SaveMods(); + } + + m_modManager->FindMods(); + m_modManager->SaveMods(); + + m_empty = (m_modManager->CountMods() == 0); + + m_modManager->UpdatePaths(); + m_modManager->ReloadResources(); + + m_modSelectedIndex = Math::Clamp(m_modSelectedIndex, static_cast(0), m_modManager->CountMods() - 1); +} + +void CScreenModList::CloseWindow() +{ + m_main->ChangePhase(PHASE_MAIN_MENU); +} + +void CScreenModList::UpdateModList() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CList* pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_LIST)); + if (pl == nullptr) return; + + pl->Flush(); + + if (m_empty) + { + return; + } + + auto mods = m_modManager->GetMods(); + for (size_t i = 0; i < mods.size(); ++i) + { + const auto& mod = mods[i]; + pl->SetItemName(i, mod.name); + pl->SetCheck(i, mod.enabled); + pl->SetEnable(i, true); + } + + pl->SetSelect(m_modSelectedIndex); + pl->ShowSelect(false); +} + +void CScreenModList::UpdateModDetails() +{ + //TODO +} + +void CScreenModList::UpdateModSummary() +{ + //TODO +} + +void CScreenModList::UpdateEnableDisableButton() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE)); + if (pb == nullptr) return; + + std::string buttonName{}; + + if (m_empty) + { + pb->ClearState(STATE_ENABLE); + + // Set some default name + GetResource(RES_TEXT, RT_MOD_ENABLE, buttonName); + pb->SetName(buttonName); + + return; + } + + const auto& mod = m_modManager->GetMod(m_modSelectedIndex); + + if (mod.enabled) + { + GetResource(RES_TEXT, RT_MOD_DISABLE, buttonName); + pb->SetName(buttonName); + } + else + { + GetResource(RES_TEXT, RT_MOD_ENABLE, buttonName); + pb->SetName(buttonName); + } +} + +void CScreenModList::UpdateApplyButton() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_APPLY)); + if (pb == nullptr) return; + + if (m_empty) + { + pb->ClearState(STATE_ENABLE); + return; + } + + if (m_changes) + { + pb->SetState(STATE_ENABLE); + } + else + { + pb->ClearState(STATE_ENABLE); + } +} + +void CScreenModList::UpdateUpDownButtons() +{ + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CButton* pb_up = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_MOVE_UP)); + if (pb_up == nullptr) return; + + CButton* pb_down = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_MOVE_DOWN)); + if (pb_down == nullptr) return; + + if (m_empty) + { + pb_up->ClearState(STATE_ENABLE); + pb_down->ClearState(STATE_ENABLE); + return; + } + + if (m_modSelectedIndex == 0) + { + pb_up->ClearState(STATE_ENABLE); + } + else + { + pb_up->SetState(STATE_ENABLE); + } + + if (m_modSelectedIndex >= m_modManager->CountMods() - 1) + { + pb_down->ClearState(STATE_ENABLE); + } + else + { + pb_down->SetState(STATE_ENABLE); + } +} + +} // namespace Ui diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h new file mode 100644 index 00000000..ac039bee --- /dev/null +++ b/src/ui/screen/screen_mod_list.h @@ -0,0 +1,96 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#pragma once + +#include "app/modman.h" + +#include "ui/screen/screen.h" + +#include "level/level_category.h" + +#include +#include + +namespace Ui +{ +class CMainDialog; + +/** + * \class CScreenModList + * \brief This class is the front-end for the \ref CModManager. + * + * \section Assumptions Assumptions + * + * It assumes the user is changing something in the mods folders while the screen is visible, + * e.g. removing them or adding more. For this reason, the mods are always reloaded after the user + * lefts the screen, possibly after asking the user if their changes should be saved. They are also reloaded + * when the user opens the screen to avoid weird situations like "the mod is no longer there but in theory + * it's still in the game's memory even if it's not visible on the list". + * + * Unsafe changes, like removing a mod which is still enabled, are a sole responsibility of the user and + * we basically can't guarantee the game will behave properly in such cases even when they happen while + * this screen is visible. + * + * \section Features UI Features + * + * The user can reorder mods with appropriate buttons and enable/disable them. To avoid reloading + * the resources after every change, the changes are not immediate. The resources are reloaded in the + * cases described above and also after using the Apply or Refresh buttons. The only technical + * difference between them is that the Refresh button is always enabled, but Apply is only enabled + * if the user made any changes in the list by using the UI. The reason is, again, to avoid dealing with + * weird situations like described above. + * + * The UI also shows the selected mod metadata like description, version, etc. + * + * There is also a button which will try to open the default web browser with the Workshop website, + * where the user can search for new mods. + * + * For convenience, also a button opening a saves/mods folder is provided. + */ +class CScreenModList : public CScreen +{ +public: + CScreenModList(CMainDialog* dialog, CModManager* modManager); + + void CreateInterface() override; + bool EventProcess(const Event &event) override; + +protected: + void ApplyChanges(); + void CloseWindow(); + + void UpdateModList(); + void UpdateModDetails(); + void UpdateModSummary(); + void UpdateEnableDisableButton(); + void UpdateApplyButton(); + void UpdateUpDownButtons(); + +protected: + Ui::CMainDialog* m_dialog; + + CModManager* m_modManager; + + size_t m_modSelectedIndex = 0; + bool m_changes = false; + bool m_empty = true; +}; + +} // namespace Ui diff --git a/src/ui/screen/screen_setup.cpp b/src/ui/screen/screen_setup.cpp index 7a83ae1e..67848640 100644 --- a/src/ui/screen/screen_setup.cpp +++ b/src/ui/screen/screen_setup.cpp @@ -83,7 +83,7 @@ void CScreenSetup::CreateInterface() ddim.y = 0.05f; pw->CreateGroup(pos, ddim, 3, EVENT_NULL); // transparent -> gray - ddim.x = 0.65f/5-0.01f; + ddim.x = 0.78f/5-0.01f; ddim.y = 0.06f; pos.x = 0.115f; pos.y = 0.76f; @@ -116,12 +116,6 @@ void CScreenSetup::CreateInterface() pb->SetState(STATE_CARD); pb->SetState(STATE_CHECK, (m_tab == PHASE_SETUPs)); - pos.x += ddim.x+0.01f; - pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_SETUPm); - pb->SetState(STATE_SHADOW); - pb->SetState(STATE_CARD); - pb->SetState(STATE_CHECK, (m_tab == PHASE_SETUPm)); - pos.x = 0.10f; ddim.x = 0.80f; pos.y = 0.34f; @@ -154,10 +148,6 @@ bool CScreenSetup::EventProcess(const Event &event) CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return false; - CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_SETUPm)); - if ( pb == nullptr ) return false; - pb->SetState(STATE_ENABLE); - if ( event.type == pw->GetEventTypeClose() || event.type == EVENT_INTERFACE_BACK || (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(ESCAPE)) ) @@ -189,10 +179,6 @@ bool CScreenSetup::EventProcess(const Event &event) m_main->ChangePhase(PHASE_SETUPs); return false; - case EVENT_INTERFACE_SETUPm: - m_main->ChangePhase(PHASE_SETUPm); - return false; - default: break; } @@ -202,10 +188,6 @@ bool CScreenSetup::EventProcess(const Event &event) CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return false; - CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_SETUPm)); - if ( pb == nullptr ) return false; - pb->ClearState(STATE_ENABLE); - if ( event.type == pw->GetEventTypeClose() || event.type == EVENT_INTERFACE_BACK || (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(ESCAPE)) ) @@ -239,9 +221,6 @@ bool CScreenSetup::EventProcess(const Event &event) m_main->ChangePhase(PHASE_SETUPss); return false; - case EVENT_INTERFACE_SETUPm: - assert(false); // should never get here - default: break; } diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp deleted file mode 100644 index 69cd13fe..00000000 --- a/src/ui/screen/screen_setup_mods.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsitec.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - -#include "ui/screen/screen_setup_mods.h" - -#include "common/config.h" - -#include "app/app.h" -#include "app/modman.h" - -#include "common/logger.h" -#include "common/restext.h" -#include "common/settings.h" -#include "common/stringutils.h" - -#include "common/resources/resourcemanager.h" - -#include "common/system/system.h" - -#include "level/parser/parser.h" - -#include "ui/controls/button.h" -#include "ui/controls/edit.h" -#include "ui/controls/interface.h" -#include "ui/controls/label.h" -#include "ui/controls/list.h" -#include "ui/controls/window.h" - -#include -#include -#include - -using namespace boost::filesystem; - -namespace Ui -{ - -CScreenSetupMods::CScreenSetupMods(CMainDialog* dialog, CModManager* modManager) - : m_dialog(dialog), - m_modManager(modManager) -{ -} - -void CScreenSetupMods::SetActive() -{ - m_tab = PHASE_SETUPm; -} - -void CScreenSetupMods::CreateInterface() -{ - CWindow* pw; - CLabel* pl; - CButton* pb; - CList* pli; - Math::Point pos, ddim; - std::string name; - - m_modManager->FindMods(); - - CScreenSetup::CreateInterface(); - pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); - if ( pw == nullptr ) return; - - // Displays a list of unloaded mods: - pos.x = ox+sx*3; - pos.y = oy+sy*9; - ddim.x = dim.x*6; - ddim.y = dim.y*1; - GetResource(RES_TEXT, RT_MODS_UNLOADED, name); - pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, name); - pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); - - pos.y = oy+sy*3.75f; - ddim.x = dim.x*6.5f; - ddim.y = dim.y*6.05f; - pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_MODS_UNLOADED); - pli->SetState(STATE_SHADOW); - UpdateUnloadedModList(); - - // Displays a list of loaded mods: - pos.x = ox+sx*9.5f; - pos.y = oy+sy*9; - ddim.x = dim.x*6; - ddim.y = dim.y*1; - GetResource(RES_TEXT, RT_MODS_LOADED, name); - pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, name); - pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); - - pos.y = oy+sy*3.75f; - ddim.x = dim.x*6.5f; - ddim.y = dim.y*6.05f; - pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_MODS_LOADED); - pli->SetState(STATE_SHADOW); - UpdateLoadedModList(); - - pos = pli->GetPos(); - ddim = pli->GetDim(); - pos.x = ox+sx*8.2f; - pos.y = oy+sy*2; - ddim.x = dim.x*1; - ddim.y = dim.y*1; - pb = pw->CreateButton(pos, ddim, 40, EVENT_INTERFACE_WORKSHOP); - pb->SetState(STATE_SHADOW); - - pos.x += dim.x*1.3f; - pb = pw->CreateButton(pos, ddim, 57, EVENT_INTERFACE_MODS_DIR); - pb->SetState(STATE_SHADOW); - - pos.x += dim.x*1.3f; - ddim.x = dim.x*2.5f; - pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_LOAD); - pb->SetState(STATE_SHADOW); - pb->ClearState(STATE_ENABLE); - - pos.x += dim.x*2.8f; - pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_UNLOAD); - pb->SetState(STATE_SHADOW); - pb->ClearState(STATE_ENABLE); -} - -bool CScreenSetupMods::EventProcess(const Event &event) -{ - CWindow* pw; - CButton* pb; - CList* pl; - std::string modName; - const std::string website = "https://www.moddb.com/games/colobot-gold-edition"; - const std::string modDir = CResourceManager::GetSaveLocation() + "/mods"; - auto systemUtils = CSystemUtils::Create(); // platform-specific utils - - if (!CScreenSetup::EventProcess(event)) return false; - - pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); - if ( pw == nullptr ) return false; - - switch (event.type) - { - case EVENT_INTERFACE_LOAD: - pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); - if (pl == nullptr) return false; - modName = pl->GetItemName(pl->GetSelect()); - - m_modManager->EnableMod(modName); - m_modManager->SaveMods(); - m_modManager->UpdatePaths(); - m_modManager->ReloadResources(); - - m_main->ChangePhase(PHASE_SETUPm); - break; - - case EVENT_INTERFACE_UNLOAD: - pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); - if (pl == nullptr) return false; - modName = pl->GetItemName(pl->GetSelect()); - - m_modManager->DisableMod(modName); - m_modManager->SaveMods(); - m_modManager->UpdatePaths(); - m_modManager->ReloadResources(); - - m_main->ChangePhase(PHASE_SETUPm); - break; - - case EVENT_INTERFACE_MODS_UNLOADED: - pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); - if ( pl == nullptr ) break; - - pb = static_cast(pw->SearchControl(EVENT_INTERFACE_UNLOAD)); - if ( pb == nullptr ) break; - pl->SetSelect(-1); - pb->ClearState(STATE_ENABLE); - - pb = static_cast(pw->SearchControl(EVENT_INTERFACE_LOAD)); - if ( pb == nullptr ) break; - pb->SetState(STATE_ENABLE); - break; - - case EVENT_INTERFACE_MODS_LOADED: - pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); - if ( pl == nullptr ) break; - - pb = static_cast(pw->SearchControl(EVENT_INTERFACE_LOAD)); - if ( pb == nullptr ) break; - pl->SetSelect(-1); - pb->ClearState(STATE_ENABLE); - - pb = static_cast(pw->SearchControl(EVENT_INTERFACE_UNLOAD)); - if ( pb == nullptr ) break; - pb->SetState(STATE_ENABLE); - break; - - case EVENT_INTERFACE_MODS_DIR: - if (!systemUtils->OpenPath(modDir)) - { - std::string title, text; - GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TITLE, title); - GetResource(RES_TEXT, RT_DIALOG_OPEN_PATH_FAILED_TEXT, text); - - // Workaround for Windows: the label skips everything after the first \\ character - std::string modDirWithoutBackSlashes = modDir; - std::replace(modDirWithoutBackSlashes.begin(), modDirWithoutBackSlashes.end(), '\\', '/'); - - m_dialog->StartInformation(title, title, StrUtils::Format(text.c_str(), modDirWithoutBackSlashes.c_str())); - } - break; - - case EVENT_INTERFACE_WORKSHOP: - if (!systemUtils->OpenWebsite(website)) - { - std::string title, text; - GetResource(RES_TEXT, RT_DIALOG_OPEN_WEBSITE_FAILED_TITLE, title); - GetResource(RES_TEXT, RT_DIALOG_OPEN_WEBSITE_FAILED_TEXT, text); - m_dialog->StartInformation(title, title, StrUtils::Format(text.c_str(), website.c_str())); - } - break; - - default: - return true; - } - return false; -} - -void CScreenSetupMods::UpdateUnloadedModList() -{ - CWindow* pw; - CList* pl; - int i = 0; - directory_iterator end_itr; - - pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); - if ( pw == nullptr ) return; - - pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_UNLOADED)); - if ( pl == nullptr ) return; - pl->Flush(); - - for (const auto& mod : m_modManager->GetMods()) - { - if (!mod.enabled) - { - pl->SetItemName(i++, mod.name); - } - } - - pl->ShowSelect(false); // shows the selected columns -} - -void CScreenSetupMods::UpdateLoadedModList() -{ - CWindow* pw; - CList* pl; - int i = 0; - directory_iterator end_itr; - - pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); - if ( pw == nullptr ) return; - - pl = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_LOADED)); - if ( pl == nullptr ) return; - pl->Flush(); - - for (const auto& mod : m_modManager->GetMods()) - { - if (mod.enabled) - { - pl->SetItemName(i++, mod.name); - } - } - - pl->ShowSelect(false); // shows the selected columns -} - -} // namespace Ui diff --git a/src/ui/screen/screen_setup_mods.h b/src/ui/screen/screen_setup_mods.h deleted file mode 100644 index 9098d7d7..00000000 --- a/src/ui/screen/screen_setup_mods.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsitec.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - -#pragma once - -#include "ui/maindialog.h" - -#include "ui/screen/screen_setup.h" - -#include - -class CModManager; - -namespace Ui -{ - -class CScreenSetupMods : public CScreenSetup -{ -public: - CScreenSetupMods(CMainDialog* dialog, CModManager* modManager); - void SetActive() override; - - void CreateInterface() override; - bool EventProcess(const Event &event) override; - -protected: - void UpdateUnloadedModList(); - void UpdateLoadedModList(); - -protected: - CMainDialog* m_dialog; - - CModManager* m_modManager; -}; - -} // namespace Ui From eac74c23ec743fea825041f869f0b25f33b2826c Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Tue, 21 Jul 2020 20:39:00 +0200 Subject: [PATCH 16/29] Fix linter issues --- src/app/modman.cpp | 1 - src/ui/screen/screen_mod_list.h | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/app/modman.cpp b/src/app/modman.cpp index cf552641..06c2b430 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -33,7 +33,6 @@ #include #include #include -#include "modman.h" using namespace boost::filesystem; diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h index ac039bee..e75d4baa 100644 --- a/src/ui/screen/screen_mod_list.h +++ b/src/ui/screen/screen_mod_list.h @@ -23,11 +23,6 @@ #include "ui/screen/screen.h" -#include "level/level_category.h" - -#include -#include - namespace Ui { class CMainDialog; From f57da76ae8d71e4e70225af9b89db6d4852ec303 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 22 Jul 2020 16:18:18 +0200 Subject: [PATCH 17/29] Add handling of mods manifests Also add Polish translations for mod manager related strings. --- CMakeLists.txt | 2 +- po/colobot.pot | 45 ++++++++---- po/cs.po | 17 +++++ po/de.po | 17 +++++ po/fr.po | 17 +++++ po/pl.po | 46 +++++++----- po/pt.po | 17 +++++ po/ru.po | 17 +++++ src/CMakeLists.txt | 2 + src/app/app.cpp | 4 +- src/app/moddata.cpp | 114 +++++++++++++++++++++++++++++ src/app/moddata.h | 36 ++++++++++ src/app/modman.cpp | 27 ++++++- src/app/modman.h | 35 ++++----- src/app/pathman.cpp | 4 +- src/common/restext.cpp | 17 +++-- src/common/restext.h | 15 ++-- src/ui/screen/screen_mod_list.cpp | 115 ++++++++++++++++++++++++++---- src/ui/screen/screen_mod_list.h | 5 +- 19 files changed, 468 insertions(+), 84 deletions(-) create mode 100644 src/app/moddata.cpp create mode 100644 src/app/moddata.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 94c3a574..90843488 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,7 +189,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(NORMAL_CXX_FLAGS "/wd\"4244\" /wd\"4309\" /wd\"4800\" /wd\"4996\" /wd\"4351\" /EHsc") # disable some useless warnings if(MSVC_STATIC) set(RELEASE_CXX_FLAGS "/MT /Ox") - set(DEBUG_CXX_FLAGS "/MTd /ZI") + set(DEBUG_CXX_FLAGS "/MTd /Od /ZI") else(MSVC_STATIC) set(RELEASE_CXX_FLAGS "/MD /Ox") set(DEBUG_CXX_FLAGS "/MDd /Od /ZI") diff --git a/po/colobot.pot b/po/colobot.pot index 53bfda71..b459a8f3 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -123,21 +123,6 @@ msgstr "" msgid "2) Then press the key you want to use instead." msgstr "" -msgid "Mods:" -msgstr "" - -msgid "Information:" -msgstr "" - -msgid "Description:" -msgstr "" - -msgid "Enable\\Enable the selected mod" -msgstr "" - -msgid "Disable\\Disable the selected mod" -msgstr "" - msgid "Face type:" msgstr "" @@ -315,6 +300,36 @@ msgstr "" msgid "%s: %d pts" msgstr "" +msgid "Mods:" +msgstr "" + +msgid "Information:" +msgstr "" + +msgid "Description:" +msgstr "" + +msgid "Enable\\Enable the selected mod" +msgstr "" + +msgid "Disable\\Disable the selected mod" +msgstr "" + +msgid "Unknown author" +msgstr "" + +msgid "by" +msgstr "" + +msgid "Version" +msgstr "" + +msgid "Website" +msgstr "" + +msgid "No description." +msgstr "" + msgid "Code battle" msgstr "" diff --git a/po/cs.po b/po/cs.po index 532510ea..6fb3aa3e 100644 --- a/po/cs.po +++ b/po/cs.po @@ -988,6 +988,10 @@ msgstr "Další objekt\\Vybere následující objekt" msgid "No" msgstr "Ne" +#, fuzzy +msgid "No description." +msgstr "Rozlišení:" + msgid "No energy in the subsoil" msgstr "Pod povrchem není zdroj energie" @@ -1735,6 +1739,10 @@ msgstr "Jednotka" msgid "Unknown Object" msgstr "Neznámý objekt" +#, fuzzy +msgid "Unknown author" +msgstr "Neznámá funkce" + msgid "Unknown command" msgstr "Neznámý příkaz" @@ -1771,6 +1779,9 @@ msgstr "Proměnná nebyla nastavena" msgid "Vault" msgstr "Trezor" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "" @@ -1789,6 +1800,9 @@ msgstr "Vosa byla smrtelně raněna" msgid "Waste" msgstr "Odpad" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -1975,6 +1989,9 @@ msgstr "\\Fialové vlajky" msgid "\\Yellow flags" msgstr "\\Žluté vlajky" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" diff --git a/po/de.po b/po/de.po index 15f4c336..23b581f4 100644 --- a/po/de.po +++ b/po/de.po @@ -1004,6 +1004,10 @@ msgstr "Nächstes auswählen\\Nächstes Objekt auswählen" msgid "No" msgstr "Nein" +#, fuzzy +msgid "No description." +msgstr "Auflösung:" + msgid "No energy in the subsoil" msgstr "Kein unterirdisches Energievorkommen" @@ -1752,6 +1756,10 @@ msgstr "Einheit" msgid "Unknown Object" msgstr "Das Objekt existiert nicht" +#, fuzzy +msgid "Unknown author" +msgstr "Unbekannte Funktion" + msgid "Unknown command" msgstr "Befehl unbekannt" @@ -1788,6 +1796,9 @@ msgstr "Der Wert dieser Variable wurde nicht definiert" msgid "Vault" msgstr "Bunker" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "" @@ -1806,6 +1817,9 @@ msgstr "Wespe tödlich verwundet" msgid "Waste" msgstr "Abfall" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -1990,6 +2004,9 @@ msgstr "\\Violette Fahne" msgid "\\Yellow flags" msgstr "\\Gelbe Fahne" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" diff --git a/po/fr.po b/po/fr.po index 1b07f1fe..6c13748d 100644 --- a/po/fr.po +++ b/po/fr.po @@ -1006,6 +1006,10 @@ msgstr "Sélectionner l'objet suivant\\Sélectionner l'objet suivant" msgid "No" msgstr "Non" +#, fuzzy +msgid "No description." +msgstr "Résolutions :" + msgid "No energy in the subsoil" msgstr "Pas d'énergie en sous-sol" @@ -1754,6 +1758,10 @@ msgstr "Unité" msgid "Unknown Object" msgstr "Objet inconnu" +#, fuzzy +msgid "Unknown author" +msgstr "Routine inconnue" + msgid "Unknown command" msgstr "Commande inconnue" @@ -1790,6 +1798,9 @@ msgstr "Variable non initialisée" msgid "Vault" msgstr "Coffre-fort" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "Synchronisation verticale :\\Réduit la fréquence d'images par seconde à afficher." @@ -1808,6 +1819,9 @@ msgstr "Guêpe mortellement touchée" msgid "Waste" msgstr "Déchet" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -1992,6 +2006,9 @@ msgstr "\\Drapeaux violets" msgid "\\Yellow flags" msgstr "\\Drapeaux jaunes" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" diff --git a/po/pl.po b/po/pl.po index 251de7fe..9f00c679 100644 --- a/po/pl.po +++ b/po/pl.po @@ -125,7 +125,7 @@ msgid "Apply changes\\Activates the changed settings" msgstr "Zastosuj zmiany\\Aktywuje zmienione ustawienia" msgid "Apply\\Apply the current mod configuration" -msgstr "" +msgstr "Zastosuj\\Zastosuj obecną konfigurację modów" msgid "Appropriate constructor missing" msgstr "Brak odpowiedniego konstruktora" @@ -481,9 +481,8 @@ msgstr "Kopalnia" msgid "Descend\\Reduces the power of the jet" msgstr "W dół\\Zmniejsza moc silnika" -#, fuzzy msgid "Description:" -msgstr "Rozdzielczość:" +msgstr "Opis:" msgid "Destroy" msgstr "Zniszcz" @@ -497,9 +496,8 @@ msgstr "Destroyer" msgid "Device\\Driver and resolution settings" msgstr "Urządzenie\\Ustawienia sterownika i rozdzielczości" -#, fuzzy msgid "Disable\\Disable the selected mod" -msgstr "Usuń\\Usuwa zaznaczony plik" +msgstr "Wyłącz\\Wyłącza zaznaczonego moda" msgid "Dividing by zero" msgstr "Dzielenie przez zero" @@ -518,7 +516,7 @@ msgid "Down (\\key gdown;)" msgstr "Dół (\\key gdown;)" msgid "Down\\Move the selected mod down so it's loaded later (mods may overwrite files from the mods above them)" -msgstr "" +msgstr "W dół\\Przenieś zaznaczonego moda w dół, aby był załadowany później (mody mogą nadpisywać pliki modów wyżej)" msgid "Drawer bot" msgstr "Robot rysownik" @@ -547,9 +545,8 @@ msgstr "Jajo" msgid "Empty character constant" msgstr "" -#, fuzzy msgid "Enable\\Enable the selected mod" -msgstr "Wczytaj\\Wczytuje zaznaczoną misję" +msgstr "Włącz\\Włącza zaznaczonego moda" msgid "End of block missing" msgstr "Brak końca bloku" @@ -778,7 +775,7 @@ msgid "Information exchange post" msgstr "Stacja przekaźnikowa informacji" msgid "Information:" -msgstr "" +msgstr "Informacje:" msgid "Instruction \"break\" outside a loop" msgstr "Polecenie \"break\" na zewnątrz pętli" @@ -931,13 +928,13 @@ msgid "Missions\\Select mission" msgstr "Misje\\Wybierz misję" msgid "Mods" -msgstr "" +msgstr "Mody" msgid "Mods:" -msgstr "" +msgstr "Mody:" msgid "Mods\\Mod manager" -msgstr "" +msgstr "Mody\\Zarządzanie modami" msgid "Mouse inversion X\\Inversion of the scrolling direction on the X axis" msgstr "Odwrócenie myszy X\\Odwrócenie kierunków przewijania w poziomie" @@ -987,6 +984,9 @@ msgstr "Następny obiekt\\Zaznacza następny obiekt" msgid "No" msgstr "Nie" +msgid "No description." +msgstr "Brak opisu." + msgid "No energy in the subsoil" msgstr "Brak energii w ziemi" @@ -1108,7 +1108,7 @@ msgid "Open (Ctrl+O)" msgstr "Otwórz (Ctrl+O)" msgid "Open Directory\\Open the mods directory" -msgstr "" +msgstr "Otwórz katalog\\Otwórz katalog z modami" msgid "Opening brace missing" msgstr "Brak klamry otwierającej" @@ -1321,7 +1321,7 @@ msgid "Reflections on the buttons \\Shiny buttons" msgstr "Odbicia na przyciskach \\Świecące przyciski" msgid "Refresh\\Refresh the list of currently installed mods" -msgstr "" +msgstr "Odśwież\\Odśwież listę obecnie zainstalowanych modów" msgid "Remains of Apollo mission" msgstr "Pozostałości z misji Apollo" @@ -1608,7 +1608,7 @@ msgid "The types of the two operands are incompatible" msgstr "Niezgodne typy operatorów" msgid "There are unsaved changes. Do you want to save them before leaving?" -msgstr "" +msgstr "Są niezapisane zmiany. Czy chcesz je zapisać przed wyjściem?" msgid "This class already exists" msgstr "Taka klasa już istnieje" @@ -1734,6 +1734,9 @@ msgstr "Jednostka" msgid "Unknown Object" msgstr "Obiekt nieznany" +msgid "Unknown author" +msgstr "Nieznany autor" + msgid "Unknown command" msgstr "Nieznane polecenie" @@ -1747,7 +1750,7 @@ msgid "Up (\\key gup;)" msgstr "Góra (\\key gup;)" msgid "Up\\Move the selected mod up so it's loaded sooner (mods may overwrite files from the mods above them)" -msgstr "" +msgstr "W górę\\Przenieś zaznaczonego moda w górę, aby był załadowany wcześniej (mody mogą nadpisywać pliki modów wyżej)" msgid "Uranium deposit (site for derrick)" msgstr "Złoże uranu (miejsce na kopalnię)" @@ -1770,6 +1773,9 @@ msgstr "Zmienna nie została zainicjalizowana" msgid "Vault" msgstr "Skrytka" +msgid "Version" +msgstr "Wersja" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "Synchronizacja pionowa\\Ogranicza ilość klatek na sekundę do wartości odświeżania ekranu" @@ -1788,6 +1794,9 @@ msgstr "Osa śmiertelnie raniona" msgid "Waste" msgstr "Odpady" +msgid "Website" +msgstr "Strona internetowa" + msgid "Wheeled builder" msgstr "" @@ -1822,7 +1831,7 @@ msgid "Withdraw shield (\\key action;)" msgstr "Wyłącz osłonę (\\key action;)" msgid "Workshop\\Open the workshop to search for mods" -msgstr "" +msgstr "Warsztat\\Otwórz warsztat, aby poszukać modów" msgid "Worm" msgstr "Robal" @@ -1974,6 +1983,9 @@ msgstr "\\Fioletowe flagi" msgid "\\Yellow flags" msgstr "\\Żółte flagi" +msgid "by" +msgstr "autorstwa" + msgid "colobot.info" msgstr "colobot.info" diff --git a/po/pt.po b/po/pt.po index aed2d681..05285903 100644 --- a/po/pt.po +++ b/po/pt.po @@ -1001,6 +1001,10 @@ msgstr "Próximo objeto\\Selecionar o próximo objeto" msgid "No" msgstr "Não" +#, fuzzy +msgid "No description." +msgstr "Resolução:" + msgid "No energy in the subsoil" msgstr "Nenhuma energia no subsolo" @@ -1749,6 +1753,10 @@ msgstr "Unidade" msgid "Unknown Object" msgstr "Objeto desconhecido" +#, fuzzy +msgid "Unknown author" +msgstr "Função desconhecida" + msgid "Unknown command" msgstr "Comando desconhecido" @@ -1785,6 +1793,9 @@ msgstr "Variável não inicializada" msgid "Vault" msgstr "Cofre" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "" @@ -1803,6 +1814,9 @@ msgstr "Vespa fatalmente ferida" msgid "Waste" msgstr "Desperdício" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -1987,6 +2001,9 @@ msgstr "\\Bandeiras violetas" msgid "\\Yellow flags" msgstr "\\Bandeiras amarelas" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" diff --git a/po/ru.po b/po/ru.po index d450ccf4..2a4dccaa 100644 --- a/po/ru.po +++ b/po/ru.po @@ -1012,6 +1012,10 @@ msgstr "Следующий объект\\Выбор следующего объ msgid "No" msgstr "Нет" +#, fuzzy +msgid "No description." +msgstr "Разрешение:" + msgid "No energy in the subsoil" msgstr "Под землей нет запасов энергии" @@ -1765,6 +1769,10 @@ msgstr "Юнит" msgid "Unknown Object" msgstr "Неизвестный объект" +#, fuzzy +msgid "Unknown author" +msgstr "Неизвестная функция" + msgid "Unknown command" msgstr "Неизвестная команда" @@ -1801,6 +1809,9 @@ msgstr "Переменная не инициализирована" msgid "Vault" msgstr "Хранилище" +msgid "Version" +msgstr "" + msgid "Vertical Synchronization\\Limits the number of frames per second to display frequency" msgstr "" @@ -1819,6 +1830,9 @@ msgstr "Оса смертельно ранена" msgid "Waste" msgstr "Мусор" +msgid "Website" +msgstr "" + msgid "Wheeled builder" msgstr "" @@ -2003,6 +2017,9 @@ msgstr "\\Фиолетовый флаг" msgid "\\Yellow flags" msgstr "\\Желтый флаг" +msgid "by" +msgstr "" + msgid "colobot.info" msgstr "colobot.info" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index df752615..f00e77a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,6 +145,8 @@ set(BASE_SOURCES app/controller.h app/input.cpp app/input.h + app/moddata.cpp + app/moddata.h app/modman.cpp app/modman.h app/pathman.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index 54a14d07..72c2c2e1 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -520,9 +520,7 @@ bool CApplication::Create() GetLogger()->Warn("Config could not be loaded. Default values will be used!\n"); } - m_modManager->FindMods(); - m_modManager->SaveMods(); - m_modManager->UpdatePaths(); + m_modManager->Init(); // Create the sound instance. #ifdef OPENAL_SOUND diff --git a/src/app/moddata.cpp b/src/app/moddata.cpp new file mode 100644 index 00000000..3ff716d7 --- /dev/null +++ b/src/app/moddata.cpp @@ -0,0 +1,114 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#include "app/moddata.h" + +#include "common/logger.h" +#include "common/make_unique.h" + +#include "common/resources/resourcemanager.h" +#include "common/resources/inputstream.h" + +#include +#include +#include + +namespace pt = boost::property_tree; + +boost::optional LoadManifest(const std::string& path, bool loaded = false); +std::string GetStringProperty(const pt::ptree& manifest, const std::string& key); +std::unordered_map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key); + +ModData LoadModData(const std::string& path, bool loaded) +{ + ModData modData{}; + + auto manifestOptional = LoadManifest(path); + if (!manifestOptional.has_value()) + { + return modData; + } + auto manifest = manifestOptional.get(); + + modData.displayName = GetLanguageStringProperty(manifest, "DisplayName"); + modData.version = GetStringProperty(manifest, "Version"); + modData.author = GetStringProperty(manifest, "Author"); + modData.website = GetStringProperty(manifest, "Website"); + modData.summary = GetLanguageStringProperty(manifest, "Summary"); + + return modData; +} + +boost::optional LoadManifest(const std::string& path, bool loaded) +{ + try + { + auto inputStream = MakeUnique("manifest.json"); + if (!inputStream->is_open()) + { + GetLogger()->Error("Error on parsing the manifest file %s: failed to open file\n"); + return {}; + } + + pt::ptree manifest{}; + boost::property_tree::json_parser::read_json(*inputStream, manifest); + + return manifest; + } + catch (std::exception& e) + { + GetLogger()->Warn("Error on parsing the manifest file %s: %s\n", path.c_str(), e.what()); + return {}; + } + return {}; +} + +std::string GetStringProperty(const pt::ptree& manifest, const std::string& key) +{ + auto prop = manifest.get_optional(key); + if (prop.has_value()) + { + return prop.get(); + } + return {}; +} + +std::unordered_map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key) +{ + std::unordered_map ret; + auto prop = manifest.get_child_optional(key); + if (prop.has_value()) + { + for (const auto& child : prop.get()) + { + Language language = LANGUAGE_ENGLISH; + std::string strLanguage = child.first.data(); + if (!ParseLanguage(strLanguage, language)) + { + GetLogger()->Warn("Error on parsing the manifest file: %s language %s is not a valid language\n", key.c_str(), strLanguage.c_str()); + continue; + } + else + { + ret.insert(std::make_pair(language, child.second.data())); + } + } + } + return ret; +} diff --git a/src/app/moddata.h b/src/app/moddata.h new file mode 100644 index 00000000..90f3887c --- /dev/null +++ b/src/app/moddata.h @@ -0,0 +1,36 @@ +/* +* This file is part of the Colobot: Gold Edition source code +* Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam +* http://epsitec.ch; http://colobot.info; http://github.com/colobot +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see http://gnu.org/licenses +*/ + +#pragma once + +#include "common/language.h" + +#include + +struct ModData +{ + std::unordered_map displayName{}; + std::string author{}; + std::string version{}; + std::string website{}; + std::unordered_map summary{}; +}; + +//! Loads the metadata for a mod in the given path. +ModData LoadModData(const std::string& path, bool loaded = false); diff --git a/src/app/modman.cpp b/src/app/modman.cpp index 06c2b430..a8dcf7d3 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -42,10 +42,8 @@ CModManager::CModManager(CApplication* app, CPathManager* pathManager) { } -void CModManager::FindMods() +void CModManager::Init() { - m_mods.clear(); - // Load names from the config file std::vector savedModNames; GetConfigFile().GetArrayProperty("Mods", "Names", savedModNames); @@ -102,6 +100,29 @@ void CModManager::FindMods() mod.path = newMod.second; m_mods.push_back(mod); } + + SaveMods(); + + // Load the metadata for each mod + for (auto& mod : m_mods) + { + m_pathManager->AddMod(mod.path); + mod.data = LoadModData(mod.path); + m_pathManager->RemoveMod(mod.path); + } + + UpdatePaths(); +} + +void CModManager::ReloadMods() +{ + m_mods.clear(); + m_pathManager->RemoveAllMods(); + + Init(); + + // Apply the configuration + ReloadResources(); } void CModManager::EnableMod(size_t i) diff --git a/src/app/modman.h b/src/app/modman.h index 984d54ea..a937ec20 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -19,13 +19,12 @@ #pragma once -#include "ui/maindialog.h" - -#include "ui/screen/screen_setup.h" +#include "app/moddata.h" #include #include +class CApplication; class CPathManager; struct Mod @@ -33,7 +32,7 @@ struct Mod std::string name{}; std::string path{}; bool enabled = false; - //TODO: add metadata for UI + ModData data{}; }; /** @@ -43,20 +42,22 @@ struct Mod * The order matters since the order in which files are loaded matters, * because some files can be overwritten. * - * The list can be kept in the config file with the \ref SaveMods function. - * - * The changes in the list do not immediately apply. - * Separate calls to \ref UpdatePaths and \ref ReloadResources, probably in this order, - * need to be done for the changes to apply. + * The changes in the list do not immediately apply + * and will be lost after the \ref ReloadMods call if they were not + * saved beforehand with \ref SaveMods. * + * The changes are also lost to mods which are no longer found in the search paths. */ class CModManager { public: CModManager(CApplication* app, CPathManager* pathManager); + //! Loads mods without resource reloading; should be called only once after creation + void Init(); + //! Finds all the mods along with their metadata - void FindMods(); + void ReloadMods(); //! Removes a mod from the list of loaded mods void EnableMod(size_t i); @@ -70,15 +71,9 @@ public: //! Moves the selected mod down in the list so that it's loaded later than others, returns the new index size_t MoveDown(size_t i); - //! Reloads application resources so the enabled mods are applied - void ReloadResources(); - //! Saves the current configuration of mods to the config file void SaveMods(); - //! Updates the paths in Path Manager according to the current mod configuration - void UpdatePaths(); - //! Number of mods loaded size_t CountMods() const; @@ -88,10 +83,16 @@ public: //! Returns the list of mods const std::vector& GetMods() const; +private: + //! Updates the paths in Path Manager according to the current mod configuration + void UpdatePaths(); + + //! Reloads application resources so the enabled mods are applied + void ReloadResources(); + private: CApplication* m_app; CPathManager* m_pathManager; - //TODO: better data structure? std::vector m_mods; }; diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index 080c9b70..e353ec1a 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -71,14 +71,14 @@ void CPathManager::AddModSearchDir(const std::string &modSearchDirPath) void CPathManager::AddMod(const std::string &modPath) { - GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); + GetLogger()->Debug("Adding mod: '%s'\n", modPath.c_str()); CResourceManager::AddLocation(modPath, true); m_mods.push_back(modPath); } void CPathManager::RemoveMod(const std::string &modPath) { - GetLogger()->Info("Unloading mod: '%s'\n", modPath.c_str()); + GetLogger()->Debug("Removing mod: '%s'\n", modPath.c_str()); CResourceManager::RemoveLocation(modPath); auto it = std::find(m_mods.cbegin(), m_mods.cend(), modPath); if (it != m_mods.cend()) diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 664b8ac3..294aa1eb 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -93,12 +93,6 @@ void InitializeRestext() stringsText[RT_SETUP_KEY1] = TR("1) First click on the key you want to redefine."); stringsText[RT_SETUP_KEY2] = TR("2) Then press the key you want to use instead."); - stringsText[RT_MOD_LIST] = TR("Mods:"); - stringsText[RT_MOD_DETAILS] = TR("Information:"); - stringsText[RT_MOD_SUMMARY] = TR("Description:"); - stringsText[RT_MOD_ENABLE] = TR("Enable\\Enable the selected mod"); - stringsText[RT_MOD_DISABLE] = TR("Disable\\Disable the selected mod"); - stringsText[RT_PERSO_FACE] = TR("Face type:"); stringsText[RT_PERSO_GLASSES] = TR("Eyeglasses:"); stringsText[RT_PERSO_HAIR] = TR("Hair color:"); @@ -165,7 +159,16 @@ void InitializeRestext() stringsText[RT_SCOREBOARD_RESULTS_TIME]= TR("Time: %s"); stringsText[RT_SCOREBOARD_RESULTS_LINE]= TR("%s: %d pts"); - + stringsText[RT_MOD_LIST] = TR("Mods:"); + stringsText[RT_MOD_DETAILS] = TR("Information:"); + stringsText[RT_MOD_SUMMARY] = TR("Description:"); + stringsText[RT_MOD_ENABLE] = TR("Enable\\Enable the selected mod"); + stringsText[RT_MOD_DISABLE] = TR("Disable\\Disable the selected mod"); + stringsText[RT_MOD_UNKNOWN_AUTHOR] = TR("Unknown author"); + stringsText[RT_MOD_AUTHOR_FIELD_NAME] = TR("by"); + stringsText[RT_MOD_VERSION_FIELD_NAME] = TR("Version"); + stringsText[RT_MOD_WEBSITE_FIELD_NAME] = TR("Website"); + stringsText[RT_MOD_NO_SUMMARY] = TR("No description."); stringsEvent[EVENT_LABEL_CODE_BATTLE] = TR("Code battle"); diff --git a/src/common/restext.h b/src/common/restext.h index 241b6c38..5031d069 100644 --- a/src/common/restext.h +++ b/src/common/restext.h @@ -153,11 +153,16 @@ enum ResTextType RT_SCOREBOARD_RESULTS_TIME= 232, RT_SCOREBOARD_RESULTS_LINE= 233, - RT_MOD_LIST = 234, - RT_MOD_DETAILS = 235, - RT_MOD_SUMMARY = 236, - RT_MOD_ENABLE = 237, - RT_MOD_DISABLE = 238, + RT_MOD_LIST = 234, + RT_MOD_DETAILS = 235, + RT_MOD_SUMMARY = 236, + RT_MOD_ENABLE = 237, + RT_MOD_DISABLE = 238, + RT_MOD_UNKNOWN_AUTHOR = 239, + RT_MOD_AUTHOR_FIELD_NAME = 240, + RT_MOD_VERSION_FIELD_NAME = 241, + RT_MOD_WEBSITE_FIELD_NAME = 242, + RT_MOD_NO_SUMMARY = 243, RT_MAX //! < number of values }; diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index b8558f81..a216218b 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -32,6 +32,8 @@ #include "common/system/system.h" +#include "level/robotmain.h" + #include "math/func.h" #include "ui/controls/button.h" @@ -94,7 +96,7 @@ void CScreenModList::CreateInterface() pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); pos.y = oy+sy*6.7f; - ddim.y = dim.y*4.5f; + ddim.y = dim.y*4.6f; ddim.x = dim.x*6.5f; pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_MOD_LIST); pli->SetState(STATE_SHADOW); @@ -112,10 +114,13 @@ void CScreenModList::CreateInterface() pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); pos.y = oy+sy*6.7f; - ddim.y = dim.y*4.5f; + ddim.y = dim.y*4.3f; ddim.x = dim.x*6.5f; - pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_LIST); - pli->SetState(STATE_SHADOW); + pe = pw->CreateEdit(pos, ddim, 0, EVENT_INTERFACE_MOD_DETAILS); + pe->SetState(STATE_SHADOW); + pe->SetMaxChar(500); + pe->SetEditCap(false); // just to see + pe->SetHighlightCap(true); UpdateModDetails(); @@ -139,7 +144,7 @@ void CScreenModList::CreateInterface() pe->SetState(STATE_SHADOW); pe->SetMaxChar(500); pe->SetEditCap(false); // just to see - pe->SetHighlightCap(false); + pe->SetHighlightCap(true); UpdateModSummary(); @@ -337,14 +342,10 @@ void CScreenModList::ApplyChanges() m_modManager->SaveMods(); } - m_modManager->FindMods(); - m_modManager->SaveMods(); + m_modManager->ReloadMods(); m_empty = (m_modManager->CountMods() == 0); - m_modManager->UpdatePaths(); - m_modManager->ReloadResources(); - m_modSelectedIndex = Math::Clamp(m_modSelectedIndex, static_cast(0), m_modManager->CountMods() - 1); } @@ -372,7 +373,8 @@ void CScreenModList::UpdateModList() for (size_t i = 0; i < mods.size(); ++i) { const auto& mod = mods[i]; - pl->SetItemName(i, mod.name); + auto name = GetLanguageStringProperty(mod.data.displayName, mod.name); + pl->SetItemName(i, name); pl->SetCheck(i, mod.enabled); pl->SetEnable(i, true); } @@ -383,12 +385,80 @@ void CScreenModList::UpdateModList() void CScreenModList::UpdateModDetails() { - //TODO + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CEdit* pe = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_DETAILS)); + if (pe == nullptr) return; + + if (m_empty) + { + pe->SetText("No information"); + return; + } + + std::string details{}; + + const auto& mod = m_modManager->GetMod(m_modSelectedIndex); + const auto data = mod.data; + + auto name = GetLanguageStringProperty(data.displayName, mod.name); + details += "\\b;" + name + '\n'; + + std::string authorFieldName; + GetResource(RES_TEXT, RT_MOD_AUTHOR_FIELD_NAME, authorFieldName); + details += "\\s;" + authorFieldName + " "; + if (!data.author.empty()) + { + details += data.author; + } + else + { + std::string unknownAuthor; + GetResource(RES_TEXT, RT_MOD_UNKNOWN_AUTHOR, unknownAuthor); + details += unknownAuthor; + } + details += '\n'; + + details += '\n'; + + if (!data.version.empty()) + { + std::string versionFieldName; + GetResource(RES_TEXT, RT_MOD_VERSION_FIELD_NAME, versionFieldName); + details += "\\t;" + versionFieldName + '\n' + data.version + '\n'; + } + + if (!data.website.empty()) + { + std::string websiteFieldName; + GetResource(RES_TEXT, RT_MOD_WEBSITE_FIELD_NAME, websiteFieldName); + details += "\\t;" + websiteFieldName + '\n' + data.website + '\n'; + } + + pe->SetText(details); } void CScreenModList::UpdateModSummary() { - //TODO + CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); + if (pw == nullptr) return; + + CEdit* pe = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_SUMMARY)); + if (pe == nullptr) return; + + std::string noSummary; + GetResource(RES_TEXT, RT_MOD_NO_SUMMARY, noSummary); + + if (m_empty) + { + pe->SetText(noSummary); + return; + } + + const auto& mod = m_modManager->GetMod(m_modSelectedIndex); + + pe->SetText(GetLanguageStringProperty(mod.data.summary, noSummary)); } void CScreenModList::UpdateEnableDisableButton() @@ -487,4 +557,23 @@ void CScreenModList::UpdateUpDownButtons() } } +std::string CScreenModList::GetLanguageStringProperty(std::unordered_map property, const std::string& fallback) +{ + std::string ret{}; + const auto language = m_app->GetLanguage(); + if (property.count(language) > 0) + { + ret = property.at(language); + } + else if (property.count(LANGUAGE_ENGLISH) > 0) + { + ret = property.at(LANGUAGE_ENGLISH); + } + else + { + ret = fallback; + } + return ret; +} + } // namespace Ui diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h index e75d4baa..655406ec 100644 --- a/src/ui/screen/screen_mod_list.h +++ b/src/ui/screen/screen_mod_list.h @@ -21,11 +21,12 @@ #include "app/modman.h" +#include "ui/maindialog.h" + #include "ui/screen/screen.h" namespace Ui { -class CMainDialog; /** * \class CScreenModList @@ -78,6 +79,8 @@ protected: void UpdateApplyButton(); void UpdateUpDownButtons(); + std::string GetLanguageStringProperty(std::unordered_map property, const std::string& fallback); + protected: Ui::CMainDialog* m_dialog; From 2b96eda86d40724886c66b81b38a920ff83b5372 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 22 Jul 2020 17:13:52 +0200 Subject: [PATCH 18/29] Fix minor issues --- src/app/moddata.cpp | 12 ++++++------ src/app/moddata.h | 2 +- src/app/modman.h | 2 +- src/ui/screen/screen_mod_list.cpp | 4 +++- src/ui/screen/screen_mod_list.h | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/app/moddata.cpp b/src/app/moddata.cpp index 3ff716d7..c2653023 100644 --- a/src/app/moddata.cpp +++ b/src/app/moddata.cpp @@ -31,16 +31,16 @@ namespace pt = boost::property_tree; -boost::optional LoadManifest(const std::string& path, bool loaded = false); +boost::optional LoadManifest(const std::string& path); std::string GetStringProperty(const pt::ptree& manifest, const std::string& key); std::unordered_map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key); -ModData LoadModData(const std::string& path, bool loaded) +ModData LoadModData(const std::string& path) { ModData modData{}; auto manifestOptional = LoadManifest(path); - if (!manifestOptional.has_value()) + if (!manifestOptional) { return modData; } @@ -55,7 +55,7 @@ ModData LoadModData(const std::string& path, bool loaded) return modData; } -boost::optional LoadManifest(const std::string& path, bool loaded) +boost::optional LoadManifest(const std::string& path) { try { @@ -82,7 +82,7 @@ boost::optional LoadManifest(const std::string& path, bool loaded) std::string GetStringProperty(const pt::ptree& manifest, const std::string& key) { auto prop = manifest.get_optional(key); - if (prop.has_value()) + if (prop) { return prop.get(); } @@ -93,7 +93,7 @@ std::unordered_map GetLanguageStringProperty(const pt::pt { std::unordered_map ret; auto prop = manifest.get_child_optional(key); - if (prop.has_value()) + if (prop) { for (const auto& child : prop.get()) { diff --git a/src/app/moddata.h b/src/app/moddata.h index 90f3887c..551b701a 100644 --- a/src/app/moddata.h +++ b/src/app/moddata.h @@ -33,4 +33,4 @@ struct ModData }; //! Loads the metadata for a mod in the given path. -ModData LoadModData(const std::string& path, bool loaded = false); +ModData LoadModData(const std::string& path); diff --git a/src/app/modman.h b/src/app/modman.h index a937ec20..bd66117e 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -22,7 +22,7 @@ #include "app/moddata.h" #include -#include +#include class CApplication; class CPathManager; diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index a216218b..8868e0a9 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -43,6 +43,8 @@ #include "ui/controls/list.h" #include "ui/controls/window.h" +#include + namespace Ui { @@ -557,7 +559,7 @@ void CScreenModList::UpdateUpDownButtons() } } -std::string CScreenModList::GetLanguageStringProperty(std::unordered_map property, const std::string& fallback) +std::string CScreenModList::GetLanguageStringProperty(const std::unordered_map& property, const std::string& fallback) { std::string ret{}; const auto language = m_app->GetLanguage(); diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h index 655406ec..7be1a133 100644 --- a/src/ui/screen/screen_mod_list.h +++ b/src/ui/screen/screen_mod_list.h @@ -79,7 +79,7 @@ protected: void UpdateApplyButton(); void UpdateUpDownButtons(); - std::string GetLanguageStringProperty(std::unordered_map property, const std::string& fallback); + std::string GetLanguageStringProperty(const std::unordered_map& property, const std::string& fallback); protected: Ui::CMainDialog* m_dialog; From 1be69a0a513d1abfba0ac955b3174cb2ff10ae8c Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 22 Jul 2020 17:24:07 +0200 Subject: [PATCH 19/29] Fix compile errors --- src/app/moddata.cpp | 6 +++--- src/app/moddata.h | 6 +++--- src/ui/screen/screen_mod_list.cpp | 2 +- src/ui/screen/screen_mod_list.h | 4 +++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/app/moddata.cpp b/src/app/moddata.cpp index c2653023..4ccf2f8f 100644 --- a/src/app/moddata.cpp +++ b/src/app/moddata.cpp @@ -33,7 +33,7 @@ namespace pt = boost::property_tree; boost::optional LoadManifest(const std::string& path); std::string GetStringProperty(const pt::ptree& manifest, const std::string& key); -std::unordered_map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key); +std::map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key); ModData LoadModData(const std::string& path) { @@ -89,9 +89,9 @@ std::string GetStringProperty(const pt::ptree& manifest, const std::string& key) return {}; } -std::unordered_map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key) +std::map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key) { - std::unordered_map ret; + std::map ret; auto prop = manifest.get_child_optional(key); if (prop) { diff --git a/src/app/moddata.h b/src/app/moddata.h index 551b701a..3969c812 100644 --- a/src/app/moddata.h +++ b/src/app/moddata.h @@ -21,15 +21,15 @@ #include "common/language.h" -#include +#include struct ModData { - std::unordered_map displayName{}; + std::map displayName{}; std::string author{}; std::string version{}; std::string website{}; - std::unordered_map summary{}; + std::map summary{}; }; //! Loads the metadata for a mod in the given path. diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index 8868e0a9..852e08f7 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -559,7 +559,7 @@ void CScreenModList::UpdateUpDownButtons() } } -std::string CScreenModList::GetLanguageStringProperty(const std::unordered_map& property, const std::string& fallback) +std::string CScreenModList::GetLanguageStringProperty(const std::map& property, const std::string& fallback) { std::string ret{}; const auto language = m_app->GetLanguage(); diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h index 7be1a133..0da3a7c3 100644 --- a/src/ui/screen/screen_mod_list.h +++ b/src/ui/screen/screen_mod_list.h @@ -25,6 +25,8 @@ #include "ui/screen/screen.h" +#include + namespace Ui { @@ -79,7 +81,7 @@ protected: void UpdateApplyButton(); void UpdateUpDownButtons(); - std::string GetLanguageStringProperty(const std::unordered_map& property, const std::string& fallback); + std::string GetLanguageStringProperty(const std::map& property, const std::string& fallback); protected: Ui::CMainDialog* m_dialog; From df415880d08f0ece01dc9e475293a9b6ad69155b Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 22 Jul 2020 17:26:46 +0200 Subject: [PATCH 20/29] Fix linter issues --- src/app/moddata.cpp | 2 +- src/app/moddata.h | 34 +++++++++++++++---------------- src/ui/screen/screen_mod_list.cpp | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/app/moddata.cpp b/src/app/moddata.cpp index 4ccf2f8f..43ed4368 100644 --- a/src/app/moddata.cpp +++ b/src/app/moddata.cpp @@ -22,8 +22,8 @@ #include "common/logger.h" #include "common/make_unique.h" -#include "common/resources/resourcemanager.h" #include "common/resources/inputstream.h" +#include "common/resources/resourcemanager.h" #include #include diff --git a/src/app/moddata.h b/src/app/moddata.h index 3969c812..25484e8e 100644 --- a/src/app/moddata.h +++ b/src/app/moddata.h @@ -1,21 +1,21 @@ /* -* This file is part of the Colobot: Gold Edition source code -* Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam -* http://epsitec.ch; http://colobot.info; http://github.com/colobot -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see http://gnu.org/licenses -*/ + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ #pragma once diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index 852e08f7..c20fdcdc 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -421,7 +421,7 @@ void CScreenModList::UpdateModDetails() details += unknownAuthor; } details += '\n'; - + details += '\n'; if (!data.version.empty()) From 62b770f7d3ba265863bd8d3458cf795f3d24edae Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 22 Jul 2020 17:50:19 +0200 Subject: [PATCH 21/29] Improve UI of mod manager a little The arrow buttons are smaller and the up button is now above the down button. What is more, the width of the back button is now the same as in other places. --- src/ui/screen/screen_mod_list.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index c20fdcdc..95d42c57 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -169,13 +169,15 @@ void CScreenModList::CreateInterface() UpdateEnableDisableButton(); // Display the move up button - pos.x -= dim.x*1.3f; - ddim.x = dim.x*1; + pos.x -= dim.x*0.8f; + pos.y = oy+sy*2.48; + ddim.x = dim.x*0.5; + ddim.y = dim.y*0.5; pb = pw->CreateButton(pos, ddim, 49, EVENT_INTERFACE_MOD_MOVE_UP); pb->SetState(STATE_SHADOW); // Display the move down button - pos.x -= dim.x*1.3f; + pos.y = oy+sy*2; pb = pw->CreateButton(pos, ddim, 50, EVENT_INTERFACE_MOD_MOVE_DOWN); pb->SetState(STATE_SHADOW); @@ -183,6 +185,9 @@ void CScreenModList::CreateInterface() // Display the refresh button pos.x -= dim.x*1.3f; + pos.y = oy+sy*2; + ddim.x = dim.x*1; + ddim.y = dim.y*1; pb = pw->CreateButton(pos, ddim, 87, EVENT_INTERFACE_MODS_REFRESH); pb->SetState(STATE_SHADOW); @@ -198,7 +203,7 @@ void CScreenModList::CreateInterface() // Back button pos.x = ox+sx*3; - ddim.x = dim.x*2.3f; + ddim.x = dim.x*4; pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK); pb->SetState(STATE_SHADOW); From 16795e0d49ba8b65f6bc7d6e74dc33c27561f365 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 22 Jul 2020 21:40:13 +0200 Subject: [PATCH 22/29] Use level parser instead of JSON for manifest --- src/CMakeLists.txt | 2 - src/app/moddata.cpp | 114 ------------------------------ src/app/moddata.h | 36 ---------- src/app/modman.cpp | 75 +++++++++++++++++++- src/app/modman.h | 14 +++- src/level/parser/parser.cpp | 24 +++++-- src/ui/screen/screen_mod_list.cpp | 33 +++------ src/ui/screen/screen_mod_list.h | 2 - 8 files changed, 116 insertions(+), 184 deletions(-) delete mode 100644 src/app/moddata.cpp delete mode 100644 src/app/moddata.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f00e77a6..df752615 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,8 +145,6 @@ set(BASE_SOURCES app/controller.h app/input.cpp app/input.h - app/moddata.cpp - app/moddata.h app/modman.cpp app/modman.h app/pathman.cpp diff --git a/src/app/moddata.cpp b/src/app/moddata.cpp deleted file mode 100644 index 43ed4368..00000000 --- a/src/app/moddata.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsitec.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - -#include "app/moddata.h" - -#include "common/logger.h" -#include "common/make_unique.h" - -#include "common/resources/inputstream.h" -#include "common/resources/resourcemanager.h" - -#include -#include -#include - -namespace pt = boost::property_tree; - -boost::optional LoadManifest(const std::string& path); -std::string GetStringProperty(const pt::ptree& manifest, const std::string& key); -std::map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key); - -ModData LoadModData(const std::string& path) -{ - ModData modData{}; - - auto manifestOptional = LoadManifest(path); - if (!manifestOptional) - { - return modData; - } - auto manifest = manifestOptional.get(); - - modData.displayName = GetLanguageStringProperty(manifest, "DisplayName"); - modData.version = GetStringProperty(manifest, "Version"); - modData.author = GetStringProperty(manifest, "Author"); - modData.website = GetStringProperty(manifest, "Website"); - modData.summary = GetLanguageStringProperty(manifest, "Summary"); - - return modData; -} - -boost::optional LoadManifest(const std::string& path) -{ - try - { - auto inputStream = MakeUnique("manifest.json"); - if (!inputStream->is_open()) - { - GetLogger()->Error("Error on parsing the manifest file %s: failed to open file\n"); - return {}; - } - - pt::ptree manifest{}; - boost::property_tree::json_parser::read_json(*inputStream, manifest); - - return manifest; - } - catch (std::exception& e) - { - GetLogger()->Warn("Error on parsing the manifest file %s: %s\n", path.c_str(), e.what()); - return {}; - } - return {}; -} - -std::string GetStringProperty(const pt::ptree& manifest, const std::string& key) -{ - auto prop = manifest.get_optional(key); - if (prop) - { - return prop.get(); - } - return {}; -} - -std::map GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key) -{ - std::map ret; - auto prop = manifest.get_child_optional(key); - if (prop) - { - for (const auto& child : prop.get()) - { - Language language = LANGUAGE_ENGLISH; - std::string strLanguage = child.first.data(); - if (!ParseLanguage(strLanguage, language)) - { - GetLogger()->Warn("Error on parsing the manifest file: %s language %s is not a valid language\n", key.c_str(), strLanguage.c_str()); - continue; - } - else - { - ret.insert(std::make_pair(language, child.second.data())); - } - } - } - return ret; -} diff --git a/src/app/moddata.h b/src/app/moddata.h deleted file mode 100644 index 25484e8e..00000000 --- a/src/app/moddata.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsitec.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - -#pragma once - -#include "common/language.h" - -#include - -struct ModData -{ - std::map displayName{}; - std::string author{}; - std::string version{}; - std::string website{}; - std::map summary{}; -}; - -//! Loads the metadata for a mod in the given path. -ModData LoadModData(const std::string& path); diff --git a/src/app/modman.cpp b/src/app/modman.cpp index a8dcf7d3..d129271f 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -29,6 +29,8 @@ #include "common/resources/resourcemanager.h" +#include "level/parser/parser.h" + #include #include #include @@ -107,7 +109,7 @@ void CModManager::Init() for (auto& mod : m_mods) { m_pathManager->AddMod(mod.path); - mod.data = LoadModData(mod.path); + LoadModData(mod); m_pathManager->RemoveMod(mod.path); } @@ -207,3 +209,74 @@ const std::vector& CModManager::GetMods() const { return m_mods; } + +void CModManager::LoadModData(Mod& mod) +{ + auto& data = mod.data; + + data.displayName = mod.name; + + try + { + CLevelParser levelParser("manifest.txt"); + if (levelParser.Exists()) + { + levelParser.Load(); + + CLevelParserLine* line = nullptr; + + // DisplayName + line = levelParser.GetIfDefined("DisplayName"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.displayName = line->GetParam("text")->AsString(); + } + + // Author + line = levelParser.GetIfDefined("Author"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.author = line->GetParam("text")->AsString(); + } + + // Version + line = levelParser.GetIfDefined("Version"); + if (line != nullptr) + { + if (line->GetParam("text")->IsDefined()) + { + data.version = line->GetParam("text")->AsString(); + } + else if (line->GetParam("major")->IsDefined() && line->GetParam("minor")->IsDefined() && line->GetParam("patch")->IsDefined()) + { + auto major = boost::lexical_cast(line->GetParam("major")->AsInt()); + auto minor = boost::lexical_cast(line->GetParam("minor")->AsInt()); + auto patch = boost::lexical_cast(line->GetParam("patch")->AsInt()); + data.version = boost::algorithm::join(std::vector{ major, minor, patch }, ","); + } + } + + // Website + line = levelParser.GetIfDefined("Website"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.website = line->GetParam("text")->AsString(); + } + + // Summary + line = levelParser.GetIfDefined("Summary"); + if (line != nullptr && line->GetParam("text")->IsDefined()) + { + data.summary = line->GetParam("text")->AsString(); + } + } + else + { + GetLogger()->Warn("No manifest file for mod %s\n", mod.name.c_str()); + } + } + catch (CLevelParserException& e) + { + GetLogger()->Warn("Failed parsing manifest for mod %s: %s\n", mod.name.c_str(), e.what()); + } +} diff --git a/src/app/modman.h b/src/app/modman.h index bd66117e..57c54524 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -19,14 +19,21 @@ #pragma once -#include "app/moddata.h" - #include #include class CApplication; class CPathManager; +struct ModData +{ + std::string displayName{}; + std::string author{}; + std::string version{}; + std::string website{}; + std::string summary{}; +}; + struct Mod { std::string name{}; @@ -90,6 +97,9 @@ private: //! Reloads application resources so the enabled mods are applied void ReloadResources(); + //! Load mod data into mod + void LoadModData(Mod& mod); + private: CApplication* m_app; CPathManager* m_pathManager; diff --git a/src/level/parser/parser.cpp b/src/level/parser/parser.cpp index e8f871bd..1bf78a24 100644 --- a/src/level/parser/parser.cpp +++ b/src/level/parser/parser.cpp @@ -41,6 +41,7 @@ #include #include #include +#include CLevelParser::CLevelParser() { @@ -172,13 +173,28 @@ void CLevelParser::Load() boost::replace_all(line, "\t", " "); // replace tab by space // ignore comments - std::size_t comment = line.find("//"); - if (comment != std::string::npos) - line = line.substr(0, comment); + size_t pos = 0; + std::string linesuffix = line; + boost::regex commentRegex{ R"(("[^"]*")|('[^']*')|(//.*$))" }; + boost::smatch matches; + while (boost::regex_search(linesuffix, matches, commentRegex)) + { + if (matches[3].matched) + { + pos += std::distance(linesuffix.cbegin(), matches.prefix().second); + line = line.substr(0, pos); + linesuffix = ""; + } + else + { + pos += std::distance(linesuffix.cbegin(), matches.suffix().first); + linesuffix = matches.suffix().str(); + } + } boost::algorithm::trim(line); - std::size_t pos = line.find_first_of(" \t\n"); + pos = line.find_first_of(" \t\n"); std::string command = line.substr(0, pos); if (pos != std::string::npos) { diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index 95d42c57..8717ed57 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -380,7 +380,7 @@ void CScreenModList::UpdateModList() for (size_t i = 0; i < mods.size(); ++i) { const auto& mod = mods[i]; - auto name = GetLanguageStringProperty(mod.data.displayName, mod.name); + auto name = mod.data.displayName; pl->SetItemName(i, name); pl->SetCheck(i, mod.enabled); pl->SetEnable(i, true); @@ -409,8 +409,7 @@ void CScreenModList::UpdateModDetails() const auto& mod = m_modManager->GetMod(m_modSelectedIndex); const auto data = mod.data; - auto name = GetLanguageStringProperty(data.displayName, mod.name); - details += "\\b;" + name + '\n'; + details += "\\b;" + data.displayName + '\n'; std::string authorFieldName; GetResource(RES_TEXT, RT_MOD_AUTHOR_FIELD_NAME, authorFieldName); @@ -465,7 +464,14 @@ void CScreenModList::UpdateModSummary() const auto& mod = m_modManager->GetMod(m_modSelectedIndex); - pe->SetText(GetLanguageStringProperty(mod.data.summary, noSummary)); + if (!mod.data.summary.empty()) + { + pe->SetText(mod.data.summary); + } + else + { + pe->SetText(noSummary); + } } void CScreenModList::UpdateEnableDisableButton() @@ -564,23 +570,4 @@ void CScreenModList::UpdateUpDownButtons() } } -std::string CScreenModList::GetLanguageStringProperty(const std::map& property, const std::string& fallback) -{ - std::string ret{}; - const auto language = m_app->GetLanguage(); - if (property.count(language) > 0) - { - ret = property.at(language); - } - else if (property.count(LANGUAGE_ENGLISH) > 0) - { - ret = property.at(LANGUAGE_ENGLISH); - } - else - { - ret = fallback; - } - return ret; -} - } // namespace Ui diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h index 0da3a7c3..639fd775 100644 --- a/src/ui/screen/screen_mod_list.h +++ b/src/ui/screen/screen_mod_list.h @@ -81,8 +81,6 @@ protected: void UpdateApplyButton(); void UpdateUpDownButtons(); - std::string GetLanguageStringProperty(const std::map& property, const std::string& fallback); - protected: Ui::CMainDialog* m_dialog; From 51668c12d7c9b055549ca381356b4642d3514524 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Thu, 23 Jul 2020 17:44:38 +0200 Subject: [PATCH 23/29] Add changes listing to mods It's just listing directories of a mod for now, but should give some idea what the mod changes. Also moved some functionality from pathman to modman. Mods added with the `-mod` parameter are now managed by modman. --- po/colobot.pot | 6 ++ po/cs.po | 7 ++ po/de.po | 7 ++ po/fr.po | 7 ++ po/pl.po | 6 ++ po/pt.po | 7 ++ po/ru.po | 7 ++ src/app/modman.cpp | 39 +++++++++-- src/app/modman.h | 6 ++ src/app/pathman.cpp | 88 ++++++++++-------------- src/app/pathman.h | 16 ++--- src/common/resources/resourcemanager.cpp | 4 +- src/common/resources/resourcemanager.h | 2 +- src/common/restext.cpp | 2 + src/common/restext.h | 4 +- src/ui/screen/screen_mod_list.cpp | 19 +++++ 16 files changed, 158 insertions(+), 69 deletions(-) diff --git a/po/colobot.pot b/po/colobot.pot index b459a8f3..49ecac68 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -327,9 +327,15 @@ msgstr "" msgid "Website" msgstr "" +msgid "Changes" +msgstr "" + msgid "No description." msgstr "" +msgid "No changes." +msgstr "" + msgid "Code battle" msgstr "" diff --git a/po/cs.po b/po/cs.po index 6fb3aa3e..829446fd 100644 --- a/po/cs.po +++ b/po/cs.po @@ -374,6 +374,10 @@ msgstr "Změnit kameru\\Přepíná mezi kamerou na robotu a za robotem" msgid "Change player\\Change player" msgstr "Změnit hráče\\Změnit hráče" +#, fuzzy +msgid "Changes" +msgstr "Hlavolamy" + msgid "Chapters:" msgstr "Kapitoly:" @@ -988,6 +992,9 @@ msgstr "Další objekt\\Vybere následující objekt" msgid "No" msgstr "Ne" +msgid "No changes." +msgstr "" + #, fuzzy msgid "No description." msgstr "Rozlišení:" diff --git a/po/de.po b/po/de.po index 23b581f4..06c7cb73 100644 --- a/po/de.po +++ b/po/de.po @@ -375,6 +375,10 @@ msgstr "Andere Kamera\\Sichtpunkt einstellen" msgid "Change player\\Change player" msgstr "Anderer Spieler\\Spielername ändern" +#, fuzzy +msgid "Changes" +msgstr "Herausforderungen" + msgid "Chapters:" msgstr "Liste der Kapitel:" @@ -1004,6 +1008,9 @@ msgstr "Nächstes auswählen\\Nächstes Objekt auswählen" msgid "No" msgstr "Nein" +msgid "No changes." +msgstr "" + #, fuzzy msgid "No description." msgstr "Auflösung:" diff --git a/po/fr.po b/po/fr.po index 6c13748d..1d096618 100644 --- a/po/fr.po +++ b/po/fr.po @@ -377,6 +377,10 @@ msgstr "Changement de caméra\\Autre de point de vue" msgid "Change player\\Change player" msgstr "Autre joueur\\Choix du nom du joueur" +#, fuzzy +msgid "Changes" +msgstr "Défis" + msgid "Chapters:" msgstr "Liste des chapitres :" @@ -1006,6 +1010,9 @@ msgstr "Sélectionner l'objet suivant\\Sélectionner l'objet suivant" msgid "No" msgstr "Non" +msgid "No changes." +msgstr "" + #, fuzzy msgid "No description." msgstr "Résolutions :" diff --git a/po/pl.po b/po/pl.po index 9f00c679..cc2bcd96 100644 --- a/po/pl.po +++ b/po/pl.po @@ -373,6 +373,9 @@ msgstr "Zmień kamerę\\Przełącza pomiędzy kamerą pokładową i śledzącą" msgid "Change player\\Change player" msgstr "Zmień gracza\\Zmień gracza" +msgid "Changes" +msgstr "Zmiany" + msgid "Chapters:" msgstr "Rozdziały:" @@ -984,6 +987,9 @@ msgstr "Następny obiekt\\Zaznacza następny obiekt" msgid "No" msgstr "Nie" +msgid "No changes." +msgstr "Brak zmian." + msgid "No description." msgstr "Brak opisu." diff --git a/po/pt.po b/po/pt.po index 05285903..b8088c59 100644 --- a/po/pt.po +++ b/po/pt.po @@ -371,6 +371,10 @@ msgstr "Mudar câmera\\Alterna entre câmera incorporada e câmera seguidora" msgid "Change player\\Change player" msgstr "Mudar jogador\\Mudar jogador" +#, fuzzy +msgid "Changes" +msgstr "Desafios" + msgid "Chapters:" msgstr "Capítulos:" @@ -1001,6 +1005,9 @@ msgstr "Próximo objeto\\Selecionar o próximo objeto" msgid "No" msgstr "Não" +msgid "No changes." +msgstr "" + #, fuzzy msgid "No description." msgstr "Resolução:" diff --git a/po/ru.po b/po/ru.po index 2a4dccaa..bb3db29a 100644 --- a/po/ru.po +++ b/po/ru.po @@ -378,6 +378,10 @@ msgstr "Изменить вид\\Переключение между борто msgid "Change player\\Change player" msgstr "Новый игрок\\Выберите имя для игрока" +#, fuzzy +msgid "Changes" +msgstr "Задания" + msgid "Chapters:" msgstr "Разделы:" @@ -1012,6 +1016,9 @@ msgstr "Следующий объект\\Выбор следующего объ msgid "No" msgstr "Нет" +msgid "No changes." +msgstr "" + #, fuzzy msgid "No description." msgstr "Разрешение:" diff --git a/src/app/modman.cpp b/src/app/modman.cpp index d129271f..54596b63 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -67,7 +67,7 @@ void CModManager::Init() } // Search the folders for mods - const auto rawPaths = m_pathManager->FindMods(); + auto rawPaths = m_pathManager->FindMods(); std::map modPaths; for (const auto& path : rawPaths) { @@ -108,9 +108,9 @@ void CModManager::Init() // Load the metadata for each mod for (auto& mod : m_mods) { - m_pathManager->AddMod(mod.path); + MountMod(mod, "/temp/mod"); LoadModData(mod); - m_pathManager->RemoveMod(mod.path); + UnmountMod(mod); } UpdatePaths(); @@ -118,8 +118,8 @@ void CModManager::Init() void CModManager::ReloadMods() { + UnmountAllMods(); m_mods.clear(); - m_pathManager->RemoveAllMods(); Init(); @@ -165,12 +165,11 @@ size_t CModManager::MoveDown(size_t i) void CModManager::UpdatePaths() { - m_pathManager->RemoveAllMods(); for (const auto& mod : m_mods) { if (mod.enabled) { - m_pathManager->AddMod(mod.path); + MountMod(mod); } } } @@ -218,7 +217,7 @@ void CModManager::LoadModData(Mod& mod) try { - CLevelParser levelParser("manifest.txt"); + CLevelParser levelParser("temp/mod/manifest.txt"); if (levelParser.Exists()) { levelParser.Load(); @@ -279,4 +278,30 @@ void CModManager::LoadModData(Mod& mod) { GetLogger()->Warn("Failed parsing manifest for mod %s: %s\n", mod.name.c_str(), e.what()); } + + // Changes + data.changes = CResourceManager::ListDirectories("temp/mod"); +} + +void CModManager::MountMod(const Mod& mod, const std::string& mountPoint) +{ + GetLogger()->Debug("Mounting mod: '%s' at path %s\n", mod.path.c_str(), mountPoint.c_str()); + CResourceManager::AddLocation(mod.path, true, mountPoint); +} + +void CModManager::UnmountMod(const Mod& mod) +{ + if (CResourceManager::LocationExists(mod.path)) + { + GetLogger()->Debug("Unmounting mod: '%s'\n", mod.path.c_str()); + CResourceManager::RemoveLocation(mod.path); + } +} + +void CModManager::UnmountAllMods() +{ + for (const auto& mod : m_mods) + { + UnmountMod(mod); + } } diff --git a/src/app/modman.h b/src/app/modman.h index 57c54524..a8af565e 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -19,6 +19,7 @@ #pragma once +#include #include #include @@ -32,6 +33,7 @@ struct ModData std::string version{}; std::string website{}; std::string summary{}; + std::vector changes{}; }; struct Mod @@ -100,6 +102,10 @@ private: //! Load mod data into mod void LoadModData(Mod& mod); + void MountMod(const Mod& mod, const std::string& mountPoint = ""); + void UnmountMod(const Mod& mod); + void UnmountAllMods(); + private: CApplication* m_app; CPathManager* m_pathManager; diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index e353ec1a..e4c385dd 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -64,56 +64,6 @@ void CPathManager::SetSavePath(const std::string &savePath) m_savePath = savePath; } -void CPathManager::AddModSearchDir(const std::string &modSearchDirPath) -{ - m_modSearchDirs.push_back(modSearchDirPath); -} - -void CPathManager::AddMod(const std::string &modPath) -{ - GetLogger()->Debug("Adding mod: '%s'\n", modPath.c_str()); - CResourceManager::AddLocation(modPath, true); - m_mods.push_back(modPath); -} - -void CPathManager::RemoveMod(const std::string &modPath) -{ - GetLogger()->Debug("Removing mod: '%s'\n", modPath.c_str()); - CResourceManager::RemoveLocation(modPath); - auto it = std::find(m_mods.cbegin(), m_mods.cend(), modPath); - if (it != m_mods.cend()) - m_mods.erase(it); -} - -void CPathManager::RemoveAllMods() -{ - for (const auto& modPath : m_mods) - { - CResourceManager::RemoveLocation(modPath); - } - m_mods.clear(); -} - -bool CPathManager::ModLoaded(const std::string& modPath) -{ - return std::find(m_mods.cbegin(), m_mods.cend(), modPath) != m_mods.end(); -} - -std::vector CPathManager::FindMods() const -{ - std::vector mods; - GetLogger()->Info("Found mods:\n"); - for (const auto &searchPath : m_modSearchDirs) - { - for (const auto &modPath : FindModsInDir(searchPath)) - { - GetLogger()->Info(" * %s\n", modPath.c_str()); - mods.push_back(modPath); - } - } - return mods; -} - const std::string& CPathManager::GetDataPath() { return m_dataPath; @@ -191,6 +141,44 @@ void CPathManager::InitPaths() GetLogger()->Debug(" * %s\n", path.c_str()); } +void CPathManager::AddMod(const std::string &path) +{ + m_mods.push_back(path); +} + +std::vector CPathManager::FindMods() const +{ + std::vector mods; + GetLogger()->Info("Found mods:\n"); + for (const auto &searchPath : m_modSearchDirs) + { + for (const auto &modPath : FindModsInDir(searchPath)) + { + GetLogger()->Info(" * %s\n", modPath.c_str()); + mods.push_back(modPath); + } + } + GetLogger()->Info("Additional mod paths:\n"); + for (const auto& modPath : m_mods) + { + if (boost::filesystem::exists(modPath)) + { + GetLogger()->Info(" * %s\n", modPath.c_str()); + mods.push_back(modPath); + } + else + { + GetLogger()->Warn("Mod does not exist: %s\n", modPath.c_str()); + } + } + return mods; +} + +void CPathManager::AddModSearchDir(const std::string &modSearchDirPath) +{ + m_modSearchDirs.push_back(modSearchDirPath); +} + std::vector CPathManager::FindModsInDir(const std::string &dir) const { std::vector ret; diff --git a/src/app/pathman.h b/src/app/pathman.h index 2fedfee6..d1ba4bbe 100644 --- a/src/app/pathman.h +++ b/src/app/pathman.h @@ -37,12 +37,6 @@ public: void SetDataPath(const std::string &dataPath); void SetLangPath(const std::string &langPath); void SetSavePath(const std::string &savePath); - void AddModSearchDir(const std::string &modSearchDirPath); - void AddMod(const std::string &modPath); - void RemoveMod(const std::string &modPath); - void RemoveAllMods(); - bool ModLoaded(const std::string& modPath); - std::vector FindMods() const; const std::string& GetDataPath(); const std::string& GetLangPath(); @@ -53,8 +47,14 @@ public: //! Loads configured paths void InitPaths(); + //! Adds a path to a mod + void AddMod(const std::string& path); + //! Find paths to mods in mod search directories + std::vector FindMods() const; + //! Adds a mod search directory + void AddModSearchDir(const std::string &modSearchDirPath); + private: - //! Loads all mods from given directory std::vector FindModsInDir(const std::string &dir) const; private: @@ -66,6 +66,6 @@ private: std::string m_savePath; //! Mod search paths std::vector m_modSearchDirs; - //! Mod paths + //! Additional mod paths std::vector m_mods; }; diff --git a/src/common/resources/resourcemanager.cpp b/src/common/resources/resourcemanager.cpp index bddb5236..7c572483 100644 --- a/src/common/resources/resourcemanager.cpp +++ b/src/common/resources/resourcemanager.cpp @@ -62,9 +62,9 @@ std::string CResourceManager::CleanPath(const std::string& path) } -bool CResourceManager::AddLocation(const std::string &location, bool prepend) +bool CResourceManager::AddLocation(const std::string &location, bool prepend, const std::string &mountPoint) { - if (!PHYSFS_mount(location.c_str(), nullptr, prepend ? 0 : 1)) + if (!PHYSFS_mount(location.c_str(), mountPoint.c_str(), prepend ? 0 : 1)) { GetLogger()->Error("Error while mounting \"%s\": %s\n", location.c_str(), PHYSFS_getLastError()); return false; diff --git a/src/common/resources/resourcemanager.h b/src/common/resources/resourcemanager.h index 0a1bca16..d15a6f02 100644 --- a/src/common/resources/resourcemanager.h +++ b/src/common/resources/resourcemanager.h @@ -36,7 +36,7 @@ public: static std::string CleanPath(const std::string &path); //! Add a location to the search path - static bool AddLocation(const std::string &location, bool prepend = true); + static bool AddLocation(const std::string &location, bool prepend = true, const std::string &mountPoint = ""); //! Remove a location from the search path static bool RemoveLocation(const std::string &location); //! List all locations in the search path diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 294aa1eb..becde931 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -168,7 +168,9 @@ void InitializeRestext() stringsText[RT_MOD_AUTHOR_FIELD_NAME] = TR("by"); stringsText[RT_MOD_VERSION_FIELD_NAME] = TR("Version"); stringsText[RT_MOD_WEBSITE_FIELD_NAME] = TR("Website"); + stringsText[RT_MOD_CHANGES_FIELD_NAME] = TR("Changes"); stringsText[RT_MOD_NO_SUMMARY] = TR("No description."); + stringsText[RT_MOD_NO_CHANGES] = TR("No changes."); stringsEvent[EVENT_LABEL_CODE_BATTLE] = TR("Code battle"); diff --git a/src/common/restext.h b/src/common/restext.h index 5031d069..165c52cd 100644 --- a/src/common/restext.h +++ b/src/common/restext.h @@ -162,7 +162,9 @@ enum ResTextType RT_MOD_AUTHOR_FIELD_NAME = 240, RT_MOD_VERSION_FIELD_NAME = 241, RT_MOD_WEBSITE_FIELD_NAME = 242, - RT_MOD_NO_SUMMARY = 243, + RT_MOD_CHANGES_FIELD_NAME = 243, + RT_MOD_NO_SUMMARY = 244, + RT_MOD_NO_CHANGES = 245, RT_MAX //! < number of values }; diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index 8717ed57..f151f781 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -442,7 +442,26 @@ void CScreenModList::UpdateModDetails() details += "\\t;" + websiteFieldName + '\n' + data.website + '\n'; } + std::string changesFieldName; + GetResource(RES_TEXT, RT_MOD_CHANGES_FIELD_NAME, changesFieldName); + details += "\\t;" + changesFieldName + '\n'; + if (!data.changes.empty()) + { + for (const auto& change : data.changes) + { + details += change + '\n'; + } + } + else + { + std::string noChanges; + GetResource(RES_TEXT, RT_MOD_NO_CHANGES, noChanges); + details += noChanges; + } + pe->SetText(details); + + pe->SetFirstLine(0); } void CScreenModList::UpdateModSummary() From e6ce4112c67932c007af3eff4be8805a2652c593 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Thu, 23 Jul 2020 17:57:25 +0200 Subject: [PATCH 24/29] Update translations --- po/cs.po | 22 ++++++---------------- po/de.po | 22 ++++++---------------- po/fr.po | 22 ++++++---------------- po/pl.po | 4 ---- po/pt.po | 22 ++++++---------------- po/ru.po | 22 ++++++---------------- 6 files changed, 30 insertions(+), 84 deletions(-) diff --git a/po/cs.po b/po/cs.po index 829446fd..c7755b3b 100644 --- a/po/cs.po +++ b/po/cs.po @@ -374,9 +374,8 @@ msgstr "Změnit kameru\\Přepíná mezi kamerou na robotu a za robotem" msgid "Change player\\Change player" msgstr "Změnit hráče\\Změnit hráče" -#, fuzzy msgid "Changes" -msgstr "Hlavolamy" +msgstr "" msgid "Chapters:" msgstr "Kapitoly:" @@ -486,9 +485,8 @@ msgstr "Vrtná věž" msgid "Descend\\Reduces the power of the jet" msgstr "Klesat\\Snížit tah tryskového motoru" -#, fuzzy msgid "Description:" -msgstr "Rozlišení:" +msgstr "" msgid "Destroy" msgstr "Zbourat" @@ -502,9 +500,8 @@ msgstr "Drtič" msgid "Device\\Driver and resolution settings" msgstr "Obrazovka\\Nastavení grafické karty a rozlišení" -#, fuzzy msgid "Disable\\Disable the selected mod" -msgstr "Smazat\\Smaže vybraný soubor" +msgstr "" msgid "Dividing by zero" msgstr "Dělení nulou" @@ -552,9 +549,8 @@ msgstr "Vejce" msgid "Empty character constant" msgstr "" -#, fuzzy msgid "Enable\\Enable the selected mod" -msgstr "Nahrát\\Nahraje vybranou misi" +msgstr "" msgid "End of block missing" msgstr "Chybí konec bloku" @@ -995,9 +991,8 @@ msgstr "Ne" msgid "No changes." msgstr "" -#, fuzzy msgid "No description." -msgstr "Rozlišení:" +msgstr "" msgid "No energy in the subsoil" msgstr "Pod povrchem není zdroj energie" @@ -1746,9 +1741,8 @@ msgstr "Jednotka" msgid "Unknown Object" msgstr "Neznámý objekt" -#, fuzzy msgid "Unknown author" -msgstr "Neznámá funkce" +msgstr "" msgid "Unknown command" msgstr "Neznámý příkaz" @@ -2004,7 +1998,3 @@ msgstr "colobot.info" msgid "epsitec.com" msgstr "epsitec.com" - -#, fuzzy -#~ msgid "No mods installed!" -#~ msgstr "Žádné uživatelské mapy nejsou nainstalovány!" diff --git a/po/de.po b/po/de.po index 06c7cb73..059898a0 100644 --- a/po/de.po +++ b/po/de.po @@ -375,9 +375,8 @@ msgstr "Andere Kamera\\Sichtpunkt einstellen" msgid "Change player\\Change player" msgstr "Anderer Spieler\\Spielername ändern" -#, fuzzy msgid "Changes" -msgstr "Herausforderungen" +msgstr "" msgid "Chapters:" msgstr "Liste der Kapitel:" @@ -487,9 +486,8 @@ msgstr "Bohrturm" msgid "Descend\\Reduces the power of the jet" msgstr "Sinken\\Leistung des Triebwerks drosseln" -#, fuzzy msgid "Description:" -msgstr "Auflösung:" +msgstr "" msgid "Destroy" msgstr "Zerstören" @@ -503,9 +501,8 @@ msgstr "Einstampfer" msgid "Device\\Driver and resolution settings" msgstr "Bildschirm\\Driver und Bildschirmauflösung" -#, fuzzy msgid "Disable\\Disable the selected mod" -msgstr "Löschen\\Löscht die gespeicherte Mission" +msgstr "" msgid "Dividing by zero" msgstr "Division durch Null" @@ -553,9 +550,8 @@ msgstr "Ei" msgid "Empty character constant" msgstr "" -#, fuzzy msgid "Enable\\Enable the selected mod" -msgstr "Laden\\Öffnet eine gespeicherte Mission" +msgstr "" msgid "End of block missing" msgstr "Es fehlt eine geschlossene geschweifte Klammer \"}\" (Ende des Blocks)" @@ -1011,9 +1007,8 @@ msgstr "Nein" msgid "No changes." msgstr "" -#, fuzzy msgid "No description." -msgstr "Auflösung:" +msgstr "" msgid "No energy in the subsoil" msgstr "Kein unterirdisches Energievorkommen" @@ -1763,9 +1758,8 @@ msgstr "Einheit" msgid "Unknown Object" msgstr "Das Objekt existiert nicht" -#, fuzzy msgid "Unknown author" -msgstr "Unbekannte Funktion" +msgstr "" msgid "Unknown command" msgstr "Befehl unbekannt" @@ -2099,10 +2093,6 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Schatten unter der Maus\\Ein Schatten erscheint unter der Maus" -#, fuzzy -#~ msgid "No mods installed!" -#~ msgstr "Keine benutzerdefinierten Level vorhanden" - #~ msgid "No other robot" #~ msgstr "Kein anderer Roboter" diff --git a/po/fr.po b/po/fr.po index 1d096618..551267e5 100644 --- a/po/fr.po +++ b/po/fr.po @@ -377,9 +377,8 @@ msgstr "Changement de caméra\\Autre de point de vue" msgid "Change player\\Change player" msgstr "Autre joueur\\Choix du nom du joueur" -#, fuzzy msgid "Changes" -msgstr "Défis" +msgstr "" msgid "Chapters:" msgstr "Liste des chapitres :" @@ -489,9 +488,8 @@ msgstr "Derrick" msgid "Descend\\Reduces the power of the jet" msgstr "Descendre\\Diminuer la puissance du réacteur" -#, fuzzy msgid "Description:" -msgstr "Résolutions :" +msgstr "" msgid "Destroy" msgstr "Détruire" @@ -505,9 +503,8 @@ msgstr "Destructeur" msgid "Device\\Driver and resolution settings" msgstr "Affichage\\Pilote et résolution d'affichage" -#, fuzzy msgid "Disable\\Disable the selected mod" -msgstr "Supprimer\\Supprime l'enregistrement sélectionné" +msgstr "" msgid "Dividing by zero" msgstr "Division par zéro" @@ -555,9 +552,8 @@ msgstr "Oeuf" msgid "Empty character constant" msgstr "" -#, fuzzy msgid "Enable\\Enable the selected mod" -msgstr "Charger\\Charger la mission sélectionnée" +msgstr "" msgid "End of block missing" msgstr "Il manque la fin du bloc" @@ -1013,9 +1009,8 @@ msgstr "Non" msgid "No changes." msgstr "" -#, fuzzy msgid "No description." -msgstr "Résolutions :" +msgstr "" msgid "No energy in the subsoil" msgstr "Pas d'énergie en sous-sol" @@ -1765,9 +1760,8 @@ msgstr "Unité" msgid "Unknown Object" msgstr "Objet inconnu" -#, fuzzy msgid "Unknown author" -msgstr "Routine inconnue" +msgstr "" msgid "Unknown command" msgstr "Commande inconnue" @@ -2103,10 +2097,6 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Souris ombrée\\Jolie souris avec une ombre" -#, fuzzy -#~ msgid "No mods installed!" -#~ msgstr "Pas de niveaux spéciaux installés !" - #~ msgid "No other robot" #~ msgstr "Pas d'autre robot" diff --git a/po/pl.po b/po/pl.po index cc2bcd96..0db014ed 100644 --- a/po/pl.po +++ b/po/pl.po @@ -2076,10 +2076,6 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Cień kursora myszy\\Dodaje cień kursorowi myszy" -#, fuzzy -#~ msgid "No mods installed!" -#~ msgstr "Brak zainstalowanych poziomów użytkownika!" - #~ msgid "No other robot" #~ msgstr "Brak innego robota" diff --git a/po/pt.po b/po/pt.po index b8088c59..e1f8dc99 100644 --- a/po/pt.po +++ b/po/pt.po @@ -371,9 +371,8 @@ msgstr "Mudar câmera\\Alterna entre câmera incorporada e câmera seguidora" msgid "Change player\\Change player" msgstr "Mudar jogador\\Mudar jogador" -#, fuzzy msgid "Changes" -msgstr "Desafios" +msgstr "" msgid "Chapters:" msgstr "Capítulos:" @@ -484,9 +483,8 @@ msgstr "Extrator" msgid "Descend\\Reduces the power of the jet" msgstr "Descer\\Diminui o poder do jato" -#, fuzzy msgid "Description:" -msgstr "Resolução:" +msgstr "" msgid "Destroy" msgstr "Destruir" @@ -500,9 +498,8 @@ msgstr "Destruidor" msgid "Device\\Driver and resolution settings" msgstr "Dispositivo\\Configurações de driver e resolução" -#, fuzzy msgid "Disable\\Disable the selected mod" -msgstr "Excluir\\Exclui o arquivo selecionado" +msgstr "" msgid "Dividing by zero" msgstr "Dividindo por zero" @@ -550,9 +547,8 @@ msgstr "Ovo" msgid "Empty character constant" msgstr "" -#, fuzzy msgid "Enable\\Enable the selected mod" -msgstr "Carregar\\Carrega a missão selecionada" +msgstr "" msgid "End of block missing" msgstr "Fim do bloco ausente" @@ -1008,9 +1004,8 @@ msgstr "Não" msgid "No changes." msgstr "" -#, fuzzy msgid "No description." -msgstr "Resolução:" +msgstr "" msgid "No energy in the subsoil" msgstr "Nenhuma energia no subsolo" @@ -1760,9 +1755,8 @@ msgstr "Unidade" msgid "Unknown Object" msgstr "Objeto desconhecido" -#, fuzzy msgid "Unknown author" -msgstr "Função desconhecida" +msgstr "" msgid "Unknown command" msgstr "Comando desconhecido" @@ -2101,10 +2095,6 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Souris ombrée\\Jolie souris avec une ombre" -#, fuzzy -#~ msgid "No mods installed!" -#~ msgstr "Nenhum nível de usuário instalado!" - #~ msgid "No other robot" #~ msgstr "Pas d'autre robot" diff --git a/po/ru.po b/po/ru.po index bb3db29a..974d6ab1 100644 --- a/po/ru.po +++ b/po/ru.po @@ -378,9 +378,8 @@ msgstr "Изменить вид\\Переключение между борто msgid "Change player\\Change player" msgstr "Новый игрок\\Выберите имя для игрока" -#, fuzzy msgid "Changes" -msgstr "Задания" +msgstr "" msgid "Chapters:" msgstr "Разделы:" @@ -493,9 +492,8 @@ msgstr "Космический корабль" msgid "Descend\\Reduces the power of the jet" msgstr "Снижение и посадка\\Понижение мощности реактивного двигателя" -#, fuzzy msgid "Description:" -msgstr "Разрешение:" +msgstr "" msgid "Destroy" msgstr "Уничтожить" @@ -509,9 +507,8 @@ msgstr "Уничтожитель" msgid "Device\\Driver and resolution settings" msgstr "Устройство\\Драйвер и настройки разрешения" -#, fuzzy msgid "Disable\\Disable the selected mod" -msgstr "Удалить\\Удаление выбранного файла" +msgstr "" msgid "Dividing by zero" msgstr "Деление на ноль (запрещено!)" @@ -559,9 +556,8 @@ msgstr "Яйцо" msgid "Empty character constant" msgstr "" -#, fuzzy msgid "Enable\\Enable the selected mod" -msgstr "Загрузить\\Загрузить выбранную миссию" +msgstr "" msgid "End of block missing" msgstr "Отсутствует конец блока" @@ -1019,9 +1015,8 @@ msgstr "Нет" msgid "No changes." msgstr "" -#, fuzzy msgid "No description." -msgstr "Разрешение:" +msgstr "" msgid "No energy in the subsoil" msgstr "Под землей нет запасов энергии" @@ -1776,9 +1771,8 @@ msgstr "Юнит" msgid "Unknown Object" msgstr "Неизвестный объект" -#, fuzzy msgid "Unknown author" -msgstr "Неизвестная функция" +msgstr "" msgid "Unknown command" msgstr "Неизвестная команда" @@ -2111,10 +2105,6 @@ msgstr "epsitec.com" #~ msgid "Mouse shadow\\Gives the mouse a shadow" #~ msgstr "Тень мыши\\Мышь отбрасывает тень" -#, fuzzy -#~ msgid "No mods installed!" -#~ msgstr "Не установленны пользовательские уровни!" - #~ msgid "No other robot" #~ msgstr "Нет робота" From 253cca379b94575b699e7bc9438f3ad635c98384 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Thu, 23 Jul 2020 18:07:02 +0200 Subject: [PATCH 25/29] List which level subdirectory a mod changes --- src/app/modman.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/app/modman.cpp b/src/app/modman.cpp index 54596b63..76afa079 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -281,6 +281,17 @@ void CModManager::LoadModData(Mod& mod) // Changes data.changes = CResourceManager::ListDirectories("temp/mod"); + auto levelsIt = std::find(data.changes.begin(), data.changes.end(), "levels"); + if (levelsIt != data.changes.end()) + { + auto levelsDirs = CResourceManager::ListDirectories("temp/mod/levels"); + if (!levelsDirs.empty()) + { + std::transform(levelsDirs.begin(), levelsDirs.end(), levelsDirs.begin(), [](const std::string& dir) { return "levels/" + dir; }); + levelsIt = data.changes.erase(levelsIt); + data.changes.insert(levelsIt, levelsDirs.begin(), levelsDirs.end()); + } + } } void CModManager::MountMod(const Mod& mod, const std::string& mountPoint) From 93f3abee1ad333f9a3cc2898152adc66c86ca33a Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Fri, 24 Jul 2020 12:57:45 +0200 Subject: [PATCH 26/29] Change mod version separator to dot I don't know why I put comma in there and how I didn't notice this earlier. --- src/app/modman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modman.cpp b/src/app/modman.cpp index 76afa079..033ae458 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -251,7 +251,7 @@ void CModManager::LoadModData(Mod& mod) auto major = boost::lexical_cast(line->GetParam("major")->AsInt()); auto minor = boost::lexical_cast(line->GetParam("minor")->AsInt()); auto patch = boost::lexical_cast(line->GetParam("patch")->AsInt()); - data.version = boost::algorithm::join(std::vector{ major, minor, patch }, ","); + data.version = boost::algorithm::join(std::vector{ major, minor, patch }, "."); } } From fd36ff3840a7a58e9dea4a387052f944970ff74a Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Sat, 25 Jul 2020 21:20:19 +0200 Subject: [PATCH 27/29] Fix crash when changing text with UTF-8 chars The code left a byte from the previous text in CEdit UI control if the new text is shorter than the old one. So an exception was thrown because it's an invalid UTF-8 byte. --- src/ui/controls/edit.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ui/controls/edit.cpp b/src/ui/controls/edit.cpp index 62987308..570bd518 100644 --- a/src/ui/controls/edit.cpp +++ b/src/ui/controls/edit.cpp @@ -1299,7 +1299,9 @@ void CEdit::SetText(const std::string& text, bool bNew) if( m_len >= GetMaxChar() ) m_len = GetMaxChar(); m_text.resize( m_len + 1, '\0' ); + m_text[m_len] = '\0'; m_format.resize( m_len + 1, m_fontType ); + m_format[m_len] = m_fontType; font = m_fontType; j = 0; From 9cb80daedf10955eaacdccedde6a679d29f085b3 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Mon, 27 Jul 2020 15:59:33 +0200 Subject: [PATCH 28/29] Improve sound reloading Also some minor refactoring. Since realoding may take a lot of time, modman no longer reloads the whole app on entering/leaving the screen. --- src/app/app.cpp | 5 +- src/app/modman.cpp | 78 +++++++++++++++++++------ src/app/modman.h | 32 +++++++---- src/sound/oalsound/alsound.cpp | 35 +++++------ src/sound/oalsound/alsound.h | 1 + src/sound/sound.cpp | 4 ++ src/sound/sound.h | 4 ++ src/ui/screen/screen_mod_list.cpp | 96 ++++++++++++++----------------- src/ui/screen/screen_mod_list.h | 32 +---------- 9 files changed, 157 insertions(+), 130 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 72c2c2e1..ecd8cc1b 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -520,7 +520,9 @@ bool CApplication::Create() GetLogger()->Warn("Config could not be loaded. Default values will be used!\n"); } - m_modManager->Init(); + m_modManager->FindMods(); + m_modManager->SaveMods(); + m_modManager->MountAllMods(); // Create the sound instance. #ifdef OPENAL_SOUND @@ -1551,6 +1553,7 @@ void CApplication::StartLoadingMusic() SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp(); m_systemUtils->GetCurrentTimeStamp(musicLoadStart); + m_sound->Reset(); m_sound->CacheAll(); SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp(); diff --git a/src/app/modman.cpp b/src/app/modman.cpp index 033ae458..42ba11be 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -44,8 +44,11 @@ CModManager::CModManager(CApplication* app, CPathManager* pathManager) { } -void CModManager::Init() +void CModManager::FindMods() { + m_mods.clear(); + m_userChanges = false; + // Load names from the config file std::vector savedModNames; GetConfigFile().GetArrayProperty("Mods", "Names", savedModNames); @@ -103,9 +106,15 @@ void CModManager::Init() m_mods.push_back(mod); } - SaveMods(); - // Load the metadata for each mod + + // Unfortunately, the paths are distinguished by their real paths, not mount points + // So we must unmount mods temporarily + for (const auto& path : m_mountedModPaths) + { + UnmountMod(path); + } + for (auto& mod : m_mods) { MountMod(mod, "/temp/mod"); @@ -113,28 +122,30 @@ void CModManager::Init() UnmountMod(mod); } - UpdatePaths(); + // Mount back + for (const auto& path : m_mountedModPaths) + { + MountMod(path); + } } void CModManager::ReloadMods() { - UnmountAllMods(); - m_mods.clear(); - - Init(); - - // Apply the configuration + UnmountAllMountedMods(); + MountAllMods(); ReloadResources(); } void CModManager::EnableMod(size_t i) { m_mods[i].enabled = true; + m_userChanges = true; } void CModManager::DisableMod(size_t i) { m_mods[i].enabled = false; + m_userChanges = true; } size_t CModManager::MoveUp(size_t i) @@ -142,6 +153,7 @@ size_t CModManager::MoveUp(size_t i) if (i != 0) { std::swap(m_mods[i - 1], m_mods[i]); + m_userChanges = true; return i - 1; } else @@ -155,6 +167,7 @@ size_t CModManager::MoveDown(size_t i) if (i != m_mods.size() - 1) { std::swap(m_mods[i], m_mods[i + 1]); + m_userChanges = true; return i + 1; } else @@ -163,13 +176,27 @@ size_t CModManager::MoveDown(size_t i) } } -void CModManager::UpdatePaths() +bool CModManager::Changes() +{ + std::vector paths; + for (const auto& mod : m_mods) + { + if (mod.enabled) + { + paths.push_back(mod.path); + } + } + return paths != m_mountedModPaths || m_userChanges; +} + +void CModManager::MountAllMods() { for (const auto& mod : m_mods) { if (mod.enabled) { MountMod(mod); + m_mountedModPaths.push_back(mod.path); } } } @@ -192,6 +219,8 @@ void CModManager::SaveMods() GetConfigFile().SetArrayProperty("Mods", "Enabled", savedEnabled); GetConfigFile().Save(); + + m_userChanges = false; } size_t CModManager::CountMods() const @@ -296,23 +325,34 @@ void CModManager::LoadModData(Mod& mod) void CModManager::MountMod(const Mod& mod, const std::string& mountPoint) { - GetLogger()->Debug("Mounting mod: '%s' at path %s\n", mod.path.c_str(), mountPoint.c_str()); - CResourceManager::AddLocation(mod.path, true, mountPoint); + MountMod(mod.path, mountPoint); +} + +void CModManager::MountMod(const std::string& path, const std::string& mountPoint) +{ + GetLogger()->Debug("Mounting mod: '%s' at path %s\n", path.c_str(), mountPoint.c_str()); + CResourceManager::AddLocation(path, true, mountPoint); } void CModManager::UnmountMod(const Mod& mod) { - if (CResourceManager::LocationExists(mod.path)) + UnmountMod(mod.path); +} + +void CModManager::UnmountMod(const std::string& path) +{ + if (CResourceManager::LocationExists(path)) { - GetLogger()->Debug("Unmounting mod: '%s'\n", mod.path.c_str()); - CResourceManager::RemoveLocation(mod.path); + GetLogger()->Debug("Unmounting mod: '%s'\n", path.c_str()); + CResourceManager::RemoveLocation(path); } } -void CModManager::UnmountAllMods() +void CModManager::UnmountAllMountedMods() { - for (const auto& mod : m_mods) + for (const auto& path : m_mountedModPaths) { - UnmountMod(mod); + UnmountMod(path); } + m_mountedModPaths.clear(); } diff --git a/src/app/modman.h b/src/app/modman.h index a8af565e..09f8b0aa 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -51,21 +51,17 @@ struct Mod * The order matters since the order in which files are loaded matters, * because some files can be overwritten. * - * The changes in the list do not immediately apply - * and will be lost after the \ref ReloadMods call if they were not - * saved beforehand with \ref SaveMods. - * - * The changes are also lost to mods which are no longer found in the search paths. + * The changes in the list do not immediately apply. */ class CModManager { public: CModManager(CApplication* app, CPathManager* pathManager); - //! Loads mods without resource reloading; should be called only once after creation - void Init(); - //! Finds all the mods along with their metadata + void FindMods(); + + //! Applies the current configuration and reloads the application void ReloadMods(); //! Removes a mod from the list of loaded mods @@ -80,6 +76,9 @@ public: //! Moves the selected mod down in the list so that it's loaded later than others, returns the new index size_t MoveDown(size_t i); + //! Checks if the list of currently used mods differs from the current configuration or there were changes made by the user + bool Changes(); + //! Saves the current configuration of mods to the config file void SaveMods(); @@ -93,8 +92,8 @@ public: const std::vector& GetMods() const; private: - //! Updates the paths in Path Manager according to the current mod configuration - void UpdatePaths(); + // Allow access to MountAllMods() as CApplication doesn't want to reload itself during initialization + friend CApplication; //! Reloads application resources so the enabled mods are applied void ReloadResources(); @@ -102,13 +101,24 @@ private: //! Load mod data into mod void LoadModData(Mod& mod); + //! Updates the paths in Path Manager according to the current mod configuration + void MountAllMods(); + void MountMod(const Mod& mod, const std::string& mountPoint = ""); + void MountMod(const std::string& path, const std::string& mountPoint = ""); void UnmountMod(const Mod& mod); - void UnmountAllMods(); + void UnmountMod(const std::string& path); + void UnmountAllMountedMods(); private: CApplication* m_app; CPathManager* m_pathManager; + //! Paths to mods already in the virtual filesystem + std::vector m_mountedModPaths; + + //! List of mods std::vector m_mods; + + bool m_userChanges = false; }; diff --git a/src/sound/oalsound/alsound.cpp b/src/sound/oalsound/alsound.cpp index f27a462b..26006198 100644 --- a/src/sound/oalsound/alsound.cpp +++ b/src/sound/oalsound/alsound.cpp @@ -47,22 +47,7 @@ void CALSound::CleanUp() if (m_enabled) { GetLogger()->Info("Unloading files and closing device...\n"); - StopAll(); - StopMusic(); - - m_channels.clear(); - - m_currentMusic.reset(); - - m_oldMusic.clear(); - - m_previousMusic.music.reset(); - - m_sounds.clear(); - - m_music.clear(); - - m_enabled = false; + Reset(); alcDestroyContext(m_context); alcCloseDevice(m_device); @@ -99,6 +84,24 @@ bool CALSound::Create() return true; } +void CALSound::Reset() +{ + StopAll(); + StopMusic(); + + m_channels.clear(); + + m_currentMusic.reset(); + + m_oldMusic.clear(); + + m_previousMusic.music.reset(); + + m_sounds.clear(); + + m_music.clear(); +} + bool CALSound::GetEnable() { return m_enabled; diff --git a/src/sound/oalsound/alsound.h b/src/sound/oalsound/alsound.h index 6f76c268..75e3d7f6 100644 --- a/src/sound/oalsound/alsound.h +++ b/src/sound/oalsound/alsound.h @@ -84,6 +84,7 @@ public: ~CALSound(); bool Create() override; + void Reset() override; bool Cache(SoundType, const std::string &) override; void CacheMusic(const std::string &) override; bool IsCached(SoundType) override; diff --git a/src/sound/sound.cpp b/src/sound/sound.cpp index 1cc23c4b..87d7266c 100644 --- a/src/sound/sound.cpp +++ b/src/sound/sound.cpp @@ -52,6 +52,10 @@ void CSoundInterface::CacheAll() } } +void CSoundInterface::Reset() +{ +} + bool CSoundInterface::Cache(SoundType sound, const std::string &file) { return true; diff --git a/src/sound/sound.h b/src/sound/sound.h index 78a5449d..314eb856 100644 --- a/src/sound/sound.h +++ b/src/sound/sound.h @@ -72,6 +72,10 @@ public: */ void CacheAll(); + /** Stop all sounds and music and clean cache. + */ + virtual void Reset(); + /** Function called to cache sound effect file. * This function is called by plugin interface for each file. * \param sound - id of a file, will be used to identify sound files diff --git a/src/ui/screen/screen_mod_list.cpp b/src/ui/screen/screen_mod_list.cpp index f151f781..856f2a20 100644 --- a/src/ui/screen/screen_mod_list.cpp +++ b/src/ui/screen/screen_mod_list.cpp @@ -36,6 +36,8 @@ #include "math/func.h" +#include "sound/sound.h" + #include "ui/controls/button.h" #include "ui/controls/edit.h" #include "ui/controls/interface.h" @@ -64,9 +66,6 @@ void CScreenModList::CreateInterface() Math::Point pos, ddim; std::string name; - m_changes = false; - ApplyChanges(); - // Display the window pos.x = 0.10f; pos.y = 0.10f; @@ -104,8 +103,6 @@ void CScreenModList::CreateInterface() pli->SetState(STATE_SHADOW); pli->SetState(STATE_EXTEND); - UpdateModList(); - // Displays the mod details pos.x = ox+sx*9.5f; pos.y = oy+sy*10.5f; @@ -124,8 +121,6 @@ void CScreenModList::CreateInterface() pe->SetEditCap(false); // just to see pe->SetHighlightCap(true); - UpdateModDetails(); - pos = pli->GetPos(); ddim = pli->GetDim(); @@ -148,8 +143,6 @@ void CScreenModList::CreateInterface() pe->SetEditCap(false); // just to see pe->SetHighlightCap(true); - UpdateModSummary(); - // Apply button pos.x = ox+sx*13.75f; pos.y = oy+sy*2; @@ -158,16 +151,12 @@ void CScreenModList::CreateInterface() pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MODS_APPLY); pb->SetState(STATE_SHADOW); - UpdateApplyButton(); - // Display the enable/disable button pos.x -= dim.x*2.3f; ddim.x = dim.x*2.0f; pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_MOD_ENABLE_OR_DISABLE); pb->SetState(STATE_SHADOW); - UpdateEnableDisableButton(); - // Display the move up button pos.x -= dim.x*0.8f; pos.y = oy+sy*2.48; @@ -181,8 +170,6 @@ void CScreenModList::CreateInterface() pb = pw->CreateButton(pos, ddim, 50, EVENT_INTERFACE_MOD_MOVE_DOWN); pb->SetState(STATE_SHADOW); - UpdateUpDownButtons(); - // Display the refresh button pos.x -= dim.x*1.3f; pos.y = oy+sy*2; @@ -207,6 +194,9 @@ void CScreenModList::CreateInterface() pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK); pb->SetState(STATE_SHADOW); + FindMods(); + UpdateAll(); + // Background SetBackground("textures/interface/interface.png"); CreateVersionDisplay(); @@ -231,7 +221,7 @@ bool CScreenModList::EventProcess(const Event &event) event.type == EVENT_INTERFACE_BACK || (event.type == EVENT_KEY_DOWN && event.GetData()->key == KEY(ESCAPE))) { - if (m_changes) + if (m_modManager->Changes()) { m_dialog->StartQuestion(RT_DIALOG_CHANGES_QUESTION, true, true, false, [this]() @@ -241,14 +231,11 @@ bool CScreenModList::EventProcess(const Event &event) }, [this]() { - m_changes = false; // do not save changes on "No" - ApplyChanges(); CloseWindow(); }); } else { - ApplyChanges(); CloseWindow(); } return false; @@ -278,7 +265,6 @@ bool CScreenModList::EventProcess(const Event &event) } UpdateModList(); UpdateEnableDisableButton(); - m_changes = true; UpdateApplyButton(); break; @@ -286,7 +272,6 @@ bool CScreenModList::EventProcess(const Event &event) m_modSelectedIndex = m_modManager->MoveUp(m_modSelectedIndex); UpdateModList(); UpdateUpDownButtons(); - m_changes = true; UpdateApplyButton(); break; @@ -294,20 +279,21 @@ bool CScreenModList::EventProcess(const Event &event) m_modSelectedIndex = m_modManager->MoveDown(m_modSelectedIndex); UpdateModList(); UpdateUpDownButtons(); - m_changes = true; UpdateApplyButton(); break; case EVENT_INTERFACE_MODS_REFRESH: + // Apply any changes before refresh so that the config file + // is better synchronized with the state of the game case EVENT_INTERFACE_MODS_APPLY: ApplyChanges(); - // Update the whole UI - UpdateModList(); - UpdateModSummary(); - UpdateModDetails(); - UpdateEnableDisableButton(); - UpdateApplyButton(); - UpdateUpDownButtons(); + UpdateAll(); + // Start playing the main menu music again + if (!m_app->GetSound()->IsPlayingMusic()) + { + m_app->GetSound()->PlayMusic("music/Intro1.ogg", false); + m_app->GetSound()->CacheMusic("music/Intro2.ogg"); + } break; case EVENT_INTERFACE_MODS_DIR: @@ -341,19 +327,19 @@ bool CScreenModList::EventProcess(const Event &event) return false; } +void CScreenModList::FindMods() +{ + m_modManager->FindMods(); + if (m_modManager->CountMods() != 0) + { + m_modSelectedIndex = Math::Clamp(m_modSelectedIndex, static_cast(0), m_modManager->CountMods() - 1); + } +} + void CScreenModList::ApplyChanges() { - if (m_changes) - { - m_changes = false; - m_modManager->SaveMods(); - } - + m_modManager->SaveMods(); m_modManager->ReloadMods(); - - m_empty = (m_modManager->CountMods() == 0); - - m_modSelectedIndex = Math::Clamp(m_modSelectedIndex, static_cast(0), m_modManager->CountMods() - 1); } void CScreenModList::CloseWindow() @@ -361,6 +347,16 @@ void CScreenModList::CloseWindow() m_main->ChangePhase(PHASE_MAIN_MENU); } +void CScreenModList::UpdateAll() +{ + UpdateModList(); + UpdateModDetails(); + UpdateModSummary(); + UpdateEnableDisableButton(); + UpdateApplyButton(); + UpdateUpDownButtons(); +} + void CScreenModList::UpdateModList() { CWindow* pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); @@ -371,16 +367,16 @@ void CScreenModList::UpdateModList() pl->Flush(); - if (m_empty) + if (m_modManager->CountMods() == 0) { return; } - auto mods = m_modManager->GetMods(); + const auto& mods = m_modManager->GetMods(); for (size_t i = 0; i < mods.size(); ++i) { const auto& mod = mods[i]; - auto name = mod.data.displayName; + const auto& name = mod.data.displayName; pl->SetItemName(i, name); pl->SetCheck(i, mod.enabled); pl->SetEnable(i, true); @@ -398,7 +394,7 @@ void CScreenModList::UpdateModDetails() CEdit* pe = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_DETAILS)); if (pe == nullptr) return; - if (m_empty) + if (m_modManager->CountMods() == 0) { pe->SetText("No information"); return; @@ -475,7 +471,7 @@ void CScreenModList::UpdateModSummary() std::string noSummary; GetResource(RES_TEXT, RT_MOD_NO_SUMMARY, noSummary); - if (m_empty) + if (m_modManager->CountMods() == 0) { pe->SetText(noSummary); return; @@ -503,7 +499,7 @@ void CScreenModList::UpdateEnableDisableButton() std::string buttonName{}; - if (m_empty) + if (m_modManager->CountMods() == 0) { pb->ClearState(STATE_ENABLE); @@ -536,13 +532,7 @@ void CScreenModList::UpdateApplyButton() CButton* pb = static_cast(pw->SearchControl(EVENT_INTERFACE_MODS_APPLY)); if (pb == nullptr) return; - if (m_empty) - { - pb->ClearState(STATE_ENABLE); - return; - } - - if (m_changes) + if (m_modManager->Changes()) { pb->SetState(STATE_ENABLE); } @@ -563,7 +553,7 @@ void CScreenModList::UpdateUpDownButtons() CButton* pb_down = static_cast(pw->SearchControl(EVENT_INTERFACE_MOD_MOVE_DOWN)); if (pb_down == nullptr) return; - if (m_empty) + if (m_modManager->CountMods() == 0) { pb_up->ClearState(STATE_ENABLE); pb_down->ClearState(STATE_ENABLE); diff --git a/src/ui/screen/screen_mod_list.h b/src/ui/screen/screen_mod_list.h index 639fd775..ca2c130b 100644 --- a/src/ui/screen/screen_mod_list.h +++ b/src/ui/screen/screen_mod_list.h @@ -33,34 +33,6 @@ namespace Ui /** * \class CScreenModList * \brief This class is the front-end for the \ref CModManager. - * - * \section Assumptions Assumptions - * - * It assumes the user is changing something in the mods folders while the screen is visible, - * e.g. removing them or adding more. For this reason, the mods are always reloaded after the user - * lefts the screen, possibly after asking the user if their changes should be saved. They are also reloaded - * when the user opens the screen to avoid weird situations like "the mod is no longer there but in theory - * it's still in the game's memory even if it's not visible on the list". - * - * Unsafe changes, like removing a mod which is still enabled, are a sole responsibility of the user and - * we basically can't guarantee the game will behave properly in such cases even when they happen while - * this screen is visible. - * - * \section Features UI Features - * - * The user can reorder mods with appropriate buttons and enable/disable them. To avoid reloading - * the resources after every change, the changes are not immediate. The resources are reloaded in the - * cases described above and also after using the Apply or Refresh buttons. The only technical - * difference between them is that the Refresh button is always enabled, but Apply is only enabled - * if the user made any changes in the list by using the UI. The reason is, again, to avoid dealing with - * weird situations like described above. - * - * The UI also shows the selected mod metadata like description, version, etc. - * - * There is also a button which will try to open the default web browser with the Workshop website, - * where the user can search for new mods. - * - * For convenience, also a button opening a saves/mods folder is provided. */ class CScreenModList : public CScreen { @@ -71,9 +43,11 @@ public: bool EventProcess(const Event &event) override; protected: + void FindMods(); void ApplyChanges(); void CloseWindow(); + void UpdateAll(); void UpdateModList(); void UpdateModDetails(); void UpdateModSummary(); @@ -87,8 +61,6 @@ protected: CModManager* m_modManager; size_t m_modSelectedIndex = 0; - bool m_changes = false; - bool m_empty = true; }; } // namespace Ui From 68c254c1010c4971769e202c46f6036bef5e5f04 Mon Sep 17 00:00:00 2001 From: Fiftytwo Date: Fri, 21 Aug 2020 18:09:28 +0200 Subject: [PATCH 29/29] Update data submodule --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index 611cbfdd..069fc5bd 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 611cbfdd079e97a71f97810636f2ab2358cb4eeb +Subproject commit 069fc5bd15b87aaf5b1b301a787f05a99dfc3856