Add handling of mods manifests

Also add Polish translations for mod manager related strings.
pyro-refactor
MrSimbax 2020-07-22 16:18:18 +02:00
parent eac74c23ec
commit f57da76ae8
19 changed files with 468 additions and 84 deletions

View File

@ -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")

View File

@ -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 ""

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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

114
src/app/moddata.cpp Normal file
View File

@ -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 <boost/optional.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <istream>
namespace pt = boost::property_tree;
boost::optional<pt::ptree> LoadManifest(const std::string& path, bool loaded = false);
std::string GetStringProperty(const pt::ptree& manifest, const std::string& key);
std::unordered_map<Language, std::string> 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<pt::ptree> LoadManifest(const std::string& path, bool loaded)
{
try
{
auto inputStream = MakeUnique<CInputStream>("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<std::string>(key);
if (prop.has_value())
{
return prop.get();
}
return {};
}
std::unordered_map<Language, std::string> GetLanguageStringProperty(const pt::ptree& manifest, const std::string& key)
{
std::unordered_map<Language, std::string> 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;
}

36
src/app/moddata.h Normal file
View File

@ -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 <unordered_map>
struct ModData
{
std::unordered_map<Language, std::string> displayName{};
std::string author{};
std::string version{};
std::string website{};
std::unordered_map<Language, std::string> summary{};
};
//! Loads the metadata for a mod in the given path.
ModData LoadModData(const std::string& path, bool loaded = false);

View File

@ -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<std::string> 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)

View File

@ -19,13 +19,12 @@
#pragma once
#include "ui/maindialog.h"
#include "ui/screen/screen_setup.h"
#include "app/moddata.h"
#include <unordered_map>
#include <boost/optional.hpp>
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<Mod>& 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<Mod> m_mods;
};

View File

@ -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())

View File

@ -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");

View File

@ -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
};

View File

@ -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<size_t>(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<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if (pw == nullptr) return;
CEdit* pe = static_cast<CEdit*>(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<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if (pw == nullptr) return;
CEdit* pe = static_cast<CEdit*>(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<Language, std::string> 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

View File

@ -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<Language, std::string> property, const std::string& fallback);
protected:
Ui::CMainDialog* m_dialog;