From 0104c77d618651b039da72ef588f299f224f4d17 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Mon, 12 Dec 2022 03:27:59 +0400 Subject: [PATCH 01/35] Interface builder for random map tab --- client/CMakeLists.txt | 2 + client/gui/InterfaceBuilder.cpp | 208 +++++++++++++ client/gui/InterfaceBuilder.h | 39 +++ client/gui/NotificationHandler.h | 2 +- client/lobby/RandomMapTab.cpp | 158 +++------- client/lobby/RandomMapTab.h | 22 +- config/windows.json | 513 +++++++++++++++++++++++++++++++ 7 files changed, 804 insertions(+), 140 deletions(-) create mode 100644 client/gui/InterfaceBuilder.cpp create mode 100644 client/gui/InterfaceBuilder.h create mode 100644 config/windows.json diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 07fd813ae..18886669b 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -15,6 +15,7 @@ set(client_SRCS gui/Geometries.cpp gui/SDL_Extensions.cpp gui/NotificationHandler.cpp + gui/InterfaceBuilder.cpp widgets/AdventureMapClasses.cpp widgets/Buttons.cpp @@ -90,6 +91,7 @@ set(client_HEADERS gui/SDL_Extensions.h gui/SDL_Pixels.h gui/NotificationHandler.h + gui/InterfaceBuilder.h widgets/AdventureMapClasses.h widgets/Buttons.h diff --git a/client/gui/InterfaceBuilder.cpp b/client/gui/InterfaceBuilder.cpp new file mode 100644 index 000000000..db079f099 --- /dev/null +++ b/client/gui/InterfaceBuilder.cpp @@ -0,0 +1,208 @@ +/* +* InterfaceBuilder.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#include "StdInc.h" + +#include "InterfaceBuilder.h" + +#include "../CGameInfo.h" +#include "../gui/CAnimation.h" +#include "../gui/CGuiHandler.h" +#include "../widgets/CComponent.h" +#include "../widgets/Buttons.h" +#include "../widgets/MiscWidgets.h" +#include "../widgets/ObjectLists.h" +#include "../widgets/TextControls.h" +#include "../windows/GUIClasses.h" +#include "../windows/InfoWindows.h" + +#include "../../lib/CGeneralTextHandler.h" + + +InterfaceBuilder::InterfaceBuilder(const JsonNode & config): + CIntObject() +{ + init(config); +} + +InterfaceBuilder::InterfaceBuilder(): + CIntObject() +{ +} + +void InterfaceBuilder::addCallback(const std::string & callbackName, std::function<void(int)> callback) +{ + callbacks[callbackName] = callback; +} + +const std::shared_ptr<CIntObject> InterfaceBuilder::widget(const std::string & name) const +{ + return widgets.at(name); +} + +void InterfaceBuilder::init(const JsonNode &config) +{ + OBJ_CONSTRUCTION; + int unnamedObjectId = 0; + const std::string unnamedObjectPrefix = "__widget_"; + + for(const auto & item : config["items"].Vector()) + { + std::string name = item["name"].isNull() + ? unnamedObjectPrefix + std::to_string(unnamedObjectId++) + : item["name"].String(); + widgets[name] = buildWidget(item); + } +} + +std::string InterfaceBuilder::buildText(const JsonNode & config) const +{ + if(config.isNull()) + return ""; + + if(config.isNumber()) + { + return CGI->generaltexth->allTexts[config.Integer()]; + } + return config.String(); +} + +std::shared_ptr<CIntObject> InterfaceBuilder::buildWidget(const JsonNode & config) +{ + assert(!config.isNull()); + auto type = config["type"].String(); + + int x = 0, y = 0; + if(!config["position"].isNull()) + { + x = config["position"]["x"].Integer(); + y = config["position"]["y"].Integer(); + } + + std::string image, text = buildText(config["text"]); + auto alignment = EAlignment::CENTER; + auto color = Colors::DEFAULT_KEY_COLOR; + auto font = EFonts::FONT_TIMES; + + if(!config["image"].isNull()) + image = config["image"].String(); + if(!config["alignment"].isNull()) + { + if(config["alignment"].String() == "left") + alignment = EAlignment::TOPLEFT; + if(config["alignment"].String() == "center") + alignment = EAlignment::CENTER; + if(config["alignment"].String() == "right") + alignment = EAlignment::BOTTOMRIGHT; + } + if(!config["color"].isNull()) + { + if(config["color"].String() == "yellow") + color = Colors::YELLOW; + if(config["color"].String() == "white") + color = Colors::WHITE; + if(config["color"].String() == "gold") + color = Colors::METALLIC_GOLD; + if(config["color"].String() == "green") + color = Colors::GREEN; + if(config["color"].String() == "orange") + color = Colors::ORANGE; + if(config["color"].String() == "bright-yellow") + color = Colors::BRIGHT_YELLOW; + } + if(!config["font"].isNull()) + { + if(config["font"].String() == "big") + font = EFonts::FONT_BIG; + if(config["font"].String() == "medium") + font = EFonts::FONT_MEDIUM; + if(config["font"].String() == "small") + font = EFonts::FONT_SMALL; + if(config["font"].String() == "tiny") + font = EFonts::FONT_TINY; + } + + + if(type == "picture") + { + return std::make_shared<CPicture>(image, x, y); + } + if(type == "label") + { + return std::make_shared<CLabel>(x, y, font, alignment, color, text); + } + if(type == "toggleGroup") + { + auto group = std::make_shared<CToggleGroup>(0); + group->pos.x += x; + group->pos.y += y; + if(!config["items"].isNull()) + { + SObjectConstruction obj__i(group.get()); + int itemIdx = -1; + for(const auto & item : config["items"].Vector()) + { + itemIdx = item["index"].isNull() ? itemIdx + 1 : item["index"].Integer(); + group->addToggle(itemIdx, std::dynamic_pointer_cast<CToggleBase>(buildWidget(item))); + } + } + if(!config["selected"].isNull()) + group->setSelected(config["selected"].Integer()); + if(!config["callback"].isNull()) + group->addCallback(callbacks[config["callback"].String()]); + return group; + } + if(type == "toggleButton") + { + std::pair<std::string, std::string> zelp; + if(!config["zelp"].isNull()) + zelp = CGI->generaltexth->zelp[config["zelp"].Integer()]; + auto button = std::make_shared<CToggleButton>(Point(x, y), image, zelp); + if(!config["selected"].isNull()) + button->setSelected(config["selected"].Bool()); + if(!config["imageOrder"].isNull()) + { + auto imgOrder = config["imageOrder"].Vector(); + assert(imgOrder.size() >= 4); + button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer()); + } + if(!config["callback"].isNull()) + button->addCallback(callbacks[config["callback"].String()]); + return button; + } + if(type == "button") + { + std::pair<std::string, std::string> zelp; + if(!config["zelp"].isNull()) + zelp = CGI->generaltexth->zelp[config["zelp"].Integer()]; + auto button = std::make_shared<CButton>(Point(x, y), image, zelp); + return button; + } + if(type == "labelGroup") + { + auto group = std::make_shared<CLabelGroup>(font, alignment, color); + if(!config["items"].isNull()) + { + for(const auto & item : config["items"].Vector()) + { + if(!item["position"].isNull()) + { + x = item["position"]["x"].Integer(); + y = item["position"]["y"].Integer(); + } + if(!item["text"].isNull()) + text = buildText(item["text"]); + group->add(x, y, text); + } + } + return group; + } + return std::shared_ptr<CIntObject>(nullptr); +} diff --git a/client/gui/InterfaceBuilder.h b/client/gui/InterfaceBuilder.h new file mode 100644 index 000000000..00740d7ef --- /dev/null +++ b/client/gui/InterfaceBuilder.h @@ -0,0 +1,39 @@ +/* +* InterfaceBuilder.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#pragma once + +#include "CIntObject.h" + +#include "../../lib/JsonNode.h" + +class InterfaceBuilder: public CIntObject +{ +public: + InterfaceBuilder(); + InterfaceBuilder(const JsonNode & config); + +protected: + //must be called after adding callbacks + void init(const JsonNode & config); + + void addCallback(const std::string & callbackName, std::function<void(int)> callback); + + const std::shared_ptr<CIntObject> widget(const std::string &) const; + +private: + + std::map<std::string, std::shared_ptr<CIntObject>> widgets; + std::map<std::string, std::function<void(int)>> callbacks; + + std::shared_ptr<CIntObject> buildWidget(const JsonNode & config); + + std::string buildText(const JsonNode & param) const; +}; diff --git a/client/gui/NotificationHandler.h b/client/gui/NotificationHandler.h index a1ac262a7..536e0978d 100644 --- a/client/gui/NotificationHandler.h +++ b/client/gui/NotificationHandler.h @@ -1,5 +1,5 @@ /* -* NotificationHandler.cpp, part of VCMI engine +* NotificationHandler.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index d945388cf..2d5848b99 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -27,127 +27,72 @@ #include "../../lib/CGeneralTextHandler.h" #include "../../lib/mapping/CMapInfo.h" #include "../../lib/rmg/CMapGenOptions.h" +#include "../../lib/CModHandler.h" -RandomMapTab::RandomMapTab() +RandomMapTab::RandomMapTab(): + InterfaceBuilder() { recActions = 0; mapGenOptions = std::make_shared<CMapGenOptions>(); - OBJ_CONSTRUCTION; - background = std::make_shared<CPicture>("RANMAPBK", 0, 6); - - labelHeadlineBig = std::make_shared<CLabel>(222, 36, FONT_BIG, EAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[738]); - labelHeadlineSmall = std::make_shared<CLabel>(222, 56, FONT_SMALL, EAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[739]); - - labelMapSize = std::make_shared<CLabel>(104, 97, FONT_SMALL, EAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[752]); - groupMapSize = std::make_shared<CToggleGroup>(0); - groupMapSize->pos.y += 81; - groupMapSize->pos.x += 158; - const std::vector<std::string> mapSizeBtns = {"RANSIZS", "RANSIZM", "RANSIZL", "RANSIZX"}; - addButtonsToGroup(groupMapSize.get(), mapSizeBtns, 0, 3, 47, 198); - groupMapSize->setSelected(1); - groupMapSize->addCallback([&](int btnId) + + const JsonNode config(ResourceID("config/windows.json")); + addCallback("toggleMapSize", [&](int btnId) { auto mapSizeVal = getPossibleMapSizes(); mapGenOptions->setWidth(mapSizeVal[btnId]); mapGenOptions->setHeight(mapSizeVal[btnId]); updateMapInfoByHost(); }); - - buttonTwoLevels = std::make_shared<CToggleButton>(Point(346, 81), "RANUNDR", CGI->generaltexth->zelp[202]); - buttonTwoLevels->setSelected(true); - buttonTwoLevels->addCallback([&](bool on) + addCallback("toggleTwoLevels", [&](bool on) { mapGenOptions->setHasTwoLevels(on); updateMapInfoByHost(); }); - - labelGroupForOptions = std::make_shared<CLabelGroup>(FONT_SMALL, EAlignment::TOPLEFT, Colors::WHITE); - // Create number defs list - std::vector<std::string> numberDefs; - for(int i = 0; i <= 8; ++i) - { - numberDefs.push_back("RANNUM" + boost::lexical_cast<std::string>(i)); - } - - const int NUMBERS_WIDTH = 32; - const int BTNS_GROUP_LEFT_MARGIN = 67; - labelGroupForOptions->add(68, 133, CGI->generaltexth->allTexts[753]); - groupMaxPlayers = std::make_shared<CToggleGroup>(0); - groupMaxPlayers->pos.y += 153; - groupMaxPlayers->pos.x += BTNS_GROUP_LEFT_MARGIN; - addButtonsWithRandToGroup(groupMaxPlayers.get(), numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212); - groupMaxPlayers->addCallback([&](int btnId) + + addCallback("setPlayersCount", [&](int btnId) { mapGenOptions->setPlayerCount(btnId); - deactivateButtonsFrom(groupMaxTeams.get(), btnId); + + deactivateButtonsFrom(dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams")).get(), btnId); // deactive some CompOnlyPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I - deactivateButtonsFrom(groupCompOnlyPlayers.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); + deactivateButtonsFrom(dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers")).get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); validatePlayersCnt(btnId); updateMapInfoByHost(); }); - - labelGroupForOptions->add(68, 199, CGI->generaltexth->allTexts[754]); - groupMaxTeams = std::make_shared<CToggleGroup>(0); - groupMaxTeams->pos.y += 219; - groupMaxTeams->pos.x += BTNS_GROUP_LEFT_MARGIN; - addButtonsWithRandToGroup(groupMaxTeams.get(), numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222); - groupMaxTeams->addCallback([&](int btnId) + + addCallback("setTeamsCount", [&](int btnId) { mapGenOptions->setTeamCount(btnId); updateMapInfoByHost(); }); - - labelGroupForOptions->add(68, 265, CGI->generaltexth->allTexts[755]); - groupCompOnlyPlayers = std::make_shared<CToggleGroup>(0); - groupCompOnlyPlayers->pos.y += 285; - groupCompOnlyPlayers->pos.x += BTNS_GROUP_LEFT_MARGIN; - addButtonsWithRandToGroup(groupCompOnlyPlayers.get(), numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232); - groupCompOnlyPlayers->addCallback([&](int btnId) + + addCallback("setCompOnlyPlayers", [&](int btnId) { mapGenOptions->setCompOnlyPlayerCount(btnId); - + // deactive some MaxPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I - deactivateButtonsFrom(groupMaxPlayers.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); + deactivateButtonsFrom(dynamic_pointer_cast<CToggleGroup>(widget("groupMaxPlayers")).get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); - deactivateButtonsFrom(groupCompOnlyTeams.get(), (btnId == 0 ? 1 : btnId)); + deactivateButtonsFrom(dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams")).get(), (btnId == 0 ? 1 : btnId)); validateCompOnlyPlayersCnt(btnId); updateMapInfoByHost(); }); - - labelGroupForOptions->add(68, 331, CGI->generaltexth->allTexts[756]); - groupCompOnlyTeams = std::make_shared<CToggleGroup>(0); - groupCompOnlyTeams->pos.y += 351; - groupCompOnlyTeams->pos.x += BTNS_GROUP_LEFT_MARGIN; - addButtonsWithRandToGroup(groupCompOnlyTeams.get(), numberDefs, 0, 6, NUMBERS_WIDTH, 234, 241); - deactivateButtonsFrom(groupCompOnlyTeams.get(), 1); - groupCompOnlyTeams->addCallback([&](int btnId) + + addCallback("setCompOnlyTeams", [&](int btnId) { mapGenOptions->setCompOnlyTeamCount(btnId); updateMapInfoByHost(); }); - - labelGroupForOptions->add(68, 398, CGI->generaltexth->allTexts[757]); - const int WIDE_BTN_WIDTH = 85; - groupWaterContent = std::make_shared<CToggleGroup>(0); - groupWaterContent->pos.y += 419; - groupWaterContent->pos.x += BTNS_GROUP_LEFT_MARGIN; - const std::vector<std::string> waterContentBtns = {"RANNONE", "RANNORM", "RANISLD"}; - addButtonsWithRandToGroup(groupWaterContent.get(), waterContentBtns, 0, 2, WIDE_BTN_WIDTH, 243, 246); - groupWaterContent->addCallback([&](int btnId) + + addCallback("setWaterContent", [&](int btnId) { mapGenOptions->setWaterContent(static_cast<EWaterContent::EWaterContent>(btnId)); updateMapInfoByHost(); }); - - labelGroupForOptions->add(68, 465, CGI->generaltexth->allTexts[758]); - groupMonsterStrength = std::make_shared<CToggleGroup>(0); - groupMonsterStrength->pos.y += 485; - groupMonsterStrength->pos.x += BTNS_GROUP_LEFT_MARGIN; - const std::vector<std::string> monsterStrengthBtns = {"RANWEAK", "RANNORM", "RANSTRG"}; - addButtonsWithRandToGroup(groupMonsterStrength.get(), monsterStrengthBtns, 2, 4, WIDE_BTN_WIDTH, 248, 251, EMonsterStrength::RANDOM, false); - groupMonsterStrength->addCallback([&](int btnId) + + addCallback("setMonsterStrenght", [&](int btnId) { if(btnId < 0) mapGenOptions->setMonsterStrength(EMonsterStrength::RANDOM); @@ -155,9 +100,9 @@ RandomMapTab::RandomMapTab() mapGenOptions->setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId)); //value 2 to 4 updateMapInfoByHost(); }); - - buttonShowRandomMaps = std::make_shared<CButton>(Point(54, 535), "RANSHOW", CGI->generaltexth->zelp[252]); - + + init(config["randomMapTab"]); + updateMapInfoByHost(); } @@ -216,39 +161,14 @@ void RandomMapTab::updateMapInfoByHost() void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) { - groupMapSize->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth())); - buttonTwoLevels->setSelected(opts->getHasTwoLevels()); - groupMaxPlayers->setSelected(opts->getPlayerCount()); - groupMaxTeams->setSelected(opts->getTeamCount()); - groupCompOnlyPlayers->setSelected(opts->getCompOnlyPlayerCount()); - groupCompOnlyTeams->setSelected(opts->getCompOnlyTeamCount()); - groupWaterContent->setSelected(opts->getWaterContent()); - groupMonsterStrength->setSelected(opts->getMonsterStrength()); -} - -void RandomMapTab::addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, int helpRandIndex, int randIndex, bool animIdfromBtnId) const -{ - addButtonsToGroup(group, defs, nStart, nEnd, btnWidth, helpStartIndex, animIdfromBtnId); - - // Buttons are relative to button group, TODO better solution? - SObjectConstruction obj__i(group); - const std::string RANDOM_DEF = "RANRAND"; - group->addToggle(randIndex, std::make_shared<CToggleButton>(Point(256, 0), RANDOM_DEF, CGI->generaltexth->zelp[helpRandIndex])); - group->setSelected(randIndex); -} - -void RandomMapTab::addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, bool animIdfromBtnId) const -{ - // Buttons are relative to button group, TODO better solution? - SObjectConstruction obj__i(group); - int cnt = nEnd - nStart + 1; - for(int i = 0; i < cnt; ++i) - { - auto button = std::make_shared<CToggleButton>(Point(i * btnWidth, 0), animIdfromBtnId ? defs[i + nStart] : defs[i], CGI->generaltexth->zelp[helpStartIndex + i]); - // For blocked state we should use pressed image actually - button->setImageOrder(0, 1, 1, 3); - group->addToggle(i + nStart, button); - } + dynamic_pointer_cast<CToggleGroup>(widget("groupMapSize"))->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth())); + dynamic_pointer_cast<CToggleButton>(widget("buttonTwoLevels"))->setSelected(opts->getHasTwoLevels()); + dynamic_pointer_cast<CToggleGroup>(widget("groupMaxPlayers"))->setSelected(opts->getPlayerCount()); + dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))->setSelected(opts->getTeamCount()); + dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))->setSelected(opts->getCompOnlyPlayerCount()); + dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))->setSelected(opts->getCompOnlyTeamCount()); + dynamic_pointer_cast<CToggleGroup>(widget("groupWaterContent"))->setSelected(opts->getWaterContent()); + dynamic_pointer_cast<CToggleGroup>(widget("groupMonsterStrength"))->setSelected(opts->getMonsterStrength()); } void RandomMapTab::deactivateButtonsFrom(CToggleGroup * group, int startId) @@ -280,13 +200,13 @@ void RandomMapTab::validatePlayersCnt(int playersCnt) if(mapGenOptions->getTeamCount() >= playersCnt) { mapGenOptions->setTeamCount(playersCnt - 1); - groupMaxTeams->setSelected(mapGenOptions->getTeamCount()); + dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))->setSelected(mapGenOptions->getTeamCount()); } // total players should not exceed PlayerColor::PLAYER_LIMIT_I (8 in homm3) if(mapGenOptions->getCompOnlyPlayerCount() + playersCnt > PlayerColor::PLAYER_LIMIT_I) { mapGenOptions->setCompOnlyPlayerCount(PlayerColor::PLAYER_LIMIT_I - playersCnt); - groupCompOnlyPlayers->setSelected(mapGenOptions->getCompOnlyPlayerCount()); + dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))->setSelected(mapGenOptions->getCompOnlyPlayerCount()); } validateCompOnlyPlayersCnt(mapGenOptions->getCompOnlyPlayerCount()); @@ -304,7 +224,7 @@ void RandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt) int compOnlyTeamCount = compOnlyPlayersCnt == 0 ? 0 : compOnlyPlayersCnt - 1; mapGenOptions->setCompOnlyTeamCount(compOnlyTeamCount); updateMapInfoByHost(); - groupCompOnlyTeams->setSelected(compOnlyTeamCount); + dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))->setSelected(compOnlyTeamCount); } } diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index f7e399a17..ac78247f9 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -13,6 +13,7 @@ #include "../../lib/FunctionList.h" #include "../../lib/GameConstants.h" +#include "../gui/InterfaceBuilder.h" VCMI_LIB_NAMESPACE_BEGIN @@ -24,7 +25,7 @@ class CToggleButton; class CLabel; class CLabelGroup; -class RandomMapTab : public CIntObject +class RandomMapTab : public InterfaceBuilder { public: RandomMapTab(); @@ -35,30 +36,11 @@ public: CFunctionList<void(std::shared_ptr<CMapInfo>, std::shared_ptr<CMapGenOptions>)> mapInfoChanged; private: - void addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, int helpRandIndex, int randIndex = -1, bool animIdfromBtnId = true) const; - void addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, bool animIdfromBtnId = true) const; void deactivateButtonsFrom(CToggleGroup * group, int startId); void validatePlayersCnt(int playersCnt); void validateCompOnlyPlayersCnt(int compOnlyPlayersCnt); std::vector<int> getPossibleMapSizes(); - - std::shared_ptr<CPicture> background; - std::shared_ptr<CLabel> labelHeadlineBig; - std::shared_ptr<CLabel> labelHeadlineSmall; - - std::shared_ptr<CLabel> labelMapSize; - std::shared_ptr<CToggleGroup> groupMapSize; - std::shared_ptr<CToggleButton> buttonTwoLevels; - - std::shared_ptr<CLabelGroup> labelGroupForOptions; - std::shared_ptr<CToggleGroup> groupMaxPlayers; - std::shared_ptr<CToggleGroup> groupMaxTeams; - std::shared_ptr<CToggleGroup> groupCompOnlyPlayers; - std::shared_ptr<CToggleGroup> groupCompOnlyTeams; - std::shared_ptr<CToggleGroup> groupWaterContent; - std::shared_ptr<CToggleGroup> groupMonsterStrength; - std::shared_ptr<CButton> buttonShowRandomMaps; std::shared_ptr<CMapGenOptions> mapGenOptions; std::shared_ptr<CMapInfo> mapInfo; }; diff --git a/config/windows.json b/config/windows.json new file mode 100644 index 000000000..06de0c1a6 --- /dev/null +++ b/config/windows.json @@ -0,0 +1,513 @@ +{ + "randomMapTab": + { + "items": + [ + { + "name": "background", + "type": "picture", + "image": "RANMAPBK", + "position": {"x": 0, "y": 6} + }, + + { + "name": "labelHeadlineBig", + "type": "label", + "font": "big", + "alignment": "center", + "color": "yellow", + "text": 738, + "position": {"x": 222, "y": 36} + }, + + { + "name": "labelHeadlineSmall", + "type": "label", + "font": "small", + "alignment": "center", + "color": "white", + "text": 739, + "position": {"x": 222, "y": 56} + }, + + { + "name": "labelMapSize", + "type": "label", + "font": "small", + "alignment": "center", + "color": "white", + "text": 752, + "position": {"x": 104, "y": 97} + }, + + { + "name": "groupMapSize", + "type": "toggleGroup", + "position": {"x": 158, "y": 81}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANSIZS", + "zelp": 198, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0}, + }, + + { + "type": "toggleButton", + "image": "RANSIZM", + "zelp": 199, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 47, "y": 0}, + }, + + { + "type": "toggleButton", + "image": "RANSIZL", + "zelp": 200, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 94, "y": 0}, + }, + + { + "type": "toggleButton", + "image": "RANSIZX", + "zelp": 201, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 141, "y": 0} + } + ], + "selected": 1, + "callback": "toggleMapSize" + }, + + { + "name": "buttonTwoLevels", + "type": "toggleButton", + "image": "RANUNDR", + "position": {"x": 346, "y": 81}, + "selected": true, + "callback": "toggleTwoLevels" + }, + + { + "name": "groupMaxPlayers", + "type": "toggleGroup", + "position": {"x": 67, "y": 153}, + "items": + [ + { + "index": 1, + "type": "toggleButton", + "image": "RANNUM1", + "zelp": 204, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM2", + "zelp": 205, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 32, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM3", + "zelp": 206, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 64, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM4", + "zelp": 207, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 96, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM5", + "zelp": 208, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 128, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM6", + "zelp": 209, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 160, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM7", + "zelp": 210, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 192, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM8", + "zelp": 211, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 224, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 212, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 7, + "callback": "setPlayersCount" + }, + + { + "name": "groupMaxTeams", + "type": "toggleGroup", + "position": {"x": 67, "y": 219}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANNUM0", + "zelp": 214, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM1", + "zelp": 215, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 32, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM2", + "zelp": 216, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 64, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM3", + "zelp": 217, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 96, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM4", + "zelp": 218, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 128, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM5", + "zelp": 219, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 160, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM6", + "zelp": 220, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 192, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM7", + "zelp": 221, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 224, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 222, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 7, + "callback": "setTeamsCount" + }, + + { + "name": "groupCompOnlyPlayers", + "type": "toggleGroup", + "position": {"x": 67, "y": 285}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANNUM0", + "zelp": 224, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM1", + "zelp": 225, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 32, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM2", + "zelp": 226, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 64, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM3", + "zelp": 227, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 96, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM4", + "zelp": 228, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 128, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM5", + "zelp": 229, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 160, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM6", + "zelp": 230, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 192, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM7", + "zelp": 231, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 224, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 232, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 7, + "callback": "setCompOnlyPlayers" + }, + + { + "name": "groupCompOnlyTeams", + "type": "toggleGroup", + "position": {"x": 67, "y": 351}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANNUM0", + "zelp": 234, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM1", + "zelp": 235, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 32, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM2", + "zelp": 236, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 64, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM3", + "zelp": 237, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 96, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM4", + "zelp": 238, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 128, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM5", + "zelp": 239, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 160, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM6", + "zelp": 240, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 192, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 241, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 7, + "callback": "setCompOnlyTeams" + }, + + { + "name": "groupWaterContent", + "type": "toggleGroup", + "position": {"x": 67, "y": 419}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANNONE", + "zelp": 243, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNORM", + "zelp": 244, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 85, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANISLD", + "zelp": 245, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 170, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 246, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 3, + "callback": "setWaterContent" + }, + + { + "name": "groupMonsterStrength", + "type": "toggleGroup", + "position": {"x": 67, "y": 485}, + "items": + [ + { + "index": 2, + "type": "toggleButton", + "image": "RANWEAK", + "zelp": 248, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "index": 3, + "type": "toggleButton", + "image": "RANNORM", + "zelp": 249, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 85, "y": 0} + }, + { + "index": 4, + "type": "toggleButton", + "image": "RANSTRG", + "zelp": 250, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 170, "y": 0} + }, + { + "index": -2, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 251, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 3, + "callback": "setMonsterStrenght" + }, + + { + "name": "buttonShowRandomMaps", + "type": "button", + "position": {"x": 54, "y": 535}, + "image": "RANSHOW", + "zelp": 252 + }, + + { + "type": "labelGroup", + "font": "small", + "alignment": "left", + "color": "white", + "items": + [ + { + "position": {"x": 68, "y": 133}, + "text": 753 + }, + { + "position": {"x": 68, "y": 199}, + "text": 754 + }, + { + "position": {"x": 68, "y": 265}, + "text": 755 + }, + { + "position": {"x": 68, "y": 331}, + "text": 756 + }, + { + "position": {"x": 68, "y": 398}, + "text": 757 + }, + { + "position": {"x": 68, "y": 465}, + "text": 758 + } + ] + } + ] + } +} From 3be9969154a5b09c7a2e480dca27c25e3ecd03dc Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Mon, 12 Dec 2022 03:58:39 +0400 Subject: [PATCH 02/35] Prevent crashes for custom config --- client/gui/InterfaceBuilder.cpp | 5 +++- client/lobby/RandomMapTab.cpp | 45 ++++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/client/gui/InterfaceBuilder.cpp b/client/gui/InterfaceBuilder.cpp index db079f099..f8d383039 100644 --- a/client/gui/InterfaceBuilder.cpp +++ b/client/gui/InterfaceBuilder.cpp @@ -44,7 +44,10 @@ void InterfaceBuilder::addCallback(const std::string & callbackName, std::functi const std::shared_ptr<CIntObject> InterfaceBuilder::widget(const std::string & name) const { - return widgets.at(name); + auto iter = widgets.find(name); + if(iter == widgets.end()) + return nullptr; + return iter->second; } void InterfaceBuilder::init(const JsonNode &config) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 2d5848b99..cf02dee30 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -53,10 +53,12 @@ RandomMapTab::RandomMapTab(): { mapGenOptions->setPlayerCount(btnId); - deactivateButtonsFrom(dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams")).get(), btnId); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))) + deactivateButtonsFrom(w.get(), btnId); // deactive some CompOnlyPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I - deactivateButtonsFrom(dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers")).get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))) + deactivateButtonsFrom(w.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); validatePlayersCnt(btnId); updateMapInfoByHost(); @@ -73,9 +75,11 @@ RandomMapTab::RandomMapTab(): mapGenOptions->setCompOnlyPlayerCount(btnId); // deactive some MaxPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I - deactivateButtonsFrom(dynamic_pointer_cast<CToggleGroup>(widget("groupMaxPlayers")).get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxPlayers"))) + deactivateButtonsFrom(w.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); - deactivateButtonsFrom(dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams")).get(), (btnId == 0 ? 1 : btnId)); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))) + deactivateButtonsFrom(w.get(), (btnId == 0 ? 1 : btnId)); validateCompOnlyPlayersCnt(btnId); updateMapInfoByHost(); }); @@ -161,14 +165,22 @@ void RandomMapTab::updateMapInfoByHost() void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) { - dynamic_pointer_cast<CToggleGroup>(widget("groupMapSize"))->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth())); - dynamic_pointer_cast<CToggleButton>(widget("buttonTwoLevels"))->setSelected(opts->getHasTwoLevels()); - dynamic_pointer_cast<CToggleGroup>(widget("groupMaxPlayers"))->setSelected(opts->getPlayerCount()); - dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))->setSelected(opts->getTeamCount()); - dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))->setSelected(opts->getCompOnlyPlayerCount()); - dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))->setSelected(opts->getCompOnlyTeamCount()); - dynamic_pointer_cast<CToggleGroup>(widget("groupWaterContent"))->setSelected(opts->getWaterContent()); - dynamic_pointer_cast<CToggleGroup>(widget("groupMonsterStrength"))->setSelected(opts->getMonsterStrength()); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMapSize"))) + w->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth())); + if(auto w = dynamic_pointer_cast<CToggleButton>(widget("buttonTwoLevels"))) + w->setSelected(opts->getHasTwoLevels()); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxPlayers"))) + w->setSelected(opts->getPlayerCount()); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))) + w->setSelected(opts->getTeamCount()); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))) + w->setSelected(opts->getCompOnlyPlayerCount()); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))) + w->setSelected(opts->getCompOnlyTeamCount()); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupWaterContent"))) + w->setSelected(opts->getWaterContent()); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupgroupMonsterStrengthMaxTeams"))) + w->setSelected(opts->getMonsterStrength()); } void RandomMapTab::deactivateButtonsFrom(CToggleGroup * group, int startId) @@ -200,13 +212,15 @@ void RandomMapTab::validatePlayersCnt(int playersCnt) if(mapGenOptions->getTeamCount() >= playersCnt) { mapGenOptions->setTeamCount(playersCnt - 1); - dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))->setSelected(mapGenOptions->getTeamCount()); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))) + w->setSelected(mapGenOptions->getTeamCount()); } // total players should not exceed PlayerColor::PLAYER_LIMIT_I (8 in homm3) if(mapGenOptions->getCompOnlyPlayerCount() + playersCnt > PlayerColor::PLAYER_LIMIT_I) { mapGenOptions->setCompOnlyPlayerCount(PlayerColor::PLAYER_LIMIT_I - playersCnt); - dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))->setSelected(mapGenOptions->getCompOnlyPlayerCount()); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))) + w->setSelected(mapGenOptions->getCompOnlyPlayerCount()); } validateCompOnlyPlayersCnt(mapGenOptions->getCompOnlyPlayerCount()); @@ -224,7 +238,8 @@ void RandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt) int compOnlyTeamCount = compOnlyPlayersCnt == 0 ? 0 : compOnlyPlayersCnt - 1; mapGenOptions->setCompOnlyTeamCount(compOnlyTeamCount); updateMapInfoByHost(); - dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))->setSelected(compOnlyTeamCount); + if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))) + w->setSelected(compOnlyTeamCount); } } From b3d60ec418ece5d01428e5d3f47339d2e3db24a8 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Mon, 12 Dec 2022 04:46:42 +0400 Subject: [PATCH 03/35] Add new map sizes --- client/lobby/RandomMapTab.cpp | 2 +- lib/mapping/CMap.h | 3 +++ lib/mapping/CMapInfo.cpp | 12 ++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index cf02dee30..ec8a3fcc2 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -245,5 +245,5 @@ void RandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt) std::vector<int> RandomMapTab::getPossibleMapSizes() { - return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE}; + return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE, CMapHeader::MAP_SIZE_HUGE, CMapHeader::MAP_SIZE_XHUGE, CMapHeader::MAP_SIZE_GIANT}; } diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index b8047624b..25c1a464b 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -284,6 +284,9 @@ public: static const int MAP_SIZE_MIDDLE = 72; static const int MAP_SIZE_LARGE = 108; static const int MAP_SIZE_XLARGE = 144; + static const int MAP_SIZE_HUGE = 180; + static const int MAP_SIZE_XHUGE = 216; + static const int MAP_SIZE_GIANT = 252; CMapHeader(); virtual ~CMapHeader(); diff --git a/lib/mapping/CMapInfo.cpp b/lib/mapping/CMapInfo.cpp index 3756ae580..f33f6a02e 100644 --- a/lib/mapping/CMapInfo.cpp +++ b/lib/mapping/CMapInfo.cpp @@ -134,6 +134,12 @@ int CMapInfo::getMapSizeIconId() const return 2; case CMapHeader::MAP_SIZE_XLARGE: return 3; + case CMapHeader::MAP_SIZE_HUGE: + return 4; + case CMapHeader::MAP_SIZE_XHUGE: + return 5; + case CMapHeader::MAP_SIZE_GIANT: + return 6; default: return 4; } @@ -180,6 +186,12 @@ std::string CMapInfo::getMapSizeName() const return "L"; case CMapHeader::MAP_SIZE_XLARGE: return "XL"; + case CMapHeader::MAP_SIZE_HUGE: + return "H"; + case CMapHeader::MAP_SIZE_XHUGE: + return "XH"; + case CMapHeader::MAP_SIZE_GIANT: + return "G"; default: return "C"; } From 8dbc5c1c1f38ed4ab90ab4c6c6b6a142b05ea154 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Mon, 12 Dec 2022 04:52:44 +0400 Subject: [PATCH 04/35] Make each windows coniguration in separate file --- client/lobby/RandomMapTab.cpp | 4 +- config/windows.json | 513 ------------------------------- config/windows/randomMapTab.json | 510 ++++++++++++++++++++++++++++++ 3 files changed, 512 insertions(+), 515 deletions(-) delete mode 100644 config/windows.json create mode 100644 config/windows/randomMapTab.json diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index ec8a3fcc2..76543c3e4 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -35,7 +35,7 @@ RandomMapTab::RandomMapTab(): recActions = 0; mapGenOptions = std::make_shared<CMapGenOptions>(); - const JsonNode config(ResourceID("config/windows.json")); + const JsonNode config(ResourceID("config/windows/randomMapTab.json")); addCallback("toggleMapSize", [&](int btnId) { auto mapSizeVal = getPossibleMapSizes(); @@ -105,7 +105,7 @@ RandomMapTab::RandomMapTab(): updateMapInfoByHost(); }); - init(config["randomMapTab"]); + init(config); updateMapInfoByHost(); } diff --git a/config/windows.json b/config/windows.json deleted file mode 100644 index 06de0c1a6..000000000 --- a/config/windows.json +++ /dev/null @@ -1,513 +0,0 @@ -{ - "randomMapTab": - { - "items": - [ - { - "name": "background", - "type": "picture", - "image": "RANMAPBK", - "position": {"x": 0, "y": 6} - }, - - { - "name": "labelHeadlineBig", - "type": "label", - "font": "big", - "alignment": "center", - "color": "yellow", - "text": 738, - "position": {"x": 222, "y": 36} - }, - - { - "name": "labelHeadlineSmall", - "type": "label", - "font": "small", - "alignment": "center", - "color": "white", - "text": 739, - "position": {"x": 222, "y": 56} - }, - - { - "name": "labelMapSize", - "type": "label", - "font": "small", - "alignment": "center", - "color": "white", - "text": 752, - "position": {"x": 104, "y": 97} - }, - - { - "name": "groupMapSize", - "type": "toggleGroup", - "position": {"x": 158, "y": 81}, - "items": - [ - { - "index": 0, - "type": "toggleButton", - "image": "RANSIZS", - "zelp": 198, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 0, "y": 0}, - }, - - { - "type": "toggleButton", - "image": "RANSIZM", - "zelp": 199, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 47, "y": 0}, - }, - - { - "type": "toggleButton", - "image": "RANSIZL", - "zelp": 200, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 94, "y": 0}, - }, - - { - "type": "toggleButton", - "image": "RANSIZX", - "zelp": 201, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 141, "y": 0} - } - ], - "selected": 1, - "callback": "toggleMapSize" - }, - - { - "name": "buttonTwoLevels", - "type": "toggleButton", - "image": "RANUNDR", - "position": {"x": 346, "y": 81}, - "selected": true, - "callback": "toggleTwoLevels" - }, - - { - "name": "groupMaxPlayers", - "type": "toggleGroup", - "position": {"x": 67, "y": 153}, - "items": - [ - { - "index": 1, - "type": "toggleButton", - "image": "RANNUM1", - "zelp": 204, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 0, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM2", - "zelp": 205, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 32, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM3", - "zelp": 206, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 64, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM4", - "zelp": 207, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 96, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM5", - "zelp": 208, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 128, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM6", - "zelp": 209, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 160, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM7", - "zelp": 210, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 192, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM8", - "zelp": 211, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 224, "y": 0} - }, - { - "index": -1, - "type": "toggleButton", - "image": "RANRAND", - "zelp": 212, - "position": {"x": 256, "y": 0}, - } - ], - "selected": 7, - "callback": "setPlayersCount" - }, - - { - "name": "groupMaxTeams", - "type": "toggleGroup", - "position": {"x": 67, "y": 219}, - "items": - [ - { - "index": 0, - "type": "toggleButton", - "image": "RANNUM0", - "zelp": 214, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 0, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM1", - "zelp": 215, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 32, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM2", - "zelp": 216, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 64, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM3", - "zelp": 217, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 96, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM4", - "zelp": 218, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 128, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM5", - "zelp": 219, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 160, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM6", - "zelp": 220, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 192, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM7", - "zelp": 221, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 224, "y": 0} - }, - { - "index": -1, - "type": "toggleButton", - "image": "RANRAND", - "zelp": 222, - "position": {"x": 256, "y": 0}, - } - ], - "selected": 7, - "callback": "setTeamsCount" - }, - - { - "name": "groupCompOnlyPlayers", - "type": "toggleGroup", - "position": {"x": 67, "y": 285}, - "items": - [ - { - "index": 0, - "type": "toggleButton", - "image": "RANNUM0", - "zelp": 224, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 0, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM1", - "zelp": 225, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 32, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM2", - "zelp": 226, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 64, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM3", - "zelp": 227, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 96, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM4", - "zelp": 228, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 128, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM5", - "zelp": 229, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 160, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM6", - "zelp": 230, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 192, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM7", - "zelp": 231, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 224, "y": 0} - }, - { - "index": -1, - "type": "toggleButton", - "image": "RANRAND", - "zelp": 232, - "position": {"x": 256, "y": 0}, - } - ], - "selected": 7, - "callback": "setCompOnlyPlayers" - }, - - { - "name": "groupCompOnlyTeams", - "type": "toggleGroup", - "position": {"x": 67, "y": 351}, - "items": - [ - { - "index": 0, - "type": "toggleButton", - "image": "RANNUM0", - "zelp": 234, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 0, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM1", - "zelp": 235, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 32, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM2", - "zelp": 236, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 64, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM3", - "zelp": 237, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 96, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM4", - "zelp": 238, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 128, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM5", - "zelp": 239, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 160, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNUM6", - "zelp": 240, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 192, "y": 0} - }, - { - "index": -1, - "type": "toggleButton", - "image": "RANRAND", - "zelp": 241, - "position": {"x": 256, "y": 0}, - } - ], - "selected": 7, - "callback": "setCompOnlyTeams" - }, - - { - "name": "groupWaterContent", - "type": "toggleGroup", - "position": {"x": 67, "y": 419}, - "items": - [ - { - "index": 0, - "type": "toggleButton", - "image": "RANNONE", - "zelp": 243, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 0, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANNORM", - "zelp": 244, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 85, "y": 0} - }, - { - "type": "toggleButton", - "image": "RANISLD", - "zelp": 245, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 170, "y": 0} - }, - { - "index": -1, - "type": "toggleButton", - "image": "RANRAND", - "zelp": 246, - "position": {"x": 256, "y": 0}, - } - ], - "selected": 3, - "callback": "setWaterContent" - }, - - { - "name": "groupMonsterStrength", - "type": "toggleGroup", - "position": {"x": 67, "y": 485}, - "items": - [ - { - "index": 2, - "type": "toggleButton", - "image": "RANWEAK", - "zelp": 248, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 0, "y": 0} - }, - { - "index": 3, - "type": "toggleButton", - "image": "RANNORM", - "zelp": 249, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 85, "y": 0} - }, - { - "index": 4, - "type": "toggleButton", - "image": "RANSTRG", - "zelp": 250, - "imageOrder": [0, 1, 1, 3], - "position": {"x": 170, "y": 0} - }, - { - "index": -2, - "type": "toggleButton", - "image": "RANRAND", - "zelp": 251, - "position": {"x": 256, "y": 0}, - } - ], - "selected": 3, - "callback": "setMonsterStrenght" - }, - - { - "name": "buttonShowRandomMaps", - "type": "button", - "position": {"x": 54, "y": 535}, - "image": "RANSHOW", - "zelp": 252 - }, - - { - "type": "labelGroup", - "font": "small", - "alignment": "left", - "color": "white", - "items": - [ - { - "position": {"x": 68, "y": 133}, - "text": 753 - }, - { - "position": {"x": 68, "y": 199}, - "text": 754 - }, - { - "position": {"x": 68, "y": 265}, - "text": 755 - }, - { - "position": {"x": 68, "y": 331}, - "text": 756 - }, - { - "position": {"x": 68, "y": 398}, - "text": 757 - }, - { - "position": {"x": 68, "y": 465}, - "text": 758 - } - ] - } - ] - } -} diff --git a/config/windows/randomMapTab.json b/config/windows/randomMapTab.json new file mode 100644 index 000000000..189537ac9 --- /dev/null +++ b/config/windows/randomMapTab.json @@ -0,0 +1,510 @@ +{ + "items": + [ + { + "name": "background", + "type": "picture", + "image": "RANMAPBK", + "position": {"x": 0, "y": 6} + }, + + { + "name": "labelHeadlineBig", + "type": "label", + "font": "big", + "alignment": "center", + "color": "yellow", + "text": 738, + "position": {"x": 222, "y": 36} + }, + + { + "name": "labelHeadlineSmall", + "type": "label", + "font": "small", + "alignment": "center", + "color": "white", + "text": 739, + "position": {"x": 222, "y": 56} + }, + + { + "name": "labelMapSize", + "type": "label", + "font": "small", + "alignment": "center", + "color": "white", + "text": 752, + "position": {"x": 104, "y": 97} + }, + + { + "name": "groupMapSize", + "type": "toggleGroup", + "position": {"x": 158, "y": 81}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANSIZS", + "zelp": 198, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0}, + }, + + { + "type": "toggleButton", + "image": "RANSIZM", + "zelp": 199, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 47, "y": 0}, + }, + + { + "type": "toggleButton", + "image": "RANSIZL", + "zelp": 200, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 94, "y": 0}, + }, + + { + "type": "toggleButton", + "image": "RANSIZX", + "zelp": 201, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 141, "y": 0} + } + ], + "selected": 1, + "callback": "toggleMapSize" + }, + + { + "name": "buttonTwoLevels", + "type": "toggleButton", + "image": "RANUNDR", + "position": {"x": 346, "y": 81}, + "selected": true, + "callback": "toggleTwoLevels" + }, + + { + "name": "groupMaxPlayers", + "type": "toggleGroup", + "position": {"x": 67, "y": 153}, + "items": + [ + { + "index": 1, + "type": "toggleButton", + "image": "RANNUM1", + "zelp": 204, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM2", + "zelp": 205, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 32, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM3", + "zelp": 206, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 64, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM4", + "zelp": 207, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 96, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM5", + "zelp": 208, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 128, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM6", + "zelp": 209, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 160, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM7", + "zelp": 210, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 192, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM8", + "zelp": 211, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 224, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 212, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 7, + "callback": "setPlayersCount" + }, + + { + "name": "groupMaxTeams", + "type": "toggleGroup", + "position": {"x": 67, "y": 219}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANNUM0", + "zelp": 214, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM1", + "zelp": 215, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 32, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM2", + "zelp": 216, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 64, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM3", + "zelp": 217, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 96, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM4", + "zelp": 218, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 128, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM5", + "zelp": 219, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 160, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM6", + "zelp": 220, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 192, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM7", + "zelp": 221, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 224, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 222, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 7, + "callback": "setTeamsCount" + }, + + { + "name": "groupCompOnlyPlayers", + "type": "toggleGroup", + "position": {"x": 67, "y": 285}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANNUM0", + "zelp": 224, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM1", + "zelp": 225, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 32, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM2", + "zelp": 226, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 64, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM3", + "zelp": 227, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 96, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM4", + "zelp": 228, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 128, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM5", + "zelp": 229, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 160, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM6", + "zelp": 230, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 192, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM7", + "zelp": 231, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 224, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 232, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 7, + "callback": "setCompOnlyPlayers" + }, + + { + "name": "groupCompOnlyTeams", + "type": "toggleGroup", + "position": {"x": 67, "y": 351}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANNUM0", + "zelp": 234, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM1", + "zelp": 235, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 32, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM2", + "zelp": 236, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 64, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM3", + "zelp": 237, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 96, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM4", + "zelp": 238, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 128, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM5", + "zelp": 239, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 160, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNUM6", + "zelp": 240, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 192, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 241, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 7, + "callback": "setCompOnlyTeams" + }, + + { + "name": "groupWaterContent", + "type": "toggleGroup", + "position": {"x": 67, "y": 419}, + "items": + [ + { + "index": 0, + "type": "toggleButton", + "image": "RANNONE", + "zelp": 243, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANNORM", + "zelp": 244, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 85, "y": 0} + }, + { + "type": "toggleButton", + "image": "RANISLD", + "zelp": 245, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 170, "y": 0} + }, + { + "index": -1, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 246, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 3, + "callback": "setWaterContent" + }, + + { + "name": "groupMonsterStrength", + "type": "toggleGroup", + "position": {"x": 67, "y": 485}, + "items": + [ + { + "index": 2, + "type": "toggleButton", + "image": "RANWEAK", + "zelp": 248, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 0, "y": 0} + }, + { + "index": 3, + "type": "toggleButton", + "image": "RANNORM", + "zelp": 249, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 85, "y": 0} + }, + { + "index": 4, + "type": "toggleButton", + "image": "RANSTRG", + "zelp": 250, + "imageOrder": [0, 1, 1, 3], + "position": {"x": 170, "y": 0} + }, + { + "index": -2, + "type": "toggleButton", + "image": "RANRAND", + "zelp": 251, + "position": {"x": 256, "y": 0}, + } + ], + "selected": 3, + "callback": "setMonsterStrenght" + }, + + { + "name": "buttonShowRandomMaps", + "type": "button", + "position": {"x": 54, "y": 535}, + "image": "RANSHOW", + "zelp": 252 + }, + + { + "type": "labelGroup", + "font": "small", + "alignment": "left", + "color": "white", + "items": + [ + { + "position": {"x": 68, "y": 133}, + "text": 753 + }, + { + "position": {"x": 68, "y": 199}, + "text": 754 + }, + { + "position": {"x": 68, "y": 265}, + "text": 755 + }, + { + "position": {"x": 68, "y": 331}, + "text": 756 + }, + { + "position": {"x": 68, "y": 398}, + "text": 757 + }, + { + "position": {"x": 68, "y": 465}, + "text": 758 + } + ] + } + ] +} From 5e3504f57847be30dbf985250074b85699fd6326 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Mon, 12 Dec 2022 11:38:27 +0400 Subject: [PATCH 05/35] Add improvements --- client/gui/InterfaceBuilder.cpp | 8 -------- client/gui/InterfaceBuilder.h | 9 ++++++++- client/lobby/RandomMapTab.cpp | 32 ++++++++++++++++---------------- config/windows/randomMapTab.json | 2 +- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/client/gui/InterfaceBuilder.cpp b/client/gui/InterfaceBuilder.cpp index f8d383039..4beddbc04 100644 --- a/client/gui/InterfaceBuilder.cpp +++ b/client/gui/InterfaceBuilder.cpp @@ -42,14 +42,6 @@ void InterfaceBuilder::addCallback(const std::string & callbackName, std::functi callbacks[callbackName] = callback; } -const std::shared_ptr<CIntObject> InterfaceBuilder::widget(const std::string & name) const -{ - auto iter = widgets.find(name); - if(iter == widgets.end()) - return nullptr; - return iter->second; -} - void InterfaceBuilder::init(const JsonNode &config) { OBJ_CONSTRUCTION; diff --git a/client/gui/InterfaceBuilder.h b/client/gui/InterfaceBuilder.h index 00740d7ef..135896ec0 100644 --- a/client/gui/InterfaceBuilder.h +++ b/client/gui/InterfaceBuilder.h @@ -26,7 +26,14 @@ protected: void addCallback(const std::string & callbackName, std::function<void(int)> callback); - const std::shared_ptr<CIntObject> widget(const std::string &) const; + template<class T> + const std::shared_ptr<T> widget(const std::string & name) const + { + auto iter = widgets.find(name); + if(iter == widgets.end()) + return nullptr; + return dynamic_pointer_cast<T>(iter->second); + } private: diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 76543c3e4..e6b522366 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -53,11 +53,11 @@ RandomMapTab::RandomMapTab(): { mapGenOptions->setPlayerCount(btnId); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))) + if(auto w = widget<CToggleGroup>("groupMaxTeams")) deactivateButtonsFrom(w.get(), btnId); // deactive some CompOnlyPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))) + if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers")) deactivateButtonsFrom(w.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); validatePlayersCnt(btnId); @@ -75,10 +75,10 @@ RandomMapTab::RandomMapTab(): mapGenOptions->setCompOnlyPlayerCount(btnId); // deactive some MaxPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxPlayers"))) + if(auto w = widget<CToggleGroup>("groupMaxPlayers")) deactivateButtonsFrom(w.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))) + if(auto w = widget<CToggleGroup>("groupCompOnlyTeams")) deactivateButtonsFrom(w.get(), (btnId == 0 ? 1 : btnId)); validateCompOnlyPlayersCnt(btnId); updateMapInfoByHost(); @@ -96,7 +96,7 @@ RandomMapTab::RandomMapTab(): updateMapInfoByHost(); }); - addCallback("setMonsterStrenght", [&](int btnId) + addCallback("setMonsterStrength", [&](int btnId) { if(btnId < 0) mapGenOptions->setMonsterStrength(EMonsterStrength::RANDOM); @@ -165,21 +165,21 @@ void RandomMapTab::updateMapInfoByHost() void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) { - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMapSize"))) + if(auto w = widget<CToggleGroup>("groupMapSize")) w->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth())); - if(auto w = dynamic_pointer_cast<CToggleButton>(widget("buttonTwoLevels"))) + if(auto w = widget<CToggleButton>("buttonTwoLevels")) w->setSelected(opts->getHasTwoLevels()); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxPlayers"))) + if(auto w = widget<CToggleGroup>("groupMaxPlayers")) w->setSelected(opts->getPlayerCount()); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))) + if(auto w = widget<CToggleGroup>("groupMaxTeams")) w->setSelected(opts->getTeamCount()); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))) + if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers")) w->setSelected(opts->getCompOnlyPlayerCount()); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))) + if(auto w = widget<CToggleGroup>("groupCompOnlyTeams")) w->setSelected(opts->getCompOnlyTeamCount()); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupWaterContent"))) + if(auto w = widget<CToggleGroup>("groupWaterContent")) w->setSelected(opts->getWaterContent()); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupgroupMonsterStrengthMaxTeams"))) + if(auto w = widget<CToggleGroup>("groupMonsterStrength")) w->setSelected(opts->getMonsterStrength()); } @@ -212,14 +212,14 @@ void RandomMapTab::validatePlayersCnt(int playersCnt) if(mapGenOptions->getTeamCount() >= playersCnt) { mapGenOptions->setTeamCount(playersCnt - 1); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupMaxTeams"))) + if(auto w = widget<CToggleGroup>("groupMaxTeams")) w->setSelected(mapGenOptions->getTeamCount()); } // total players should not exceed PlayerColor::PLAYER_LIMIT_I (8 in homm3) if(mapGenOptions->getCompOnlyPlayerCount() + playersCnt > PlayerColor::PLAYER_LIMIT_I) { mapGenOptions->setCompOnlyPlayerCount(PlayerColor::PLAYER_LIMIT_I - playersCnt); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyPlayers"))) + if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers")) w->setSelected(mapGenOptions->getCompOnlyPlayerCount()); } @@ -238,7 +238,7 @@ void RandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt) int compOnlyTeamCount = compOnlyPlayersCnt == 0 ? 0 : compOnlyPlayersCnt - 1; mapGenOptions->setCompOnlyTeamCount(compOnlyTeamCount); updateMapInfoByHost(); - if(auto w = dynamic_pointer_cast<CToggleGroup>(widget("groupCompOnlyTeams"))) + if(auto w = widget<CToggleGroup>("groupCompOnlyTeams")) w->setSelected(compOnlyTeamCount); } } diff --git a/config/windows/randomMapTab.json b/config/windows/randomMapTab.json index 189537ac9..a1741c121 100644 --- a/config/windows/randomMapTab.json +++ b/config/windows/randomMapTab.json @@ -462,7 +462,7 @@ } ], "selected": 3, - "callback": "setMonsterStrenght" + "callback": "setMonsterStrength" }, { From 34bf7419364d73a73deb240fe627d7875c79cf14 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Mon, 12 Dec 2022 11:43:54 +0400 Subject: [PATCH 06/35] Fix compilation error --- client/gui/InterfaceBuilder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/gui/InterfaceBuilder.h b/client/gui/InterfaceBuilder.h index 135896ec0..61a27e547 100644 --- a/client/gui/InterfaceBuilder.h +++ b/client/gui/InterfaceBuilder.h @@ -32,7 +32,7 @@ protected: auto iter = widgets.find(name); if(iter == widgets.end()) return nullptr; - return dynamic_pointer_cast<T>(iter->second); + return std::dynamic_pointer_cast<T>(iter->second); } private: From cd58e8a860c202308fd35babcf6d386cd31cbafb Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Mon, 12 Dec 2022 11:48:39 +0400 Subject: [PATCH 07/35] Rename InterfaceBuilder --- client/CMakeLists.txt | 4 +- client/gui/InterfaceBuilder.cpp | 14 +- client/gui/InterfaceBuilder.h | 6 +- client/gui/InterfaceObjectConfigurable.cpp | 204 +++++++++++++++++++++ client/gui/InterfaceObjectConfigurable.h | 46 +++++ client/lobby/RandomMapTab.cpp | 2 +- client/lobby/RandomMapTab.h | 4 +- 7 files changed, 265 insertions(+), 15 deletions(-) create mode 100644 client/gui/InterfaceObjectConfigurable.cpp create mode 100644 client/gui/InterfaceObjectConfigurable.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 18886669b..d78ea8006 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -15,7 +15,7 @@ set(client_SRCS gui/Geometries.cpp gui/SDL_Extensions.cpp gui/NotificationHandler.cpp - gui/InterfaceBuilder.cpp + gui/InterfaceObjectConfigurable.cpp widgets/AdventureMapClasses.cpp widgets/Buttons.cpp @@ -91,7 +91,7 @@ set(client_HEADERS gui/SDL_Extensions.h gui/SDL_Pixels.h gui/NotificationHandler.h - gui/InterfaceBuilder.h + gui/InterfaceObjectConfigurable.h widgets/AdventureMapClasses.h widgets/Buttons.h diff --git a/client/gui/InterfaceBuilder.cpp b/client/gui/InterfaceBuilder.cpp index 4beddbc04..40be3acc3 100644 --- a/client/gui/InterfaceBuilder.cpp +++ b/client/gui/InterfaceBuilder.cpp @@ -10,7 +10,7 @@ #include "StdInc.h" -#include "InterfaceBuilder.h" +#include "InterfaceObjectConfigurable.h" #include "../CGameInfo.h" #include "../gui/CAnimation.h" @@ -26,23 +26,23 @@ #include "../../lib/CGeneralTextHandler.h" -InterfaceBuilder::InterfaceBuilder(const JsonNode & config): +InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config): CIntObject() { init(config); } -InterfaceBuilder::InterfaceBuilder(): +InterfaceObjectConfigurable::InterfaceObjectConfigurable(): CIntObject() { } -void InterfaceBuilder::addCallback(const std::string & callbackName, std::function<void(int)> callback) +void InterfaceObjectConfigurable::addCallback(const std::string & callbackName, std::function<void(int)> callback) { callbacks[callbackName] = callback; } -void InterfaceBuilder::init(const JsonNode &config) +void InterfaceObjectConfigurable::init(const JsonNode &config) { OBJ_CONSTRUCTION; int unnamedObjectId = 0; @@ -57,7 +57,7 @@ void InterfaceBuilder::init(const JsonNode &config) } } -std::string InterfaceBuilder::buildText(const JsonNode & config) const +std::string InterfaceObjectConfigurable::buildText(const JsonNode & config) const { if(config.isNull()) return ""; @@ -69,7 +69,7 @@ std::string InterfaceBuilder::buildText(const JsonNode & config) const return config.String(); } -std::shared_ptr<CIntObject> InterfaceBuilder::buildWidget(const JsonNode & config) +std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonNode & config) { assert(!config.isNull()); auto type = config["type"].String(); diff --git a/client/gui/InterfaceBuilder.h b/client/gui/InterfaceBuilder.h index 61a27e547..48501d03b 100644 --- a/client/gui/InterfaceBuilder.h +++ b/client/gui/InterfaceBuilder.h @@ -14,11 +14,11 @@ #include "../../lib/JsonNode.h" -class InterfaceBuilder: public CIntObject +class InterfaceObjectConfigurable: public CIntObject { public: - InterfaceBuilder(); - InterfaceBuilder(const JsonNode & config); + InterfaceObjectConfigurable(); + InterfaceObjectConfigurable(const JsonNode & config); protected: //must be called after adding callbacks diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp new file mode 100644 index 000000000..3ec429257 --- /dev/null +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -0,0 +1,204 @@ +/* +* InterfaceBuilder.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#include "StdInc.h" + +#include "InterfaceObjectConfigurable.h" + +#include "../CGameInfo.h" +#include "../gui/CAnimation.h" +#include "../gui/CGuiHandler.h" +#include "../widgets/CComponent.h" +#include "../widgets/Buttons.h" +#include "../widgets/MiscWidgets.h" +#include "../widgets/ObjectLists.h" +#include "../widgets/TextControls.h" +#include "../windows/GUIClasses.h" +#include "../windows/InfoWindows.h" + +#include "../../lib/CGeneralTextHandler.h" + + +InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config): + CIntObject() +{ + init(config); +} + +InterfaceObjectConfigurable::InterfaceObjectConfigurable(): + CIntObject() +{ +} + +void InterfaceObjectConfigurable::addCallback(const std::string & callbackName, std::function<void(int)> callback) +{ + callbacks[callbackName] = callback; +} + +void InterfaceObjectConfigurable::init(const JsonNode &config) +{ + OBJ_CONSTRUCTION; + int unnamedObjectId = 0; + const std::string unnamedObjectPrefix = "__widget_"; + + for(const auto & item : config["items"].Vector()) + { + std::string name = item["name"].isNull() + ? unnamedObjectPrefix + std::to_string(unnamedObjectId++) + : item["name"].String(); + widgets[name] = buildWidget(item); + } +} + +std::string InterfaceObjectConfigurable::buildText(const JsonNode & config) const +{ + if(config.isNull()) + return ""; + + if(config.isNumber()) + { + return CGI->generaltexth->allTexts[config.Integer()]; + } + return config.String(); +} + +std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonNode & config) +{ + assert(!config.isNull()); + auto type = config["type"].String(); + + int x = 0, y = 0; + if(!config["position"].isNull()) + { + x = config["position"]["x"].Integer(); + y = config["position"]["y"].Integer(); + } + + std::string image; + std::string text = buildText(config["text"]); + auto alignment = EAlignment::CENTER; + auto color = Colors::DEFAULT_KEY_COLOR; + auto font = EFonts::FONT_TIMES; + + if(!config["image"].isNull()) + image = config["image"].String(); + if(!config["alignment"].isNull()) + { + if(config["alignment"].String() == "left") + alignment = EAlignment::TOPLEFT; + if(config["alignment"].String() == "center") + alignment = EAlignment::CENTER; + if(config["alignment"].String() == "right") + alignment = EAlignment::BOTTOMRIGHT; + } + if(!config["color"].isNull()) + { + if(config["color"].String() == "yellow") + color = Colors::YELLOW; + if(config["color"].String() == "white") + color = Colors::WHITE; + if(config["color"].String() == "gold") + color = Colors::METALLIC_GOLD; + if(config["color"].String() == "green") + color = Colors::GREEN; + if(config["color"].String() == "orange") + color = Colors::ORANGE; + if(config["color"].String() == "bright-yellow") + color = Colors::BRIGHT_YELLOW; + } + if(!config["font"].isNull()) + { + if(config["font"].String() == "big") + font = EFonts::FONT_BIG; + if(config["font"].String() == "medium") + font = EFonts::FONT_MEDIUM; + if(config["font"].String() == "small") + font = EFonts::FONT_SMALL; + if(config["font"].String() == "tiny") + font = EFonts::FONT_TINY; + } + + + if(type == "picture") + { + return std::make_shared<CPicture>(image, x, y); + } + if(type == "label") + { + return std::make_shared<CLabel>(x, y, font, alignment, color, text); + } + if(type == "toggleGroup") + { + auto group = std::make_shared<CToggleGroup>(0); + group->pos.x += x; + group->pos.y += y; + if(!config["items"].isNull()) + { + SObjectConstruction obj__i(group.get()); + int itemIdx = -1; + for(const auto & item : config["items"].Vector()) + { + itemIdx = item["index"].isNull() ? itemIdx + 1 : item["index"].Integer(); + group->addToggle(itemIdx, std::dynamic_pointer_cast<CToggleBase>(buildWidget(item))); + } + } + if(!config["selected"].isNull()) + group->setSelected(config["selected"].Integer()); + if(!config["callback"].isNull()) + group->addCallback(callbacks[config["callback"].String()]); + return group; + } + if(type == "toggleButton") + { + std::pair<std::string, std::string> zelp; + if(!config["zelp"].isNull()) + zelp = CGI->generaltexth->zelp[config["zelp"].Integer()]; + auto button = std::make_shared<CToggleButton>(Point(x, y), image, zelp); + if(!config["selected"].isNull()) + button->setSelected(config["selected"].Bool()); + if(!config["imageOrder"].isNull()) + { + auto imgOrder = config["imageOrder"].Vector(); + assert(imgOrder.size() >= 4); + button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer()); + } + if(!config["callback"].isNull()) + button->addCallback(callbacks[config["callback"].String()]); + return button; + } + if(type == "button") + { + std::pair<std::string, std::string> zelp; + if(!config["zelp"].isNull()) + zelp = CGI->generaltexth->zelp[config["zelp"].Integer()]; + auto button = std::make_shared<CButton>(Point(x, y), image, zelp); + return button; + } + if(type == "labelGroup") + { + auto group = std::make_shared<CLabelGroup>(font, alignment, color); + if(!config["items"].isNull()) + { + for(const auto & item : config["items"].Vector()) + { + if(!item["position"].isNull()) + { + x = item["position"]["x"].Integer(); + y = item["position"]["y"].Integer(); + } + if(!item["text"].isNull()) + text = buildText(item["text"]); + group->add(x, y, text); + } + } + return group; + } + return std::shared_ptr<CIntObject>(nullptr); +} diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h new file mode 100644 index 000000000..48501d03b --- /dev/null +++ b/client/gui/InterfaceObjectConfigurable.h @@ -0,0 +1,46 @@ +/* +* InterfaceBuilder.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#pragma once + +#include "CIntObject.h" + +#include "../../lib/JsonNode.h" + +class InterfaceObjectConfigurable: public CIntObject +{ +public: + InterfaceObjectConfigurable(); + InterfaceObjectConfigurable(const JsonNode & config); + +protected: + //must be called after adding callbacks + void init(const JsonNode & config); + + void addCallback(const std::string & callbackName, std::function<void(int)> callback); + + template<class T> + const std::shared_ptr<T> widget(const std::string & name) const + { + auto iter = widgets.find(name); + if(iter == widgets.end()) + return nullptr; + return std::dynamic_pointer_cast<T>(iter->second); + } + +private: + + std::map<std::string, std::shared_ptr<CIntObject>> widgets; + std::map<std::string, std::function<void(int)>> callbacks; + + std::shared_ptr<CIntObject> buildWidget(const JsonNode & config); + + std::string buildText(const JsonNode & param) const; +}; diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index e6b522366..eaf8dd264 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -30,7 +30,7 @@ #include "../../lib/CModHandler.h" RandomMapTab::RandomMapTab(): - InterfaceBuilder() + InterfaceObjectConfigurable() { recActions = 0; mapGenOptions = std::make_shared<CMapGenOptions>(); diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index ac78247f9..6b82ae3e2 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -13,7 +13,7 @@ #include "../../lib/FunctionList.h" #include "../../lib/GameConstants.h" -#include "../gui/InterfaceBuilder.h" +#include "../gui/InterfaceObjectConfigurable.h" VCMI_LIB_NAMESPACE_BEGIN @@ -25,7 +25,7 @@ class CToggleButton; class CLabel; class CLabelGroup; -class RandomMapTab : public InterfaceBuilder +class RandomMapTab : public InterfaceObjectConfigurable { public: RandomMapTab(); From 690ff773f4d3ba30c69f6b65100373c4b508ecc4 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Mon, 12 Dec 2022 11:53:23 +0400 Subject: [PATCH 08/35] Remove files --- client/gui/InterfaceBuilder.cpp | 203 -------------------------------- client/gui/InterfaceBuilder.h | 46 -------- 2 files changed, 249 deletions(-) delete mode 100644 client/gui/InterfaceBuilder.cpp delete mode 100644 client/gui/InterfaceBuilder.h diff --git a/client/gui/InterfaceBuilder.cpp b/client/gui/InterfaceBuilder.cpp deleted file mode 100644 index 40be3acc3..000000000 --- a/client/gui/InterfaceBuilder.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* -* InterfaceBuilder.cpp, part of VCMI engine -* -* Authors: listed in file AUTHORS in main folder -* -* License: GNU General Public License v2.0 or later -* Full text of license available in license.txt file, in main folder -* -*/ - -#include "StdInc.h" - -#include "InterfaceObjectConfigurable.h" - -#include "../CGameInfo.h" -#include "../gui/CAnimation.h" -#include "../gui/CGuiHandler.h" -#include "../widgets/CComponent.h" -#include "../widgets/Buttons.h" -#include "../widgets/MiscWidgets.h" -#include "../widgets/ObjectLists.h" -#include "../widgets/TextControls.h" -#include "../windows/GUIClasses.h" -#include "../windows/InfoWindows.h" - -#include "../../lib/CGeneralTextHandler.h" - - -InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config): - CIntObject() -{ - init(config); -} - -InterfaceObjectConfigurable::InterfaceObjectConfigurable(): - CIntObject() -{ -} - -void InterfaceObjectConfigurable::addCallback(const std::string & callbackName, std::function<void(int)> callback) -{ - callbacks[callbackName] = callback; -} - -void InterfaceObjectConfigurable::init(const JsonNode &config) -{ - OBJ_CONSTRUCTION; - int unnamedObjectId = 0; - const std::string unnamedObjectPrefix = "__widget_"; - - for(const auto & item : config["items"].Vector()) - { - std::string name = item["name"].isNull() - ? unnamedObjectPrefix + std::to_string(unnamedObjectId++) - : item["name"].String(); - widgets[name] = buildWidget(item); - } -} - -std::string InterfaceObjectConfigurable::buildText(const JsonNode & config) const -{ - if(config.isNull()) - return ""; - - if(config.isNumber()) - { - return CGI->generaltexth->allTexts[config.Integer()]; - } - return config.String(); -} - -std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonNode & config) -{ - assert(!config.isNull()); - auto type = config["type"].String(); - - int x = 0, y = 0; - if(!config["position"].isNull()) - { - x = config["position"]["x"].Integer(); - y = config["position"]["y"].Integer(); - } - - std::string image, text = buildText(config["text"]); - auto alignment = EAlignment::CENTER; - auto color = Colors::DEFAULT_KEY_COLOR; - auto font = EFonts::FONT_TIMES; - - if(!config["image"].isNull()) - image = config["image"].String(); - if(!config["alignment"].isNull()) - { - if(config["alignment"].String() == "left") - alignment = EAlignment::TOPLEFT; - if(config["alignment"].String() == "center") - alignment = EAlignment::CENTER; - if(config["alignment"].String() == "right") - alignment = EAlignment::BOTTOMRIGHT; - } - if(!config["color"].isNull()) - { - if(config["color"].String() == "yellow") - color = Colors::YELLOW; - if(config["color"].String() == "white") - color = Colors::WHITE; - if(config["color"].String() == "gold") - color = Colors::METALLIC_GOLD; - if(config["color"].String() == "green") - color = Colors::GREEN; - if(config["color"].String() == "orange") - color = Colors::ORANGE; - if(config["color"].String() == "bright-yellow") - color = Colors::BRIGHT_YELLOW; - } - if(!config["font"].isNull()) - { - if(config["font"].String() == "big") - font = EFonts::FONT_BIG; - if(config["font"].String() == "medium") - font = EFonts::FONT_MEDIUM; - if(config["font"].String() == "small") - font = EFonts::FONT_SMALL; - if(config["font"].String() == "tiny") - font = EFonts::FONT_TINY; - } - - - if(type == "picture") - { - return std::make_shared<CPicture>(image, x, y); - } - if(type == "label") - { - return std::make_shared<CLabel>(x, y, font, alignment, color, text); - } - if(type == "toggleGroup") - { - auto group = std::make_shared<CToggleGroup>(0); - group->pos.x += x; - group->pos.y += y; - if(!config["items"].isNull()) - { - SObjectConstruction obj__i(group.get()); - int itemIdx = -1; - for(const auto & item : config["items"].Vector()) - { - itemIdx = item["index"].isNull() ? itemIdx + 1 : item["index"].Integer(); - group->addToggle(itemIdx, std::dynamic_pointer_cast<CToggleBase>(buildWidget(item))); - } - } - if(!config["selected"].isNull()) - group->setSelected(config["selected"].Integer()); - if(!config["callback"].isNull()) - group->addCallback(callbacks[config["callback"].String()]); - return group; - } - if(type == "toggleButton") - { - std::pair<std::string, std::string> zelp; - if(!config["zelp"].isNull()) - zelp = CGI->generaltexth->zelp[config["zelp"].Integer()]; - auto button = std::make_shared<CToggleButton>(Point(x, y), image, zelp); - if(!config["selected"].isNull()) - button->setSelected(config["selected"].Bool()); - if(!config["imageOrder"].isNull()) - { - auto imgOrder = config["imageOrder"].Vector(); - assert(imgOrder.size() >= 4); - button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer()); - } - if(!config["callback"].isNull()) - button->addCallback(callbacks[config["callback"].String()]); - return button; - } - if(type == "button") - { - std::pair<std::string, std::string> zelp; - if(!config["zelp"].isNull()) - zelp = CGI->generaltexth->zelp[config["zelp"].Integer()]; - auto button = std::make_shared<CButton>(Point(x, y), image, zelp); - return button; - } - if(type == "labelGroup") - { - auto group = std::make_shared<CLabelGroup>(font, alignment, color); - if(!config["items"].isNull()) - { - for(const auto & item : config["items"].Vector()) - { - if(!item["position"].isNull()) - { - x = item["position"]["x"].Integer(); - y = item["position"]["y"].Integer(); - } - if(!item["text"].isNull()) - text = buildText(item["text"]); - group->add(x, y, text); - } - } - return group; - } - return std::shared_ptr<CIntObject>(nullptr); -} diff --git a/client/gui/InterfaceBuilder.h b/client/gui/InterfaceBuilder.h deleted file mode 100644 index 48501d03b..000000000 --- a/client/gui/InterfaceBuilder.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -* InterfaceBuilder.h, part of VCMI engine -* -* Authors: listed in file AUTHORS in main folder -* -* License: GNU General Public License v2.0 or later -* Full text of license available in license.txt file, in main folder -* -*/ - -#pragma once - -#include "CIntObject.h" - -#include "../../lib/JsonNode.h" - -class InterfaceObjectConfigurable: public CIntObject -{ -public: - InterfaceObjectConfigurable(); - InterfaceObjectConfigurable(const JsonNode & config); - -protected: - //must be called after adding callbacks - void init(const JsonNode & config); - - void addCallback(const std::string & callbackName, std::function<void(int)> callback); - - template<class T> - const std::shared_ptr<T> widget(const std::string & name) const - { - auto iter = widgets.find(name); - if(iter == widgets.end()) - return nullptr; - return std::dynamic_pointer_cast<T>(iter->second); - } - -private: - - std::map<std::string, std::shared_ptr<CIntObject>> widgets; - std::map<std::string, std::function<void(int)>> callbacks; - - std::shared_ptr<CIntObject> buildWidget(const JsonNode & config); - - std::string buildText(const JsonNode & param) const; -}; From f90cb1be9025a886df630e6737ba0b2bd6c03ddb Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Tue, 13 Dec 2022 03:47:29 +0400 Subject: [PATCH 09/35] Template list prototype looks fine --- client/gui/InterfaceObjectConfigurable.cpp | 9 +++ client/lobby/RandomMapTab.cpp | 67 ++++++++++++++++++++++ client/lobby/RandomMapTab.h | 31 ++++++++++ 3 files changed, 107 insertions(+) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 3ec429257..6c8c64f38 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -179,6 +179,15 @@ std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonN if(!config["zelp"].isNull()) zelp = CGI->generaltexth->zelp[config["zelp"].Integer()]; auto button = std::make_shared<CButton>(Point(x, y), image, zelp); + if(!config["items"].isNull()) + { + for(const auto & item : config["items"].Vector()) + { + button->addOverlay(buildWidget(item)); + } + } + if(!config["callback"].isNull()) + button->addCallback(std::bind(callbacks[config["callback"].String()], 0)); return button; } if(type == "labelGroup") diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index eaf8dd264..163cc5798 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -105,6 +105,13 @@ RandomMapTab::RandomMapTab(): updateMapInfoByHost(); }); + //new callbacks available only from mod + addCallback("templateSelection", [&](int) + { + GH.pushInt(std::shared_ptr<TemplatesDropBox>(new TemplatesDropBox(mapGenOptions))); + }); + + init(config); updateMapInfoByHost(); @@ -247,3 +254,63 @@ std::vector<int> RandomMapTab::getPossibleMapSizes() { return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE, CMapHeader::MAP_SIZE_HUGE, CMapHeader::MAP_SIZE_XHUGE, CMapHeader::MAP_SIZE_GIANT}; } + +TemplatesDropBox::ListItem::ListItem(Point position, const std::string & text) + : CIntObject(LCLICK | HOVER, position) +{ + OBJ_CONSTRUCTION; + labelName = std::make_shared<CLabel>(0, 0, FONT_SMALL, EAlignment::TOPLEFT, Colors::WHITE, text); + labelName->setAutoRedraw(false); + + hoverImage = std::make_shared<CPicture>("List10Sl", 0, 0); + hoverImage->visible = false; + + pos.w = hoverImage->pos.w; + pos.h = hoverImage->pos.h; + type |= REDRAW_PARENT; +} + +void TemplatesDropBox::ListItem::hover(bool on) +{ + hoverImage->visible = on; + redraw(); +} + +TemplatesDropBox::TemplatesDropBox(std::shared_ptr<CMapGenOptions> options): + CIntObject(LCLICK | HOVER), + mapGenOptions(options) +{ + OBJ_CONSTRUCTION; + background = std::make_shared<CPicture>("List10Bk", 158, 76); + + int positionsToShow = 10; + + for(int i = 0; i < positionsToShow; i++) + listItems.push_back(std::make_shared<ListItem>(Point(158, 76 + i * 25), "test" + std::to_string(i))); + + slider = std::make_shared<CSlider>(Point(212 + 158, 76), 252, std::bind(&TemplatesDropBox::sliderMove, this, _1), positionsToShow, 20, 0, false, CSlider::BLUE); + + pos = background->pos; +} + +void TemplatesDropBox::sliderMove(int slidPos) +{ + if(!slider) + return; // ignore spurious call when slider is being created + //updateListItems(); + redraw(); +} + +void TemplatesDropBox::hover(bool on) +{ + hovered = on; +} + +void TemplatesDropBox::clickLeft(tribool down, bool previousState) +{ + if(!hovered) + { + assert(GH.topInt().get() == this); + GH.popInt(GH.topInt()); + } +} diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index 6b82ae3e2..a9551cea6 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -24,6 +24,7 @@ VCMI_LIB_NAMESPACE_END class CToggleButton; class CLabel; class CLabelGroup; +class CSlider; class RandomMapTab : public InterfaceObjectConfigurable { @@ -44,3 +45,33 @@ private: std::shared_ptr<CMapGenOptions> mapGenOptions; std::shared_ptr<CMapInfo> mapInfo; }; + +class TemplatesDropBox : public CIntObject +{ + struct ListItem : public CIntObject + { + std::shared_ptr<CLabel> labelName; + std::shared_ptr<CPicture> hoverImage; + ListItem(Point position, const std::string & text); + void updateItem(int info = 0, bool selected = false); + + void hover(bool on) override; + }; + +public: + TemplatesDropBox(std::shared_ptr<CMapGenOptions> options); + + void hover(bool on) override; + void clickLeft(tribool down, bool previousState) override; + +private: + + void sliderMove(int slidPos); + + std::shared_ptr<CMapGenOptions> mapGenOptions; + + std::shared_ptr<CPicture> background; + std::vector<std::shared_ptr<ListItem>> listItems; + std::shared_ptr<CSlider> slider; + +}; From 1e2abae62bc10f46d6a1cce3f0752e5b46a94591 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Tue, 13 Dec 2022 04:38:18 +0400 Subject: [PATCH 10/35] Template selections works --- client/lobby/RandomMapTab.cpp | 99 ++++++++++++++++++++++++++++++----- client/lobby/RandomMapTab.h | 19 +++++-- 2 files changed, 101 insertions(+), 17 deletions(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 163cc5798..c65b3d6ef 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -28,6 +28,7 @@ #include "../../lib/mapping/CMapInfo.h" #include "../../lib/rmg/CMapGenOptions.h" #include "../../lib/CModHandler.h" +#include "../../lib/rmg/CRmgTemplateStorage.h" RandomMapTab::RandomMapTab(): InterfaceObjectConfigurable() @@ -108,7 +109,7 @@ RandomMapTab::RandomMapTab(): //new callbacks available only from mod addCallback("templateSelection", [&](int) { - GH.pushInt(std::shared_ptr<TemplatesDropBox>(new TemplatesDropBox(mapGenOptions))); + GH.pushInt(std::make_shared<TemplatesDropBox>(this)); }); @@ -190,6 +191,18 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) w->setSelected(opts->getMonsterStrength()); } +void RandomMapTab::setTemplate(const CRmgTemplate * tmpl) +{ + mapGenOptions->setMapTemplate(tmpl); + if(auto w = widget<CButton>("templateButton")) + { + if(tmpl) + w->addTextOverlay(tmpl->getName(), EFonts::FONT_SMALL); + else + w->addTextOverlay("default", EFonts::FONT_SMALL); + } +} + void RandomMapTab::deactivateButtonsFrom(CToggleGroup * group, int startId) { logGlobal->debug("Blocking buttons from %d", startId); @@ -255,11 +268,12 @@ std::vector<int> RandomMapTab::getPossibleMapSizes() return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE, CMapHeader::MAP_SIZE_HUGE, CMapHeader::MAP_SIZE_XHUGE, CMapHeader::MAP_SIZE_GIANT}; } -TemplatesDropBox::ListItem::ListItem(Point position, const std::string & text) - : CIntObject(LCLICK | HOVER, position) +TemplatesDropBox::ListItem::ListItem(TemplatesDropBox * _dropBox, Point position) + : CIntObject(LCLICK | HOVER, position), + dropBox(_dropBox) { OBJ_CONSTRUCTION; - labelName = std::make_shared<CLabel>(0, 0, FONT_SMALL, EAlignment::TOPLEFT, Colors::WHITE, text); + labelName = std::make_shared<CLabel>(0, 0, FONT_SMALL, EAlignment::TOPLEFT, Colors::WHITE); labelName->setAutoRedraw(false); hoverImage = std::make_shared<CPicture>("List10Sl", 0, 0); @@ -270,26 +284,63 @@ TemplatesDropBox::ListItem::ListItem(Point position, const std::string & text) type |= REDRAW_PARENT; } +void TemplatesDropBox::ListItem::updateItem(int idx, const CRmgTemplate * _item) +{ + item = _item; + if(item) + { + labelName->setText(item->getName()); + } + else + { + if(idx) + labelName->setText(""); + else + labelName->setText("default"); + } +} + void TemplatesDropBox::ListItem::hover(bool on) { - hoverImage->visible = on; + if(labelName->getText().empty()) + { + hovered = false; + hoverImage->visible = false; + } + else + { + hoverImage->visible = on; + } redraw(); } -TemplatesDropBox::TemplatesDropBox(std::shared_ptr<CMapGenOptions> options): - CIntObject(LCLICK | HOVER), - mapGenOptions(options) +void TemplatesDropBox::ListItem::clickLeft(tribool down, bool previousState) { + if(down && hovered) + { + dropBox->setTemplate(item); + } +} + + +TemplatesDropBox::TemplatesDropBox(RandomMapTab * randomMapTab): + CIntObject(LCLICK | HOVER), + randomMapTab(randomMapTab) +{ + curItems = VLC->tplh->getTemplates(); + curItems.insert(curItems.begin(), nullptr); //default template + OBJ_CONSTRUCTION; background = std::make_shared<CPicture>("List10Bk", 158, 76); int positionsToShow = 10; for(int i = 0; i < positionsToShow; i++) - listItems.push_back(std::make_shared<ListItem>(Point(158, 76 + i * 25), "test" + std::to_string(i))); + listItems.push_back(std::make_shared<ListItem>(this, Point(158, 76 + i * 25))); - slider = std::make_shared<CSlider>(Point(212 + 158, 76), 252, std::bind(&TemplatesDropBox::sliderMove, this, _1), positionsToShow, 20, 0, false, CSlider::BLUE); + slider = std::make_shared<CSlider>(Point(212 + 158, 76), 252, std::bind(&TemplatesDropBox::sliderMove, this, _1), positionsToShow, (int)curItems.size(), 0, false, CSlider::BLUE); + updateListItems(); pos = background->pos; } @@ -297,7 +348,7 @@ void TemplatesDropBox::sliderMove(int slidPos) { if(!slider) return; // ignore spurious call when slider is being created - //updateListItems(); + updateListItems(); redraw(); } @@ -308,9 +359,33 @@ void TemplatesDropBox::hover(bool on) void TemplatesDropBox::clickLeft(tribool down, bool previousState) { - if(!hovered) + if(down && !hovered) { assert(GH.topInt().get() == this); GH.popInt(GH.topInt()); } } + +void TemplatesDropBox::updateListItems() +{ + int elemIdx = slider->getValue(); + for(auto item : listItems) + { + if(elemIdx < curItems.size()) + { + item->updateItem(elemIdx, curItems[elemIdx]); + elemIdx++; + } + else + { + item->updateItem(elemIdx); + } + } +} + +void TemplatesDropBox::setTemplate(const CRmgTemplate * tmpl) +{ + randomMapTab->setTemplate(tmpl); + assert(GH.topInt().get() == this); + GH.popInt(GH.topInt()); +} diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index a9551cea6..3728240c8 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -13,6 +13,7 @@ #include "../../lib/FunctionList.h" #include "../../lib/GameConstants.h" +#include "../../lib/rmg/CRmgTemplate.h" #include "../gui/InterfaceObjectConfigurable.h" VCMI_LIB_NAMESPACE_BEGIN @@ -33,6 +34,7 @@ public: void updateMapInfoByHost(); void setMapGenOptions(std::shared_ptr<CMapGenOptions> opts); + void setTemplate(const CRmgTemplate *); CFunctionList<void(std::shared_ptr<CMapInfo>, std::shared_ptr<CMapGenOptions>)> mapInfoChanged; @@ -52,26 +54,33 @@ class TemplatesDropBox : public CIntObject { std::shared_ptr<CLabel> labelName; std::shared_ptr<CPicture> hoverImage; - ListItem(Point position, const std::string & text); - void updateItem(int info = 0, bool selected = false); + TemplatesDropBox * dropBox; + const CRmgTemplate * item = nullptr; + + ListItem(TemplatesDropBox *, Point position); + void updateItem(int index, const CRmgTemplate * item = nullptr); void hover(bool on) override; + void clickLeft(tribool down, bool previousState) override; }; public: - TemplatesDropBox(std::shared_ptr<CMapGenOptions> options); + TemplatesDropBox(RandomMapTab * randomMapTab); void hover(bool on) override; void clickLeft(tribool down, bool previousState) override; + void setTemplate(const CRmgTemplate *); private: void sliderMove(int slidPos); + void updateListItems(); - std::shared_ptr<CMapGenOptions> mapGenOptions; - + RandomMapTab * randomMapTab; std::shared_ptr<CPicture> background; std::vector<std::shared_ptr<ListItem>> listItems; std::shared_ptr<CSlider> slider; + std::vector<const CRmgTemplate *> curItems; + }; From 53b2f68560f059f0cef9a7b8161245f0f43fc85f Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Wed, 14 Dec 2022 04:37:11 +0400 Subject: [PATCH 11/35] Serialize template --- client/lobby/RandomMapTab.cpp | 124 +++++++++++++++++++++++++------- client/lobby/RandomMapTab.h | 6 +- lib/rmg/CMapGenOptions.cpp | 26 ++++++- lib/rmg/CMapGenOptions.h | 40 ++++------- lib/rmg/CRmgTemplate.cpp | 17 ++++- lib/rmg/CRmgTemplate.h | 29 +++++++- lib/rmg/CRmgTemplateStorage.cpp | 3 +- lib/rmg/CZonePlacer.cpp | 1 + lib/rmg/Functions.h | 1 + 9 files changed, 189 insertions(+), 58 deletions(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index c65b3d6ef..77459a1b7 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -47,21 +47,15 @@ RandomMapTab::RandomMapTab(): addCallback("toggleTwoLevels", [&](bool on) { mapGenOptions->setHasTwoLevels(on); + setTemplate(mapGenOptions->getMapTemplate()); updateMapInfoByHost(); }); addCallback("setPlayersCount", [&](int btnId) { mapGenOptions->setPlayerCount(btnId); - - if(auto w = widget<CToggleGroup>("groupMaxTeams")) - deactivateButtonsFrom(w.get(), btnId); - - // deactive some CompOnlyPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I - if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers")) - deactivateButtonsFrom(w.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); - - validatePlayersCnt(btnId); + setMapGenOptions(mapGenOptions); + //validatePlayersCnt(btnId); updateMapInfoByHost(); }); @@ -74,14 +68,8 @@ RandomMapTab::RandomMapTab(): addCallback("setCompOnlyPlayers", [&](int btnId) { mapGenOptions->setCompOnlyPlayerCount(btnId); - - // deactive some MaxPlayers buttons to prevent total number of players exceeds PlayerColor::PLAYER_LIMIT_I - if(auto w = widget<CToggleGroup>("groupMaxPlayers")) - deactivateButtonsFrom(w.get(), PlayerColor::PLAYER_LIMIT_I - btnId + 1); - - if(auto w = widget<CToggleGroup>("groupCompOnlyTeams")) - deactivateButtonsFrom(w.get(), (btnId == 0 ? 1 : btnId)); - validateCompOnlyPlayersCnt(btnId); + setMapGenOptions(mapGenOptions); + //validateCompOnlyPlayersCnt(btnId); updateMapInfoByHost(); }); @@ -173,20 +161,80 @@ void RandomMapTab::updateMapInfoByHost() void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) { + mapGenOptions = opts; + + //prepare allowed options + for(int i = 0; i <= PlayerColor::PLAYER_LIMIT_I; ++i) + { + playerCountAllowed.insert(i); + compCountAllowed.insert(i); + playerTeamsAllowed.insert(i); + compTeamsAllowed.insert(i); + } + auto * tmpl = mapGenOptions->getMapTemplate(); + if(tmpl) + { + playerCountAllowed = tmpl->getPlayers().getNumbers(); + compCountAllowed = tmpl->getCpuPlayers().getNumbers(); + } + if(mapGenOptions->getPlayerCount() != CMapGenOptions::RANDOM_SIZE) + { + vstd::erase_if(compCountAllowed, + [opts](int el){ + return PlayerColor::PLAYER_LIMIT_I - opts->getPlayerCount() < el; + }); + vstd::erase_if(playerTeamsAllowed, + [opts](int el){ + return PlayerColor::PLAYER_LIMIT_I - opts->getPlayerCount() < el + 1; + }); + } + if(mapGenOptions->getCompOnlyPlayerCount() != CMapGenOptions::RANDOM_SIZE) + { + vstd::erase_if(playerCountAllowed, + [opts](int el){ + return PlayerColor::PLAYER_LIMIT_I - opts->getCompOnlyPlayerCount() < el; + }); + vstd::erase_if(compTeamsAllowed, + [opts](int el){ + return PlayerColor::PLAYER_LIMIT_I - opts->getCompOnlyPlayerCount() < el + 1; + }); + } + if(auto w = widget<CToggleGroup>("groupMapSize")) w->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth())); if(auto w = widget<CToggleButton>("buttonTwoLevels")) w->setSelected(opts->getHasTwoLevels()); if(auto w = widget<CToggleGroup>("groupMaxPlayers")) + { w->setSelected(opts->getPlayerCount()); + deactivateButtonsFrom(*w, playerCountAllowed); + } if(auto w = widget<CToggleGroup>("groupMaxTeams")) + { w->setSelected(opts->getTeamCount()); + deactivateButtonsFrom(*w, playerCountAllowed); + } if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers")) + { w->setSelected(opts->getCompOnlyPlayerCount()); + deactivateButtonsFrom(*w, playerTeamsAllowed); + } if(auto w = widget<CToggleGroup>("groupCompOnlyTeams")) + { w->setSelected(opts->getCompOnlyTeamCount()); + deactivateButtonsFrom(*w, compTeamsAllowed); + } if(auto w = widget<CToggleGroup>("groupWaterContent")) + { w->setSelected(opts->getWaterContent()); + if(opts->getMapTemplate()) + { + std::set<int> allowedWater(opts->getMapTemplate()->getWaterContentAllowed().begin(), opts->getMapTemplate()->getWaterContentAllowed().end()); + deactivateButtonsFrom(*w, allowedWater); + } + else + deactivateButtonsFrom(*w, {-1}); + } if(auto w = widget<CToggleGroup>("groupMonsterStrength")) w->setSelected(opts->getMonsterStrength()); } @@ -194,6 +242,7 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) void RandomMapTab::setTemplate(const CRmgTemplate * tmpl) { mapGenOptions->setMapTemplate(tmpl); + setMapGenOptions(mapGenOptions); if(auto w = widget<CButton>("templateButton")) { if(tmpl) @@ -201,16 +250,41 @@ void RandomMapTab::setTemplate(const CRmgTemplate * tmpl) else w->addTextOverlay("default", EFonts::FONT_SMALL); } + updateMapInfoByHost(); } -void RandomMapTab::deactivateButtonsFrom(CToggleGroup * group, int startId) +void RandomMapTab::deactivateButtonsFrom(CToggleGroup & group, int startAllowed, int endAllowed) { - logGlobal->debug("Blocking buttons from %d", startId); - for(auto toggle : group->buttons) + logGlobal->debug("Blocking all buttons except %d - %d", startAllowed, endAllowed); + for(auto toggle : group.buttons) { if(auto button = std::dynamic_pointer_cast<CToggleButton>(toggle.second)) { - if(startId == CMapGenOptions::RANDOM_SIZE || toggle.first < startId) + if(toggle.first == CMapGenOptions::RANDOM_SIZE + || (startAllowed == CMapGenOptions::RANDOM_SIZE && endAllowed == CMapGenOptions::RANDOM_SIZE) + || (toggle.first >= startAllowed + && (endAllowed == CMapGenOptions::RANDOM_SIZE || toggle.first <= endAllowed))) + { + //button->block(false); + } + else + { + button->block(true); + } + } + } +} + +void RandomMapTab::deactivateButtonsFrom(CToggleGroup & group, const std::set<int> & allowed) +{ + logGlobal->debug("Blocking buttons"); + for(auto toggle : group.buttons) + { + if(auto button = std::dynamic_pointer_cast<CToggleButton>(toggle.second)) + { + if(allowed.count(CMapGenOptions::RANDOM_SIZE) + || allowed.count(toggle.first) + || toggle.first == CMapGenOptions::RANDOM_SIZE) { button->block(false); } @@ -229,7 +303,7 @@ void RandomMapTab::validatePlayersCnt(int playersCnt) return; } - if(mapGenOptions->getTeamCount() >= playersCnt) + /*if(mapGenOptions->getTeamCount() >= playersCnt) { mapGenOptions->setTeamCount(playersCnt - 1); if(auto w = widget<CToggleGroup>("groupMaxTeams")) @@ -241,7 +315,7 @@ void RandomMapTab::validatePlayersCnt(int playersCnt) mapGenOptions->setCompOnlyPlayerCount(PlayerColor::PLAYER_LIMIT_I - playersCnt); if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers")) w->setSelected(mapGenOptions->getCompOnlyPlayerCount()); - } + }*/ validateCompOnlyPlayersCnt(mapGenOptions->getCompOnlyPlayerCount()); } @@ -253,14 +327,14 @@ void RandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt) return; } - if(mapGenOptions->getCompOnlyTeamCount() >= compOnlyPlayersCnt) + /*if(mapGenOptions->getCompOnlyTeamCount() >= compOnlyPlayersCnt) { int compOnlyTeamCount = compOnlyPlayersCnt == 0 ? 0 : compOnlyPlayersCnt - 1; mapGenOptions->setCompOnlyTeamCount(compOnlyTeamCount); updateMapInfoByHost(); if(auto w = widget<CToggleGroup>("groupCompOnlyTeams")) w->setSelected(compOnlyTeamCount); - } + }*/ } std::vector<int> RandomMapTab::getPossibleMapSizes() diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index 3728240c8..ba2723f5a 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -39,13 +39,17 @@ public: CFunctionList<void(std::shared_ptr<CMapInfo>, std::shared_ptr<CMapGenOptions>)> mapInfoChanged; private: - void deactivateButtonsFrom(CToggleGroup * group, int startId); + void deactivateButtonsFrom(CToggleGroup & group, int startAllower, int endAllowed); + void deactivateButtonsFrom(CToggleGroup & group, const std::set<int> & allowed); void validatePlayersCnt(int playersCnt); void validateCompOnlyPlayersCnt(int compOnlyPlayersCnt); std::vector<int> getPossibleMapSizes(); std::shared_ptr<CMapGenOptions> mapGenOptions; std::shared_ptr<CMapInfo> mapInfo; + + //options allowed - need to store as impact each other + std::set<int> playerCountAllowed, playerTeamsAllowed, compCountAllowed, compTeamsAllowed; }; class TemplatesDropBox : public CIntObject diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index 141e61782..bac65c5af 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -204,8 +204,30 @@ const CRmgTemplate * CMapGenOptions::getMapTemplate() const void CMapGenOptions::setMapTemplate(const CRmgTemplate * value) { mapTemplate = value; - //TODO validate & adapt options according to template - //assert(0); + //validate & adapt options according to template + if(mapTemplate) + { + if(!mapTemplate->matchesSize(int3(getWidth(), getHeight(), 1 + getHasTwoLevels()))) + { + auto sizes = mapTemplate->getMapSizes(); + setWidth(sizes.first.x); + setHeight(sizes.first.y); + setHasTwoLevels(sizes.first.z - 1); + } + + if(!mapTemplate->getPlayers().isInRange(getPlayerCount())) + setPlayerCount(RANDOM_SIZE); + if(!mapTemplate->getCpuPlayers().isInRange(getCompOnlyPlayerCount())) + setCompOnlyPlayerCount(RANDOM_SIZE); + if(!mapTemplate->getWaterContentAllowed().count(getWaterContent())) + setWaterContent(EWaterContent::RANDOM); + } +} + +void CMapGenOptions::setMapTemplate(const std::string & name) +{ + if(!name.empty()) + setMapTemplate(VLC->tplh->getTemplate(name)); } void CMapGenOptions::finalize(CRandomGenerator & rand) diff --git a/lib/rmg/CMapGenOptions.h b/lib/rmg/CMapGenOptions.h index 9017576b1..d8277518c 100644 --- a/lib/rmg/CMapGenOptions.h +++ b/lib/rmg/CMapGenOptions.h @@ -11,37 +11,12 @@ #pragma once #include "../GameConstants.h" +#include "CRmgTemplate.h" VCMI_LIB_NAMESPACE_BEGIN -class CRmgTemplate; class CRandomGenerator; -namespace EWaterContent -{ - enum EWaterContent - { - RANDOM = -1, - NONE, - NORMAL, - ISLANDS - }; -} - -namespace EMonsterStrength -{ - enum EMonsterStrength - { - RANDOM = -2, - ZONE_WEAK = -1, - ZONE_NORMAL = 0, - ZONE_STRONG = 1, - GLOBAL_WEAK = 2, - GLOBAL_NORMAL = 3, - GLOBAL_STRONG = 4 - }; -} - namespace EPlayerType { enum EPlayerType @@ -143,6 +118,7 @@ public: /// Default: Not set/random. const CRmgTemplate * getMapTemplate() const; void setMapTemplate(const CRmgTemplate * value); + void setMapTemplate(const std::string & name); std::vector<const CRmgTemplate *> getPossibleTemplates() const; @@ -187,7 +163,17 @@ public: h & waterContent; h & monsterStrength; h & players; - //TODO add name of template to class, enables selection of a template by a user + std::string templateName; + if(mapTemplate && h.saving) + { + templateName = mapTemplate->getId(); + } + //if(version > xxx) do not forget to bump version + h & templateName; + if(!h.saving) + { + setMapTemplate(templateName); + } } }; diff --git a/lib/rmg/CRmgTemplate.cpp b/lib/rmg/CRmgTemplate.cpp index 7e1051a5f..4e54e413a 100644 --- a/lib/rmg/CRmgTemplate.cpp +++ b/lib/rmg/CRmgTemplate.cpp @@ -501,9 +501,19 @@ void CRmgTemplate::setId(const std::string & value) id = value; } +void CRmgTemplate::setName(const std::string & value) +{ + name = value; +} + const std::string & CRmgTemplate::getName() const { - return name.empty() ? id : name; + return name; +} + +const std::string & CRmgTemplate::getId() const +{ + return id; } const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getPlayers() const @@ -531,6 +541,11 @@ void CRmgTemplate::validate() const //TODO add some validation checks, throw on failure } +std::pair<int3, int3> CRmgTemplate::getMapSizes() const +{ + return {minSize, maxSize}; +} + void CRmgTemplate::CPlayerCountRange::addRange(int lower, int upper) { range.push_back(std::make_pair(lower, upper)); diff --git a/lib/rmg/CRmgTemplate.h b/lib/rmg/CRmgTemplate.h index 2b8ba45e3..50ce3227f 100644 --- a/lib/rmg/CRmgTemplate.h +++ b/lib/rmg/CRmgTemplate.h @@ -14,7 +14,6 @@ #include "../GameConstants.h" #include "../ResourceSet.h" #include "../Terrain.h" -#include "CMapGenOptions.h" VCMI_LIB_NAMESPACE_BEGIN @@ -32,6 +31,31 @@ namespace ETemplateZoneType }; } +namespace EWaterContent +{ + enum EWaterContent + { + RANDOM = -1, + NONE, + NORMAL, + ISLANDS + }; +} + +namespace EMonsterStrength +{ + enum EMonsterStrength + { + RANDOM = -2, + ZONE_WEAK = -1, + ZONE_NORMAL = 0, + ZONE_STRONG = 1, + GLOBAL_WEAK = 2, + GLOBAL_NORMAL = 3, + GLOBAL_STRONG = 4 + }; +} + class DLL_LINKAGE CTreasureInfo { public: @@ -194,10 +218,13 @@ public: const std::set<EWaterContent::EWaterContent> & getWaterContentAllowed() const; void setId(const std::string & value); + void setName(const std::string & value); + const std::string & getId() const; const std::string & getName() const; const CPlayerCountRange & getPlayers() const; const CPlayerCountRange & getCpuPlayers() const; + std::pair<int3, int3> getMapSizes() const; const Zones & getZones() const; const std::vector<rmg::ZoneConnection> & getConnections() const; diff --git a/lib/rmg/CRmgTemplateStorage.cpp b/lib/rmg/CRmgTemplateStorage.cpp index ee0564488..cf932ece8 100644 --- a/lib/rmg/CRmgTemplateStorage.cpp +++ b/lib/rmg/CRmgTemplateStorage.cpp @@ -31,8 +31,9 @@ void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const { JsonDeserializer handler(nullptr, data); auto fullKey = normalizeIdentifier(scope, "core", name); //actually it's not used - templates[fullKey].setId(name); + templates[fullKey].setId(fullKey); templates[fullKey].serializeJson(handler); + templates[fullKey].setName(name); templates[fullKey].validate(); } catch(const std::exception & e) diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index 431b43ee2..572fa4fff 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -13,6 +13,7 @@ #include "CZonePlacer.h" #include "../mapping/CMap.h" #include "../mapping/CMapEditManager.h" +#include "CMapGenOptions.h" #include "RmgMap.h" #include "Zone.h" #include "Functions.h" diff --git a/lib/rmg/Functions.h b/lib/rmg/Functions.h index 9e60506cf..326ad8891 100644 --- a/lib/rmg/Functions.h +++ b/lib/rmg/Functions.h @@ -19,6 +19,7 @@ class RmgMap; class ObjectManager; class ObjectTemplate; class CMapGenerator; +class CRandomGenerator; class rmgException : public std::exception { From fc7f1dbc5aadb1d87da17945d40d6ec175179737 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Wed, 14 Dec 2022 05:23:21 +0400 Subject: [PATCH 12/35] Fix template selection --- client/lobby/RandomMapTab.cpp | 81 +++++------------------------------ client/lobby/RandomMapTab.h | 5 +-- lib/rmg/CMapGenOptions.cpp | 1 - 3 files changed, 12 insertions(+), 75 deletions(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 77459a1b7..ebd84efc7 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -42,12 +42,17 @@ RandomMapTab::RandomMapTab(): auto mapSizeVal = getPossibleMapSizes(); mapGenOptions->setWidth(mapSizeVal[btnId]); mapGenOptions->setHeight(mapSizeVal[btnId]); + if(mapGenOptions->getMapTemplate()) + if(!mapGenOptions->getMapTemplate()->matchesSize(int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()})) + setTemplate(nullptr); updateMapInfoByHost(); }); addCallback("toggleTwoLevels", [&](bool on) { mapGenOptions->setHasTwoLevels(on); - setTemplate(mapGenOptions->getMapTemplate()); + if(mapGenOptions->getMapTemplate()) + if(!mapGenOptions->getMapTemplate()->matchesSize(int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()})) + setTemplate(nullptr); updateMapInfoByHost(); }); @@ -55,7 +60,6 @@ RandomMapTab::RandomMapTab(): { mapGenOptions->setPlayerCount(btnId); setMapGenOptions(mapGenOptions); - //validatePlayersCnt(btnId); updateMapInfoByHost(); }); @@ -69,7 +73,6 @@ RandomMapTab::RandomMapTab(): { mapGenOptions->setCompOnlyPlayerCount(btnId); setMapGenOptions(mapGenOptions); - //validateCompOnlyPlayersCnt(btnId); updateMapInfoByHost(); }); @@ -97,7 +100,7 @@ RandomMapTab::RandomMapTab(): //new callbacks available only from mod addCallback("templateSelection", [&](int) { - GH.pushInt(std::make_shared<TemplatesDropBox>(this)); + GH.pushInt(std::make_shared<TemplatesDropBox>(this, int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()})); }); @@ -212,12 +215,12 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) if(auto w = widget<CToggleGroup>("groupMaxTeams")) { w->setSelected(opts->getTeamCount()); - deactivateButtonsFrom(*w, playerCountAllowed); + deactivateButtonsFrom(*w, playerTeamsAllowed); } if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers")) { w->setSelected(opts->getCompOnlyPlayerCount()); - deactivateButtonsFrom(*w, playerTeamsAllowed); + deactivateButtonsFrom(*w, compCountAllowed); } if(auto w = widget<CToggleGroup>("groupCompOnlyTeams")) { @@ -253,28 +256,6 @@ void RandomMapTab::setTemplate(const CRmgTemplate * tmpl) updateMapInfoByHost(); } -void RandomMapTab::deactivateButtonsFrom(CToggleGroup & group, int startAllowed, int endAllowed) -{ - logGlobal->debug("Blocking all buttons except %d - %d", startAllowed, endAllowed); - for(auto toggle : group.buttons) - { - if(auto button = std::dynamic_pointer_cast<CToggleButton>(toggle.second)) - { - if(toggle.first == CMapGenOptions::RANDOM_SIZE - || (startAllowed == CMapGenOptions::RANDOM_SIZE && endAllowed == CMapGenOptions::RANDOM_SIZE) - || (toggle.first >= startAllowed - && (endAllowed == CMapGenOptions::RANDOM_SIZE || toggle.first <= endAllowed))) - { - //button->block(false); - } - else - { - button->block(true); - } - } - } -} - void RandomMapTab::deactivateButtonsFrom(CToggleGroup & group, const std::set<int> & allowed) { logGlobal->debug("Blocking buttons"); @@ -296,47 +277,6 @@ void RandomMapTab::deactivateButtonsFrom(CToggleGroup & group, const std::set<in } } -void RandomMapTab::validatePlayersCnt(int playersCnt) -{ - if(playersCnt == CMapGenOptions::RANDOM_SIZE) - { - return; - } - - /*if(mapGenOptions->getTeamCount() >= playersCnt) - { - mapGenOptions->setTeamCount(playersCnt - 1); - if(auto w = widget<CToggleGroup>("groupMaxTeams")) - w->setSelected(mapGenOptions->getTeamCount()); - } - // total players should not exceed PlayerColor::PLAYER_LIMIT_I (8 in homm3) - if(mapGenOptions->getCompOnlyPlayerCount() + playersCnt > PlayerColor::PLAYER_LIMIT_I) - { - mapGenOptions->setCompOnlyPlayerCount(PlayerColor::PLAYER_LIMIT_I - playersCnt); - if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers")) - w->setSelected(mapGenOptions->getCompOnlyPlayerCount()); - }*/ - - validateCompOnlyPlayersCnt(mapGenOptions->getCompOnlyPlayerCount()); -} - -void RandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt) -{ - if(compOnlyPlayersCnt == CMapGenOptions::RANDOM_SIZE) - { - return; - } - - /*if(mapGenOptions->getCompOnlyTeamCount() >= compOnlyPlayersCnt) - { - int compOnlyTeamCount = compOnlyPlayersCnt == 0 ? 0 : compOnlyPlayersCnt - 1; - mapGenOptions->setCompOnlyTeamCount(compOnlyTeamCount); - updateMapInfoByHost(); - if(auto w = widget<CToggleGroup>("groupCompOnlyTeams")) - w->setSelected(compOnlyTeamCount); - }*/ -} - std::vector<int> RandomMapTab::getPossibleMapSizes() { return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE, CMapHeader::MAP_SIZE_HUGE, CMapHeader::MAP_SIZE_XHUGE, CMapHeader::MAP_SIZE_GIANT}; @@ -397,11 +337,12 @@ void TemplatesDropBox::ListItem::clickLeft(tribool down, bool previousState) } -TemplatesDropBox::TemplatesDropBox(RandomMapTab * randomMapTab): +TemplatesDropBox::TemplatesDropBox(RandomMapTab * randomMapTab, int3 size): CIntObject(LCLICK | HOVER), randomMapTab(randomMapTab) { curItems = VLC->tplh->getTemplates(); + vstd::erase_if(curItems, [size](const CRmgTemplate * t){return !t->matchesSize(size);}); curItems.insert(curItems.begin(), nullptr); //default template OBJ_CONSTRUCTION; diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index ba2723f5a..d30cc16cd 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -39,10 +39,7 @@ public: CFunctionList<void(std::shared_ptr<CMapInfo>, std::shared_ptr<CMapGenOptions>)> mapInfoChanged; private: - void deactivateButtonsFrom(CToggleGroup & group, int startAllower, int endAllowed); void deactivateButtonsFrom(CToggleGroup & group, const std::set<int> & allowed); - void validatePlayersCnt(int playersCnt); - void validateCompOnlyPlayersCnt(int compOnlyPlayersCnt); std::vector<int> getPossibleMapSizes(); std::shared_ptr<CMapGenOptions> mapGenOptions; @@ -69,7 +66,7 @@ class TemplatesDropBox : public CIntObject }; public: - TemplatesDropBox(RandomMapTab * randomMapTab); + TemplatesDropBox(RandomMapTab * randomMapTab, int3 size); void hover(bool on) override; void clickLeft(tribool down, bool previousState) override; diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index bac65c5af..c3ed14233 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -413,7 +413,6 @@ PlayerColor CMapGenOptions::getNextPlayerColor() const bool CMapGenOptions::checkOptions() const { - assert(countHumanPlayers() > 0); if(mapTemplate) { return true; From 4c3288dd618773e473bfae2ca8457bc29c7f2d6c Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Wed, 14 Dec 2022 05:45:50 +0400 Subject: [PATCH 13/35] Fix teams --- client/lobby/RandomMapTab.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index ebd84efc7..3667341cd 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -188,8 +188,11 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) }); vstd::erase_if(playerTeamsAllowed, [opts](int el){ - return PlayerColor::PLAYER_LIMIT_I - opts->getPlayerCount() < el + 1; + return opts->getPlayerCount() <= el; }); + + if(!playerTeamsAllowed.count(opts->getTeamCount())) + opts->setTeamCount(CMapGenOptions::RANDOM_SIZE); } if(mapGenOptions->getCompOnlyPlayerCount() != CMapGenOptions::RANDOM_SIZE) { @@ -199,8 +202,11 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) }); vstd::erase_if(compTeamsAllowed, [opts](int el){ - return PlayerColor::PLAYER_LIMIT_I - opts->getCompOnlyPlayerCount() < el + 1; + return opts->getCompOnlyPlayerCount() <= el; }); + + if(!compTeamsAllowed.count(opts->getCompOnlyTeamCount())) + opts->setCompOnlyTeamCount(CMapGenOptions::RANDOM_SIZE); } if(auto w = widget<CToggleGroup>("groupMapSize")) From 2371e3e9a221cf95fccaf3b6d0a411978c0c4428 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Fri, 16 Dec 2022 00:46:36 +0400 Subject: [PATCH 14/35] Interface builder refactored --- client/gui/InterfaceObjectConfigurable.cpp | 302 +++++++++++++-------- client/gui/InterfaceObjectConfigurable.h | 32 ++- client/lobby/RandomMapTab.cpp | 43 ++- client/lobby/RandomMapTab.h | 15 + 4 files changed, 265 insertions(+), 127 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 6c8c64f38..ddce4753e 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -57,7 +57,7 @@ void InterfaceObjectConfigurable::init(const JsonNode &config) } } -std::string InterfaceObjectConfigurable::buildText(const JsonNode & config) const +std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const { if(config.isNull()) return ""; @@ -69,145 +69,207 @@ std::string InterfaceObjectConfigurable::buildText(const JsonNode & config) cons return config.String(); } -std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonNode & config) +Point InterfaceObjectConfigurable::readPosition(const JsonNode & config) const +{ + Point p; + p.x = config["x"].Integer(); + p.y = config["y"].Integer(); + return p; +} + +ETextAlignment InterfaceObjectConfigurable::readTextAlignment(const JsonNode & config) const +{ + if(!config.isNull()) + { + if(config.String() == "center") + return ETextAlignment::CENTER; + if(config.String() == "left") + return ETextAlignment::TOPLEFT; + if(config.String() == "right") + return ETextAlignment::BOTTOMRIGHT; + } + return ETextAlignment::CENTER; +} + +SDL_Color InterfaceObjectConfigurable::readColor(const JsonNode & config) const +{ + if(!config.isNull()) + { + if(config.String() == "yellow") + return Colors::YELLOW; + if(config.String() == "white") + return Colors::WHITE; + if(config.String() == "gold") + return Colors::METALLIC_GOLD; + if(config.String() == "green") + return Colors::GREEN; + if(config.String() == "orange") + return Colors::ORANGE; + if(config.String() == "bright-yellow") + return Colors::BRIGHT_YELLOW; + } + return Colors::DEFAULT_KEY_COLOR; + +} +EFonts InterfaceObjectConfigurable::readFont(const JsonNode & config) const +{ + if(!config.isNull()) + { + if(config.String() == "big") + return EFonts::FONT_BIG; + if(config.String() == "medium") + return EFonts::FONT_MEDIUM; + if(config.String() == "small") + return EFonts::FONT_SMALL; + if(config.String() == "tiny") + return EFonts::FONT_TINY; + } + return EFonts::FONT_TIMES; +} + +std::pair<std::string, std::string> InterfaceObjectConfigurable::readHintText(const JsonNode & config) const +{ + std::pair<std::string, std::string> result; + if(!config.isNull()) + { + if(config.isNumber()) + return CGI->generaltexth->zelp[config.Integer()]; + + if(config.getType() == JsonNode::JsonType::DATA_STRUCT) + { + result.first = config["hover"].String(); + result.second = config["help"].String(); + return result; + } + if(config.getType() == JsonNode::JsonType::DATA_STRING) + { + result.first = result.second = config.String(); + } + } + return result; +} + +std::shared_ptr<CPicture> InterfaceObjectConfigurable::buildPicture(const JsonNode & config) const +{ + auto image = readText(config["image"]); + auto position = readPosition(config["position"]); + return std::make_shared<CPicture>(image, position.x, position.y); +} + +std::shared_ptr<CLabel> InterfaceObjectConfigurable::buildLabel(const JsonNode & config) const +{ + auto font = readFont(config["font"]); + auto alignment = readTextAlignment(config["alignment"]); + auto color = readColor(config["color"]); + auto text = readText(config["text"]); + auto position = readPosition(config["position"]); + return std::make_shared<CLabel>(position.x, position.y, font, alignment, color, text); +} + +std::shared_ptr<CToggleGroup> InterfaceObjectConfigurable::buildToggleGroup(const JsonNode & config) const +{ + auto position = readPosition(config["position"]); + auto group = std::make_shared<CToggleGroup>(0); + group->pos += position; + if(!config["items"].isNull()) + { + SObjectConstruction obj__i(group.get()); + int itemIdx = -1; + for(const auto & item : config["items"].Vector()) + { + itemIdx = item["index"].isNull() ? itemIdx + 1 : item["index"].Integer(); + group->addToggle(itemIdx, std::dynamic_pointer_cast<CToggleBase>(buildWidget(item))); + } + } + if(!config["selected"].isNull()) + group->setSelected(config["selected"].Integer()); + if(!config["callback"].isNull()) + group->addCallback(callbacks.at(config["callback"].String())); + return group; +} + +std::shared_ptr<CToggleButton> InterfaceObjectConfigurable::buildToggleButton(const JsonNode & config) const +{ + auto position = readPosition(config["position"]); + auto image = config["image"].String(); + auto zelp = readHintText(config["zelp"]); + auto button = std::make_shared<CToggleButton>(position, image, zelp); + if(!config["selected"].isNull()) + button->setSelected(config["selected"].Bool()); + if(!config["imageOrder"].isNull()) + { + auto imgOrder = config["imageOrder"].Vector(); + assert(imgOrder.size() >= 4); + button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer()); + } + if(!config["callback"].isNull()) + button->addCallback(callbacks.at(config["callback"].String())); + return button; +} + +std::shared_ptr<CButton> InterfaceObjectConfigurable::buildButton(const JsonNode & config) const +{ + auto position = readPosition(config["position"]); + auto image = config["image"].String(); + auto zelp = readHintText(config["zelp"]); + auto button = std::make_shared<CButton>(position, image, zelp); + if(!config["items"].isNull()) + { + for(const auto & item : config["items"].Vector()) + { + button->addOverlay(buildWidget(item)); + } + } + if(!config["callback"].isNull()) + button->addCallback(std::bind(callbacks.at(config["callback"].String()), 0)); + return button; +} + +std::shared_ptr<CLabelGroup> InterfaceObjectConfigurable::buildLabelGroup(const JsonNode & config) const +{ + auto font = readFont(config["font"]); + auto alignment = readTextAlignment(config["alignment"]); + auto color = readColor(config["color"]); + auto group = std::make_shared<CLabelGroup>(font, alignment, color); + if(!config["items"].isNull()) + { + for(const auto & item : config["items"].Vector()) + { + auto position = readPosition(item["position"]); + auto text = readText(item["text"]); + group->add(position.x, position.y, text); + } + } + return group; +} + +std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonNode & config) const { assert(!config.isNull()); auto type = config["type"].String(); - - int x = 0, y = 0; - if(!config["position"].isNull()) - { - x = config["position"]["x"].Integer(); - y = config["position"]["y"].Integer(); - } - - std::string image; - std::string text = buildText(config["text"]); - auto alignment = EAlignment::CENTER; - auto color = Colors::DEFAULT_KEY_COLOR; - auto font = EFonts::FONT_TIMES; - - if(!config["image"].isNull()) - image = config["image"].String(); - if(!config["alignment"].isNull()) - { - if(config["alignment"].String() == "left") - alignment = EAlignment::TOPLEFT; - if(config["alignment"].String() == "center") - alignment = EAlignment::CENTER; - if(config["alignment"].String() == "right") - alignment = EAlignment::BOTTOMRIGHT; - } - if(!config["color"].isNull()) - { - if(config["color"].String() == "yellow") - color = Colors::YELLOW; - if(config["color"].String() == "white") - color = Colors::WHITE; - if(config["color"].String() == "gold") - color = Colors::METALLIC_GOLD; - if(config["color"].String() == "green") - color = Colors::GREEN; - if(config["color"].String() == "orange") - color = Colors::ORANGE; - if(config["color"].String() == "bright-yellow") - color = Colors::BRIGHT_YELLOW; - } - if(!config["font"].isNull()) - { - if(config["font"].String() == "big") - font = EFonts::FONT_BIG; - if(config["font"].String() == "medium") - font = EFonts::FONT_MEDIUM; - if(config["font"].String() == "small") - font = EFonts::FONT_SMALL; - if(config["font"].String() == "tiny") - font = EFonts::FONT_TINY; - } - - if(type == "picture") { - return std::make_shared<CPicture>(image, x, y); + return buildPicture(config); } if(type == "label") { - return std::make_shared<CLabel>(x, y, font, alignment, color, text); + return buildLabel(config); } if(type == "toggleGroup") { - auto group = std::make_shared<CToggleGroup>(0); - group->pos.x += x; - group->pos.y += y; - if(!config["items"].isNull()) - { - SObjectConstruction obj__i(group.get()); - int itemIdx = -1; - for(const auto & item : config["items"].Vector()) - { - itemIdx = item["index"].isNull() ? itemIdx + 1 : item["index"].Integer(); - group->addToggle(itemIdx, std::dynamic_pointer_cast<CToggleBase>(buildWidget(item))); - } - } - if(!config["selected"].isNull()) - group->setSelected(config["selected"].Integer()); - if(!config["callback"].isNull()) - group->addCallback(callbacks[config["callback"].String()]); - return group; + return buildToggleGroup(config); } if(type == "toggleButton") { - std::pair<std::string, std::string> zelp; - if(!config["zelp"].isNull()) - zelp = CGI->generaltexth->zelp[config["zelp"].Integer()]; - auto button = std::make_shared<CToggleButton>(Point(x, y), image, zelp); - if(!config["selected"].isNull()) - button->setSelected(config["selected"].Bool()); - if(!config["imageOrder"].isNull()) - { - auto imgOrder = config["imageOrder"].Vector(); - assert(imgOrder.size() >= 4); - button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer()); - } - if(!config["callback"].isNull()) - button->addCallback(callbacks[config["callback"].String()]); - return button; + return buildToggleButton(config); } if(type == "button") { - std::pair<std::string, std::string> zelp; - if(!config["zelp"].isNull()) - zelp = CGI->generaltexth->zelp[config["zelp"].Integer()]; - auto button = std::make_shared<CButton>(Point(x, y), image, zelp); - if(!config["items"].isNull()) - { - for(const auto & item : config["items"].Vector()) - { - button->addOverlay(buildWidget(item)); - } - } - if(!config["callback"].isNull()) - button->addCallback(std::bind(callbacks[config["callback"].String()], 0)); - return button; + return buildButton(config); } if(type == "labelGroup") { - auto group = std::make_shared<CLabelGroup>(font, alignment, color); - if(!config["items"].isNull()) - { - for(const auto & item : config["items"].Vector()) - { - if(!item["position"].isNull()) - { - x = item["position"]["x"].Integer(); - y = item["position"]["y"].Integer(); - } - if(!item["text"].isNull()) - text = buildText(item["text"]); - group->add(x, y, text); - } - } - return group; + return buildLabelGroup(config); } return std::shared_ptr<CIntObject>(nullptr); } diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h index 48501d03b..51f23d7be 100644 --- a/client/gui/InterfaceObjectConfigurable.h +++ b/client/gui/InterfaceObjectConfigurable.h @@ -14,6 +14,13 @@ #include "../../lib/JsonNode.h" +class CPicture; +class CLabel; +class CToggleGroup; +class CToggleButton; +class CButton; +class CLabelGroup; + class InterfaceObjectConfigurable: public CIntObject { public: @@ -35,12 +42,29 @@ protected: return std::dynamic_pointer_cast<T>(iter->second); } +private: //field deserializers + //basic serializers + Point readPosition(const JsonNode &) const; + ETextAlignment readTextAlignment(const JsonNode &) const; + SDL_Color readColor(const JsonNode &) const; + EFonts readFont(const JsonNode &) const; + std::string readText(const JsonNode &) const; + std::pair<std::string, std::string> readHintText(const JsonNode &) const; + + //basic widgets + std::shared_ptr<CPicture> buildPicture(const JsonNode &) const; + std::shared_ptr<CLabel> buildLabel(const JsonNode &) const; + std::shared_ptr<CToggleGroup> buildToggleGroup(const JsonNode &) const; + std::shared_ptr<CToggleButton> buildToggleButton(const JsonNode &) const; + std::shared_ptr<CButton> buildButton(const JsonNode &) const; + std::shared_ptr<CLabelGroup> buildLabelGroup(const JsonNode &) const; + + + std::shared_ptr<CIntObject> buildWidget(const JsonNode & config) const; + + private: std::map<std::string, std::shared_ptr<CIntObject>> widgets; std::map<std::string, std::function<void(int)>> callbacks; - - std::shared_ptr<CIntObject> buildWidget(const JsonNode & config); - - std::string buildText(const JsonNode & param) const; }; diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 3667341cd..dca4cc604 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -100,9 +100,23 @@ RandomMapTab::RandomMapTab(): //new callbacks available only from mod addCallback("templateSelection", [&](int) { - GH.pushInt(std::make_shared<TemplatesDropBox>(this, int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()})); + GH.pushIntT<TemplatesDropBox>(this, int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()}); }); + addCallback("teamAlignments", [&](int) + { + GH.pushIntT<TeamAlignmentsWidget>(this); + }); + + for(auto road : VLC->terrainTypeHandler->roads()) + { + std::string cbRoadType = "selectRoad_" + road.fileName; + addCallback(cbRoadType, [&](bool on) + { + //TODO: support road types + }); + } + init(config); @@ -293,7 +307,7 @@ TemplatesDropBox::ListItem::ListItem(TemplatesDropBox * _dropBox, Point position dropBox(_dropBox) { OBJ_CONSTRUCTION; - labelName = std::make_shared<CLabel>(0, 0, FONT_SMALL, EAlignment::TOPLEFT, Colors::WHITE); + labelName = std::make_shared<CLabel>(0, 0, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE); labelName->setAutoRedraw(false); hoverImage = std::make_shared<CPicture>("List10Sl", 0, 0); @@ -352,6 +366,9 @@ TemplatesDropBox::TemplatesDropBox(RandomMapTab * randomMapTab, int3 size): curItems.insert(curItems.begin(), nullptr); //default template OBJ_CONSTRUCTION; + pos = randomMapTab->pos.topLeft(); + pos.w = randomMapTab->pos.w; + pos.h = randomMapTab->pos.h; background = std::make_shared<CPicture>("List10Bk", 158, 76); int positionsToShow = 10; @@ -362,7 +379,6 @@ TemplatesDropBox::TemplatesDropBox(RandomMapTab * randomMapTab, int3 size): slider = std::make_shared<CSlider>(Point(212 + 158, 76), 252, std::bind(&TemplatesDropBox::sliderMove, this, _1), positionsToShow, (int)curItems.size(), 0, false, CSlider::BLUE); updateListItems(); - pos = background->pos; } void TemplatesDropBox::sliderMove(int slidPos) @@ -410,3 +426,24 @@ void TemplatesDropBox::setTemplate(const CRmgTemplate * tmpl) assert(GH.topInt().get() == this); GH.popInt(GH.topInt()); } + +TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab * randomMapTab): + CIntObject(), + randomMapTab(randomMapTab) +{ + OBJ_CONSTRUCTION; + + pos.w = 300; + pos.h = 300; + background = std::make_shared<CFilledTexture>("Bl3DCvex", pos); + center(pos); + + buttonOk = std::make_shared<CButton>(Point(43, 240), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], [](){}); + buttonCancel = std::make_shared<CButton>(Point(193, 240), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [&]() + { + assert(GH.topInt().get() == this); + GH.popInt(GH.topInt()); + }, SDLK_ESCAPE); + + +} diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index d30cc16cd..df6530142 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -85,3 +85,18 @@ private: std::vector<const CRmgTemplate *> curItems; }; + +class TeamAlignmentsWidget: public CIntObject +{ +public: + TeamAlignmentsWidget(RandomMapTab * randomMapTab); + +private: + + RandomMapTab * randomMapTab; + + std::shared_ptr<CFilledTexture> background; + std::shared_ptr<CLabelGroup> labels; + std::shared_ptr<CButton> buttonOk, buttonCancel; + std::vector<std::shared_ptr<CToggleGroup>> teams; +}; From 1d4209d97ec09d4958a1588ed079f7acb076b60f Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Fri, 16 Dec 2022 00:57:10 +0400 Subject: [PATCH 15/35] Continue refactoring --- client/gui/InterfaceObjectConfigurable.cpp | 9 +++++++++ client/gui/InterfaceObjectConfigurable.h | 3 ++- client/lobby/RandomMapTab.cpp | 22 +++++++++++----------- client/lobby/RandomMapTab.h | 12 ++++++------ 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index ddce4753e..130cfec71 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -271,5 +271,14 @@ std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonN { return buildLabelGroup(config); } + if(type == "custom") + { + return buildCustomWidget(config); + } return std::shared_ptr<CIntObject>(nullptr); } + +std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildCustomWidget(const JsonNode & config) const +{ + return nullptr; +} diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h index 51f23d7be..cae184738 100644 --- a/client/gui/InterfaceObjectConfigurable.h +++ b/client/gui/InterfaceObjectConfigurable.h @@ -42,6 +42,8 @@ protected: return std::dynamic_pointer_cast<T>(iter->second); } + virtual std::shared_ptr<CIntObject> buildCustomWidget(const JsonNode & config) const; + private: //field deserializers //basic serializers Point readPosition(const JsonNode &) const; @@ -62,7 +64,6 @@ private: //field deserializers std::shared_ptr<CIntObject> buildWidget(const JsonNode & config) const; - private: std::map<std::string, std::shared_ptr<CIntObject>> widgets; diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index dca4cc604..37f6a8829 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -100,12 +100,12 @@ RandomMapTab::RandomMapTab(): //new callbacks available only from mod addCallback("templateSelection", [&](int) { - GH.pushIntT<TemplatesDropBox>(this, int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()}); + GH.pushIntT<TemplatesDropBox>(*this, int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()}); }); addCallback("teamAlignments", [&](int) { - GH.pushIntT<TeamAlignmentsWidget>(this); + GH.pushIntT<TeamAlignmentsWidget>(*this); }); for(auto road : VLC->terrainTypeHandler->roads()) @@ -302,7 +302,7 @@ std::vector<int> RandomMapTab::getPossibleMapSizes() return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE, CMapHeader::MAP_SIZE_HUGE, CMapHeader::MAP_SIZE_XHUGE, CMapHeader::MAP_SIZE_GIANT}; } -TemplatesDropBox::ListItem::ListItem(TemplatesDropBox * _dropBox, Point position) +TemplatesDropBox::ListItem::ListItem(TemplatesDropBox & _dropBox, Point position) : CIntObject(LCLICK | HOVER, position), dropBox(_dropBox) { @@ -352,12 +352,12 @@ void TemplatesDropBox::ListItem::clickLeft(tribool down, bool previousState) { if(down && hovered) { - dropBox->setTemplate(item); + dropBox.setTemplate(item); } } -TemplatesDropBox::TemplatesDropBox(RandomMapTab * randomMapTab, int3 size): +TemplatesDropBox::TemplatesDropBox(RandomMapTab & randomMapTab, int3 size): CIntObject(LCLICK | HOVER), randomMapTab(randomMapTab) { @@ -366,15 +366,15 @@ TemplatesDropBox::TemplatesDropBox(RandomMapTab * randomMapTab, int3 size): curItems.insert(curItems.begin(), nullptr); //default template OBJ_CONSTRUCTION; - pos = randomMapTab->pos.topLeft(); - pos.w = randomMapTab->pos.w; - pos.h = randomMapTab->pos.h; + pos = randomMapTab.pos.topLeft(); + pos.w = randomMapTab.pos.w; + pos.h = randomMapTab.pos.h; background = std::make_shared<CPicture>("List10Bk", 158, 76); int positionsToShow = 10; for(int i = 0; i < positionsToShow; i++) - listItems.push_back(std::make_shared<ListItem>(this, Point(158, 76 + i * 25))); + listItems.push_back(std::make_shared<ListItem>(*this, Point(158, 76 + i * 25))); slider = std::make_shared<CSlider>(Point(212 + 158, 76), 252, std::bind(&TemplatesDropBox::sliderMove, this, _1), positionsToShow, (int)curItems.size(), 0, false, CSlider::BLUE); @@ -422,12 +422,12 @@ void TemplatesDropBox::updateListItems() void TemplatesDropBox::setTemplate(const CRmgTemplate * tmpl) { - randomMapTab->setTemplate(tmpl); + randomMapTab.setTemplate(tmpl); assert(GH.topInt().get() == this); GH.popInt(GH.topInt()); } -TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab * randomMapTab): +TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): CIntObject(), randomMapTab(randomMapTab) { diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index df6530142..aee146e1f 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -55,10 +55,10 @@ class TemplatesDropBox : public CIntObject { std::shared_ptr<CLabel> labelName; std::shared_ptr<CPicture> hoverImage; - TemplatesDropBox * dropBox; + TemplatesDropBox & dropBox; const CRmgTemplate * item = nullptr; - ListItem(TemplatesDropBox *, Point position); + ListItem(TemplatesDropBox &, Point position); void updateItem(int index, const CRmgTemplate * item = nullptr); void hover(bool on) override; @@ -66,7 +66,7 @@ class TemplatesDropBox : public CIntObject }; public: - TemplatesDropBox(RandomMapTab * randomMapTab, int3 size); + TemplatesDropBox(RandomMapTab & randomMapTab, int3 size); void hover(bool on) override; void clickLeft(tribool down, bool previousState) override; @@ -77,7 +77,7 @@ private: void sliderMove(int slidPos); void updateListItems(); - RandomMapTab * randomMapTab; + RandomMapTab & randomMapTab; std::shared_ptr<CPicture> background; std::vector<std::shared_ptr<ListItem>> listItems; std::shared_ptr<CSlider> slider; @@ -89,11 +89,11 @@ private: class TeamAlignmentsWidget: public CIntObject { public: - TeamAlignmentsWidget(RandomMapTab * randomMapTab); + TeamAlignmentsWidget(RandomMapTab & randomMapTab); private: - RandomMapTab * randomMapTab; + RandomMapTab & randomMapTab; std::shared_ptr<CFilledTexture> background; std::shared_ptr<CLabelGroup> labels; From d3c3feb0375dc3475cc548dcea192db98bb3e275 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Fri, 16 Dec 2022 02:15:53 +0400 Subject: [PATCH 16/35] Configurable template selection --- client/gui/InterfaceObjectConfigurable.cpp | 39 ++++++-- client/gui/InterfaceObjectConfigurable.h | 13 ++- client/lobby/RandomMapTab.cpp | 104 +++++++++++++-------- client/lobby/RandomMapTab.h | 13 ++- client/widgets/Buttons.cpp | 9 +- client/widgets/Buttons.h | 5 +- 6 files changed, 119 insertions(+), 64 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 130cfec71..9714b9663 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -26,14 +26,14 @@ #include "../../lib/CGeneralTextHandler.h" -InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config): - CIntObject() +InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config, int used, Point offset): + CIntObject(used, offset) { init(config); } -InterfaceObjectConfigurable::InterfaceObjectConfigurable(): - CIntObject() +InterfaceObjectConfigurable::InterfaceObjectConfigurable(int used, Point offset): + CIntObject(used, offset) { } @@ -55,6 +55,12 @@ void InterfaceObjectConfigurable::init(const JsonNode &config) : item["name"].String(); widgets[name] = buildWidget(item); } + variables = config["variables"]; +} + +const JsonNode & InterfaceObjectConfigurable::variable(const std::string & name) const +{ + return variables[name]; } std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const @@ -153,7 +159,10 @@ std::shared_ptr<CPicture> InterfaceObjectConfigurable::buildPicture(const JsonNo { auto image = readText(config["image"]); auto position = readPosition(config["position"]); - return std::make_shared<CPicture>(image, position.x, position.y); + auto pic = std::make_shared<CPicture>(image, position.x, position.y); + if(!config["visible"].isNull()) + pic->visible = config["visible"].Bool(); + return pic; } std::shared_ptr<CLabel> InterfaceObjectConfigurable::buildLabel(const JsonNode & config) const @@ -243,6 +252,18 @@ std::shared_ptr<CLabelGroup> InterfaceObjectConfigurable::buildLabelGroup(const return group; } +std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode & config) const +{ + auto position = readPosition(config["position"]); + int length = config["size"].Integer(); + auto style = config["style"].String() == "brown" ? CSlider::BROWN : CSlider::BLUE; + auto itemsVisible = config["itemsVisible"].Integer(); + auto itemsTotal = config["itemsTotal"].Integer(); + auto value = config["selected"].Integer(); + bool horizontal = config["orientation"].String() == "horizontal"; + return std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style); +} + std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonNode & config) const { assert(!config.isNull()); @@ -271,14 +292,18 @@ std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonN { return buildLabelGroup(config); } + if(type == "slider") + { + return buildSlider(config); + } if(type == "custom") { - return buildCustomWidget(config); + return const_cast<InterfaceObjectConfigurable*>(this)->buildCustomWidget(config); } return std::shared_ptr<CIntObject>(nullptr); } -std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildCustomWidget(const JsonNode & config) const +std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildCustomWidget(const JsonNode & config) { return nullptr; } diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h index cae184738..980f8b817 100644 --- a/client/gui/InterfaceObjectConfigurable.h +++ b/client/gui/InterfaceObjectConfigurable.h @@ -20,12 +20,13 @@ class CToggleGroup; class CToggleButton; class CButton; class CLabelGroup; +class CSlider; class InterfaceObjectConfigurable: public CIntObject { public: - InterfaceObjectConfigurable(); - InterfaceObjectConfigurable(const JsonNode & config); + InterfaceObjectConfigurable(int used=0, Point offset=Point()); + InterfaceObjectConfigurable(const JsonNode & config, int used=0, Point offset=Point()); protected: //must be called after adding callbacks @@ -42,9 +43,8 @@ protected: return std::dynamic_pointer_cast<T>(iter->second); } - virtual std::shared_ptr<CIntObject> buildCustomWidget(const JsonNode & config) const; + const JsonNode & variable(const std::string & name) const; -private: //field deserializers //basic serializers Point readPosition(const JsonNode &) const; ETextAlignment readTextAlignment(const JsonNode &) const; @@ -60,12 +60,15 @@ private: //field deserializers std::shared_ptr<CToggleButton> buildToggleButton(const JsonNode &) const; std::shared_ptr<CButton> buildButton(const JsonNode &) const; std::shared_ptr<CLabelGroup> buildLabelGroup(const JsonNode &) const; + std::shared_ptr<CSlider> buildSlider(const JsonNode &) const; - + //composite widgets + virtual std::shared_ptr<CIntObject> buildCustomWidget(const JsonNode & config); std::shared_ptr<CIntObject> buildWidget(const JsonNode & config) const; private: std::map<std::string, std::shared_ptr<CIntObject>> widgets; std::map<std::string, std::function<void(int)>> callbacks; + JsonNode variables; }; diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 37f6a8829..a17c66c8d 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -302,48 +302,56 @@ std::vector<int> RandomMapTab::getPossibleMapSizes() return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE, CMapHeader::MAP_SIZE_HUGE, CMapHeader::MAP_SIZE_XHUGE, CMapHeader::MAP_SIZE_GIANT}; } -TemplatesDropBox::ListItem::ListItem(TemplatesDropBox & _dropBox, Point position) - : CIntObject(LCLICK | HOVER, position), +TemplatesDropBox::ListItem::ListItem(const JsonNode & config, TemplatesDropBox & _dropBox, Point position) + : InterfaceObjectConfigurable(LCLICK | HOVER, position), dropBox(_dropBox) { OBJ_CONSTRUCTION; - labelName = std::make_shared<CLabel>(0, 0, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE); - labelName->setAutoRedraw(false); - hoverImage = std::make_shared<CPicture>("List10Sl", 0, 0); - hoverImage->visible = false; + init(config); - pos.w = hoverImage->pos.w; - pos.h = hoverImage->pos.h; + if(auto w = widget<CPicture>("hoverImage")) + { + pos.w = w->pos.w; + pos.h = w->pos.h; + } type |= REDRAW_PARENT; } void TemplatesDropBox::ListItem::updateItem(int idx, const CRmgTemplate * _item) { - item = _item; - if(item) + if(auto w = widget<CLabel>("labelName")) { - labelName->setText(item->getName()); - } - else - { - if(idx) - labelName->setText(""); + item = _item; + if(item) + { + w->setText(item->getName()); + } else - labelName->setText("default"); + { + if(idx) + w->setText(""); + else + w->setText("default"); + } } } void TemplatesDropBox::ListItem::hover(bool on) { - if(labelName->getText().empty()) + auto h = widget<CPicture>("hoverImage"); + auto w = widget<CLabel>("labelName"); + if(h && w) { - hovered = false; - hoverImage->visible = false; - } - else - { - hoverImage->visible = on; + if(w->getText().empty()) + { + hovered = false; + h->visible = false; + } + else + { + h->visible = on; + } } redraw(); } @@ -358,32 +366,43 @@ void TemplatesDropBox::ListItem::clickLeft(tribool down, bool previousState) TemplatesDropBox::TemplatesDropBox(RandomMapTab & randomMapTab, int3 size): - CIntObject(LCLICK | HOVER), + InterfaceObjectConfigurable(LCLICK | HOVER), randomMapTab(randomMapTab) { curItems = VLC->tplh->getTemplates(); vstd::erase_if(curItems, [size](const CRmgTemplate * t){return !t->matchesSize(size);}); curItems.insert(curItems.begin(), nullptr); //default template + const JsonNode config(ResourceID("config/windows/randomMapTemplateWidget.json")); + + addCallback("sliderMove", std::bind(&TemplatesDropBox::sliderMove, this, std::placeholders::_1)); + OBJ_CONSTRUCTION; pos = randomMapTab.pos.topLeft(); pos.w = randomMapTab.pos.w; pos.h = randomMapTab.pos.h; - background = std::make_shared<CPicture>("List10Bk", 158, 76); - int positionsToShow = 10; + init(config); - for(int i = 0; i < positionsToShow; i++) - listItems.push_back(std::make_shared<ListItem>(*this, Point(158, 76 + i * 25))); - - slider = std::make_shared<CSlider>(Point(212 + 158, 76), 252, std::bind(&TemplatesDropBox::sliderMove, this, _1), positionsToShow, (int)curItems.size(), 0, false, CSlider::BLUE); + if(auto w = widget<CSlider>("slider")) + { + w->setAmount(curItems.size()); + } updateListItems(); } +std::shared_ptr<CIntObject> TemplatesDropBox::buildCustomWidget(const JsonNode & config) +{ + auto position = readPosition(config["position"]); + listItems.push_back(std::make_shared<ListItem>(config, *this, position)); + return listItems.back(); +} + void TemplatesDropBox::sliderMove(int slidPos) { - if(!slider) + auto w = widget<CSlider>("slider"); + if(!w) return; // ignore spurious call when slider is being created updateListItems(); redraw(); @@ -405,17 +424,20 @@ void TemplatesDropBox::clickLeft(tribool down, bool previousState) void TemplatesDropBox::updateListItems() { - int elemIdx = slider->getValue(); - for(auto item : listItems) + if(auto w = widget<CSlider>("slider")) { - if(elemIdx < curItems.size()) + int elemIdx = w->getValue(); + for(auto item : listItems) { - item->updateItem(elemIdx, curItems[elemIdx]); - elemIdx++; - } - else - { - item->updateItem(elemIdx); + if(elemIdx < curItems.size()) + { + item->updateItem(elemIdx, curItems[elemIdx]); + elemIdx++; + } + else + { + item->updateItem(elemIdx); + } } } } diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index aee146e1f..effe2d66f 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -49,16 +49,14 @@ private: std::set<int> playerCountAllowed, playerTeamsAllowed, compCountAllowed, compTeamsAllowed; }; -class TemplatesDropBox : public CIntObject +class TemplatesDropBox : public InterfaceObjectConfigurable { - struct ListItem : public CIntObject + struct ListItem : public InterfaceObjectConfigurable { - std::shared_ptr<CLabel> labelName; - std::shared_ptr<CPicture> hoverImage; TemplatesDropBox & dropBox; const CRmgTemplate * item = nullptr; - ListItem(TemplatesDropBox &, Point position); + ListItem(const JsonNode &, TemplatesDropBox &, Point position); void updateItem(int index, const CRmgTemplate * item = nullptr); void hover(bool on) override; @@ -72,15 +70,16 @@ public: void clickLeft(tribool down, bool previousState) override; void setTemplate(const CRmgTemplate *); +protected: + std::shared_ptr<CIntObject> buildCustomWidget(const JsonNode & config) override; + private: void sliderMove(int slidPos); void updateListItems(); RandomMapTab & randomMapTab; - std::shared_ptr<CPicture> background; std::vector<std::shared_ptr<ListItem>> listItems; - std::shared_ptr<CSlider> slider; std::vector<const CRmgTemplate *> curItems; diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index e180181ae..435a89396 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -566,16 +566,21 @@ void CSlider::setScrollStep(int to) scrollStep = to; } -int CSlider::getAmount() +int CSlider::getAmount() const { return amount; } -int CSlider::getValue() +int CSlider::getValue() const { return value; } +int CSlider::getCapacity() const +{ + return capacity; +} + void CSlider::moveLeft() { moveTo(value-1); diff --git a/client/widgets/Buttons.h b/client/widgets/Buttons.h index d370b515e..f967fa953 100644 --- a/client/widgets/Buttons.h +++ b/client/widgets/Buttons.h @@ -256,8 +256,9 @@ public: void setAmount(int to); /// Accessors - int getAmount(); - int getValue(); + int getAmount() const; + int getValue() const; + int getCapacity() const; void addCallback(std::function<void(int)> callback); From 0c41787ca563c839ff4de5f81ee826e6d7427fff Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Fri, 16 Dec 2022 02:17:10 +0400 Subject: [PATCH 17/35] Remove uncompleted code --- client/lobby/RandomMapTab.cpp | 24 ++---------------------- client/lobby/RandomMapTab.h | 15 --------------- 2 files changed, 2 insertions(+), 37 deletions(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index a17c66c8d..679675e2c 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -105,7 +105,8 @@ RandomMapTab::RandomMapTab(): addCallback("teamAlignments", [&](int) { - GH.pushIntT<TeamAlignmentsWidget>(*this); + //TODO: support team alignments + //GH.pushIntT<TeamAlignmentsWidget>(*this); }); for(auto road : VLC->terrainTypeHandler->roads()) @@ -448,24 +449,3 @@ void TemplatesDropBox::setTemplate(const CRmgTemplate * tmpl) assert(GH.topInt().get() == this); GH.popInt(GH.topInt()); } - -TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): - CIntObject(), - randomMapTab(randomMapTab) -{ - OBJ_CONSTRUCTION; - - pos.w = 300; - pos.h = 300; - background = std::make_shared<CFilledTexture>("Bl3DCvex", pos); - center(pos); - - buttonOk = std::make_shared<CButton>(Point(43, 240), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], [](){}); - buttonCancel = std::make_shared<CButton>(Point(193, 240), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [&]() - { - assert(GH.topInt().get() == this); - GH.popInt(GH.topInt()); - }, SDLK_ESCAPE); - - -} diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index effe2d66f..72eab43e3 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -84,18 +84,3 @@ private: std::vector<const CRmgTemplate *> curItems; }; - -class TeamAlignmentsWidget: public CIntObject -{ -public: - TeamAlignmentsWidget(RandomMapTab & randomMapTab); - -private: - - RandomMapTab & randomMapTab; - - std::shared_ptr<CFilledTexture> background; - std::shared_ptr<CLabelGroup> labels; - std::shared_ptr<CButton> buttonOk, buttonCancel; - std::vector<std::shared_ptr<CToggleGroup>> teams; -}; From 8f089b3302146d0285246a5857064e772caf0d4c Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Fri, 16 Dec 2022 02:19:23 +0400 Subject: [PATCH 18/35] Rename folder --- client/lobby/RandomMapTab.cpp | 4 ++-- config/{windows => widgets}/randomMapTab.json | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename config/{windows => widgets}/randomMapTab.json (100%) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 679675e2c..18c216f6d 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -36,7 +36,7 @@ RandomMapTab::RandomMapTab(): recActions = 0; mapGenOptions = std::make_shared<CMapGenOptions>(); - const JsonNode config(ResourceID("config/windows/randomMapTab.json")); + const JsonNode config(ResourceID("config/widgets/randomMapTab.json")); addCallback("toggleMapSize", [&](int btnId) { auto mapSizeVal = getPossibleMapSizes(); @@ -374,7 +374,7 @@ TemplatesDropBox::TemplatesDropBox(RandomMapTab & randomMapTab, int3 size): vstd::erase_if(curItems, [size](const CRmgTemplate * t){return !t->matchesSize(size);}); curItems.insert(curItems.begin(), nullptr); //default template - const JsonNode config(ResourceID("config/windows/randomMapTemplateWidget.json")); + const JsonNode config(ResourceID("config/widgets/randomMapTemplateWidget.json")); addCallback("sliderMove", std::bind(&TemplatesDropBox::sliderMove, this, std::placeholders::_1)); diff --git a/config/windows/randomMapTab.json b/config/widgets/randomMapTab.json similarity index 100% rename from config/windows/randomMapTab.json rename to config/widgets/randomMapTab.json From 1d59dfecef55d45e6901a0af4116989b28de14e7 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Fri, 16 Dec 2022 02:48:07 +0400 Subject: [PATCH 19/35] Support translations --- client/gui/InterfaceObjectConfigurable.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 9714b9663..7fa80aa0e 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -72,7 +72,18 @@ std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const { return CGI->generaltexth->allTexts[config.Integer()]; } - return config.String(); + + const std::string delimiter = "/"; + std::string s = config.String(); + JsonNode translated = CGI->generaltexth->localizedTexts; + for(size_t p = s.find(delimiter); p != std::string::npos; p = s.find(delimiter)) + { + translated = translated[s.substr(0, p)]; + s.erase(0, p + delimiter.length()); + } + if(s == config.String()) + return s; + return translated[s].String(); } Point InterfaceObjectConfigurable::readPosition(const JsonNode & config) const @@ -157,7 +168,7 @@ std::pair<std::string, std::string> InterfaceObjectConfigurable::readHintText(co std::shared_ptr<CPicture> InterfaceObjectConfigurable::buildPicture(const JsonNode & config) const { - auto image = readText(config["image"]); + auto image = config["image"].String(); auto position = readPosition(config["position"]); auto pic = std::make_shared<CPicture>(image, position.x, position.y); if(!config["visible"].isNull()) From 268b87052b476648da9344e95f42ea124ecbbefb Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Fri, 16 Dec 2022 02:49:52 +0400 Subject: [PATCH 20/35] Fix for hint --- client/gui/InterfaceObjectConfigurable.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 7fa80aa0e..b4d80edb6 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -154,8 +154,8 @@ std::pair<std::string, std::string> InterfaceObjectConfigurable::readHintText(co if(config.getType() == JsonNode::JsonType::DATA_STRUCT) { - result.first = config["hover"].String(); - result.second = config["help"].String(); + result.first = readText(config["hover"]); + result.second = readText(config["help"]); return result; } if(config.getType() == JsonNode::JsonType::DATA_STRING) From 4508487afa234601937880ce8bb43de57a739aad Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Sat, 17 Dec 2022 02:28:42 +0400 Subject: [PATCH 21/35] Fix cmake for macOS build --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7b251ba0..1f56b7f76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ option(ENABLE_GITVERSION "Enable Version.cpp with Git commit hash" ON) option(ENABLE_DEBUG_CONSOLE "Enable debug console for Windows builds" ON) option(ENABLE_MULTI_PROCESS_BUILDS "Enable /MP flag for MSVS solution" ON) option(ENABLE_SINGLE_APP_BUILD "Builds client and server as single executable" OFF) +option(COPY_CONFIG_ON_BUILD "Copies config folder into output directory at building phase" ON) # Used for Snap packages and also useful for debugging if(NOT APPLE_IOS) @@ -80,8 +81,8 @@ if(ENABLE_ERM AND NOT ENABLE_LUA) set(ENABLE_LUA ON) endif() -# We want to deploy assets into build directory for easier debugging without install -if(NOT APPLE_IOS AND NOT COPY_CONFIG_ON_BUILD) +# We don't want to deploy assets into build directory for iOS build +if(APPLE_IOS AND COPY_CONFIG_ON_BUILD) set(COPY_CONFIG_ON_BUILD OFF) endif() From f27a40dd3494ebe0f63906ef748fb68cce7acdd8 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Sat, 17 Dec 2022 03:52:40 +0400 Subject: [PATCH 22/35] Support roads and teams customization in engine --- lib/rmg/CMapGenOptions.cpp | 32 +++++++++++++++++++++++++++++++- lib/rmg/CMapGenOptions.h | 26 ++++++++++++++++++++++---- lib/rmg/CMapGenerator.cpp | 24 ++++++++++++++++++------ lib/rmg/RoadPlacer.cpp | 7 +++++++ lib/serializer/CSerializer.h | 2 +- 5 files changed, 79 insertions(+), 12 deletions(-) diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index c3ed14233..d653029f6 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -230,6 +230,26 @@ void CMapGenOptions::setMapTemplate(const std::string & name) setMapTemplate(VLC->tplh->getTemplate(name)); } +void CMapGenOptions::setRoadEnabled(const std::string & roadName, bool enable) +{ + if(enable) + disabledRoads.erase(roadName); + else + disabledRoads.insert(roadName); +} + +bool CMapGenOptions::isRoadEnabled(const std::string & roadName) const +{ + return !disabledRoads.count(roadName); +} + +void CMapGenOptions::setPlayerTeam(PlayerColor color, TeamID team) +{ + auto it = players.find(color); + if(it == players.end()) assert(0); + it->second.setTeam(team); +} + void CMapGenOptions::finalize(CRandomGenerator & rand) { logGlobal->info("RMG map: %dx%d, %s underground", getWidth(), getHeight(), getHasTwoLevels() ? "WITH" : "NO"); @@ -473,7 +493,7 @@ const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand return *RandomGeneratorUtil::nextItem(templates, rand); } -CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI) +CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI), team(TeamID::NO_TEAM) { } @@ -515,4 +535,14 @@ void CMapGenOptions::CPlayerSettings::setPlayerType(EPlayerType::EPlayerType val playerType = value; } +TeamID CMapGenOptions::CPlayerSettings::getTeam() const +{ + return team; +} + +void CMapGenOptions::CPlayerSettings::setTeam(TeamID value) +{ + team = value; +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/CMapGenOptions.h b/lib/rmg/CMapGenOptions.h index d8277518c..39d947d05 100644 --- a/lib/rmg/CMapGenOptions.h +++ b/lib/rmg/CMapGenOptions.h @@ -51,6 +51,10 @@ public: /// The default value is EPlayerType::AI. EPlayerType::EPlayerType getPlayerType() const; void setPlayerType(EPlayerType::EPlayerType value); + + /// Team id for this player. TeamID::NO_TEAM by default - team will be randomly assigned + TeamID getTeam() const; + void setTeam(TeamID value); /// Constant for a random town selection. static const si32 RANDOM_TOWN = -1; @@ -59,6 +63,7 @@ public: PlayerColor color; si32 startingTown; EPlayerType::EPlayerType playerType; + TeamID team; public: template <typename Handler> @@ -67,6 +72,8 @@ public: h & color; h & startingTown; h & playerType; + if(version >= 806) + h & team; } }; @@ -105,6 +112,9 @@ public: EMonsterStrength::EMonsterStrength getMonsterStrength() const; void setMonsterStrength(EMonsterStrength::EMonsterStrength value); + + bool isRoadEnabled(const std::string & roadName) const; + void setRoadEnabled(const std::string & roadName, bool enable); /// The first player colors belong to standard players and the last player colors belong to comp only players. /// All standard players are by default of type EPlayerType::AI. @@ -113,6 +123,8 @@ public: /// Sets a player type for a standard player. A standard player is the opposite of a computer only player. The /// values which can be chosen for the player type are EPlayerType::AI or EPlayerType::HUMAN. void setPlayerTypeForStandardPlayer(PlayerColor color, EPlayerType::EPlayerType playerType); + + void setPlayerTeam(PlayerColor color, TeamID team = TeamID::NO_TEAM); /// The random map template to generate the map with or empty/not set if the template should be chosen randomly. /// Default: Not set/random. @@ -147,6 +159,8 @@ private: EWaterContent::EWaterContent waterContent; EMonsterStrength::EMonsterStrength monsterStrength; std::map<PlayerColor, CPlayerSettings> players; + std::set<std::string> disabledRoads; + const CRmgTemplate * mapTemplate; public: @@ -168,11 +182,15 @@ public: { templateName = mapTemplate->getId(); } - //if(version > xxx) do not forget to bump version - h & templateName; - if(!h.saving) + if(version >= 806) { - setMapTemplate(templateName); + h & templateName; + if(!h.saving) + { + setMapTemplate(templateName); + } + + h & disabledRoads; } } }; diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index ffe2a691f..21e8ccc1a 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -76,6 +76,11 @@ void CMapGenerator::loadConfig() config.pandoraMultiplierSpells = randomMapJson["pandoras"]["valueMultiplierSpells"].Integer(); config.pandoraSpellSchool = randomMapJson["pandoras"]["valueSpellSchool"].Integer(); config.pandoraSpell60 = randomMapJson["pandoras"]["valueSpell60"].Integer(); + //override config with game options + if(!mapGenOptions.isRoadEnabled(config.secondaryRoadType)) + config.secondaryRoadType = ""; + if(!mapGenOptions.isRoadEnabled(config.defaultRoadType)) + config.defaultRoadType = config.secondaryRoadType; } const CMapGenerator::Config & CMapGenerator::getConfig() const @@ -238,14 +243,21 @@ void CMapGenerator::addPlayerInfo() player.canHumanPlay = true; } - if (teamNumbers[j].empty()) + if(pSettings.getTeam() != TeamID::NO_TEAM) { - logGlobal->error("Not enough places in team for %s player", ((j == CPUONLY) ? "CPU" : "CPU or human")); - assert (teamNumbers[j].size()); + player.team = pSettings.getTeam(); + } + else + { + if (teamNumbers[j].empty()) + { + logGlobal->error("Not enough places in team for %s player", ((j == CPUONLY) ? "CPU" : "CPU or human")); + assert (teamNumbers[j].size()); + } + auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand); + player.team = TeamID(*itTeam); + teamNumbers[j].erase(itTeam); } - auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand); - player.team = TeamID(*itTeam); - teamNumbers[j].erase(itTeam); map->map().players[pSettings.getColor().getNum()] = player; } diff --git a/lib/rmg/RoadPlacer.cpp b/lib/rmg/RoadPlacer.cpp index a435395af..ff108375b 100644 --- a/lib/rmg/RoadPlacer.cpp +++ b/lib/rmg/RoadPlacer.cpp @@ -22,6 +22,9 @@ VCMI_LIB_NAMESPACE_BEGIN void RoadPlacer::process() { + if(generator.getConfig().defaultRoadType.empty() && generator.getConfig().secondaryRoadType.empty()) + return; //do not generate roads at all + connectRoads(); } @@ -68,6 +71,10 @@ bool RoadPlacer::createRoad(const int3 & dst) void RoadPlacer::drawRoads(bool secondary) { + if((secondary && generator.getConfig().secondaryRoadType.empty()) + || (!secondary && generator.getConfig().defaultRoadType.empty())) + return; + zone.areaPossible().subtract(roads); zone.freePaths().unite(roads); map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector()); diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 9b0d14e47..bcfc6fed9 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN -const ui32 SERIALIZATION_VERSION = 805; +const ui32 SERIALIZATION_VERSION = 806; const ui32 MINIMAL_SERIALIZATION_VERSION = 805; const std::string SAVEGAME_MAGIC = "VCMISVG"; From 42281f51e8361910ec1e422740efa309e897dd8d Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Sat, 17 Dec 2022 03:53:26 +0400 Subject: [PATCH 23/35] Revert "Remove uncompleted code" This reverts commit 0c41787ca563c839ff4de5f81ee826e6d7427fff. --- client/lobby/RandomMapTab.cpp | 24 ++++++++++++++++++++++-- client/lobby/RandomMapTab.h | 15 +++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 18c216f6d..213628fa9 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -105,8 +105,7 @@ RandomMapTab::RandomMapTab(): addCallback("teamAlignments", [&](int) { - //TODO: support team alignments - //GH.pushIntT<TeamAlignmentsWidget>(*this); + GH.pushIntT<TeamAlignmentsWidget>(*this); }); for(auto road : VLC->terrainTypeHandler->roads()) @@ -449,3 +448,24 @@ void TemplatesDropBox::setTemplate(const CRmgTemplate * tmpl) assert(GH.topInt().get() == this); GH.popInt(GH.topInt()); } + +TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): + CIntObject(), + randomMapTab(randomMapTab) +{ + OBJ_CONSTRUCTION; + + pos.w = 300; + pos.h = 300; + background = std::make_shared<CFilledTexture>("Bl3DCvex", pos); + center(pos); + + buttonOk = std::make_shared<CButton>(Point(43, 240), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], [](){}); + buttonCancel = std::make_shared<CButton>(Point(193, 240), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [&]() + { + assert(GH.topInt().get() == this); + GH.popInt(GH.topInt()); + }, SDLK_ESCAPE); + + +} diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index 72eab43e3..effe2d66f 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -84,3 +84,18 @@ private: std::vector<const CRmgTemplate *> curItems; }; + +class TeamAlignmentsWidget: public CIntObject +{ +public: + TeamAlignmentsWidget(RandomMapTab & randomMapTab); + +private: + + RandomMapTab & randomMapTab; + + std::shared_ptr<CFilledTexture> background; + std::shared_ptr<CLabelGroup> labels; + std::shared_ptr<CButton> buttonOk, buttonCancel; + std::vector<std::shared_ptr<CToggleGroup>> teams; +}; From d93e844609c227db50ba2b9f2273499f3ea05ff9 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Sat, 17 Dec 2022 04:54:01 +0400 Subject: [PATCH 24/35] Use roads names instead of their filenames --- config/randomMap.json | 4 ++-- lib/Terrain.cpp | 6 ++++-- lib/Terrain.h | 11 ++++++++++- lib/rmg/RoadPlacer.cpp | 4 ++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/config/randomMap.json b/config/randomMap.json index 926214ea2..fa54f121f 100644 --- a/config/randomMap.json +++ b/config/randomMap.json @@ -16,8 +16,8 @@ "extraResourcesLimit" : 3 }, "minGuardStrength" : 2000, - "defaultRoadType" : "pc", //pd - dirt, pg - gravel, pc - cobblestone - "secondaryRoadType": "pd", + "defaultRoadType" : "cobblestoneRoad", + "secondaryRoadType": "dirtRoad", "treasureValueLimit" : 20000, //generate pandora with gold for treasure above this limit "prisons" : { diff --git a/lib/Terrain.cpp b/lib/Terrain.cpp index fff98de34..7b6635080 100644 --- a/lib/Terrain.cpp +++ b/lib/Terrain.cpp @@ -220,6 +220,7 @@ void TerrainTypeHandler::initRivers(const std::vector<std::string> & allConfigs) { RiverType info; + info.name = river.first; info.fileName = river.second["animation"].String(); info.code = river.second["code"].String(); info.deltaName = river.second["delta"].String(); @@ -255,6 +256,7 @@ void TerrainTypeHandler::initRoads(const std::vector<std::string> & allConfigs) { RoadType info; + info.name = road.first; info.fileName = road.second["animation"].String(); info.code = road.second["code"].String(); info.movementCost = static_cast<ui8>(road.second["moveCost"].Float()); @@ -295,7 +297,7 @@ void TerrainTypeHandler::recreateRiverMaps() { const auto * riverInfo = &riverTypes[i]; - riverInfoByName[riverInfo->fileName] = riverInfo; + riverInfoByName[riverInfo->name] = riverInfo; riverInfoByCode[riverInfo->code] = riverInfo; riverInfoById[riverInfo->id] = riverInfo; } @@ -307,7 +309,7 @@ void TerrainTypeHandler::recreateRoadMaps() { const auto * roadInfo = &roadTypes[i]; - roadInfoByName[roadInfo->fileName] = roadInfo; + roadInfoByName[roadInfo->name] = roadInfo; roadInfoByCode[roadInfo->code] = roadInfo; roadInfoById[roadInfo->id] = roadInfo; } diff --git a/lib/Terrain.h b/lib/Terrain.h index 6e23a9ca2..5d6fca1e7 100644 --- a/lib/Terrain.h +++ b/lib/Terrain.h @@ -89,7 +89,7 @@ public: class DLL_LINKAGE RiverType { public: - + std::string name; std::string fileName; std::string code; std::string deltaName; @@ -99,6 +99,10 @@ public: template <typename Handler> void serialize(Handler& h, const int version) { + if(version >= 806) + { + h & name; + } h & fileName; h & code; h & deltaName; @@ -109,6 +113,7 @@ public: class DLL_LINKAGE RoadType { public: + std::string name; std::string fileName; std::string code; RoadId id; @@ -118,6 +123,10 @@ public: template <typename Handler> void serialize(Handler& h, const int version) { + if(version >= 806) + { + h & name; + } h & fileName; h & code; h & id; diff --git a/lib/rmg/RoadPlacer.cpp b/lib/rmg/RoadPlacer.cpp index ff108375b..b8a037135 100644 --- a/lib/rmg/RoadPlacer.cpp +++ b/lib/rmg/RoadPlacer.cpp @@ -78,8 +78,8 @@ void RoadPlacer::drawRoads(bool secondary) zone.areaPossible().subtract(roads); zone.freePaths().unite(roads); map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector()); - std::string roadCode = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType); - RoadId roadType = VLC->terrainTypeHandler->getRoadByCode(roadCode)->id; + std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType); + RoadId roadType = VLC->terrainTypeHandler->getRoadByName(roadName)->id; map.getEditManager()->drawRoad(roadType, &generator.rand); } From 4f5fab702e628b22a10b199dda3c1ee74c25fc2c Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Sat, 17 Dec 2022 04:54:37 +0400 Subject: [PATCH 25/35] Support roads selection in random map tab --- client/lobby/RandomMapTab.cpp | 7 ++++--- config/translate.json | 13 +++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 213628fa9..755fe5ded 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -110,10 +110,11 @@ RandomMapTab::RandomMapTab(): for(auto road : VLC->terrainTypeHandler->roads()) { - std::string cbRoadType = "selectRoad_" + road.fileName; - addCallback(cbRoadType, [&](bool on) + std::string cbRoadType = "selectRoad_" + road.name; + addCallback(cbRoadType, [&, road](bool on) { - //TODO: support road types + mapGenOptions->setRoadEnabled(road.name, on); + updateMapInfoByHost(); }); } diff --git a/config/translate.json b/config/translate.json index 3352fedd3..5322a50fb 100644 --- a/config/translate.json +++ b/config/translate.json @@ -113,5 +113,18 @@ "label" : "Hide complete quests", "help" : "Hide all quests that already completed" } + }, + "randomMapTab": + { + "widgets": + { + "defaultTemplate": "default", + "templateLabel": "Template", + "teamAlignmentsButton": "Setup...", + "teamAlignmentsLabel": "Team alignments", + "dirtRoad": "Dirt", + "gravelRoad": "Gravel", + "cobblestoneRoad": "Stone" + } } } From b1f2c7aed400ed7a4aa6b57360f28f963c9d6fae Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Sat, 17 Dec 2022 08:19:16 +0400 Subject: [PATCH 26/35] Team alignments implemented --- client/gui/InterfaceObjectConfigurable.cpp | 62 ++++++++++++++++- client/gui/InterfaceObjectConfigurable.h | 8 +++ client/lobby/RandomMapTab.cpp | 79 ++++++++++++++++++++-- client/lobby/RandomMapTab.h | 5 +- client/widgets/Buttons.cpp | 5 ++ client/widgets/Buttons.h | 1 + client/widgets/Images.h | 2 +- config/translate.json | 5 +- lib/rmg/CMapGenOptions.cpp | 3 + lib/rmg/CMapGenerator.cpp | 5 +- 10 files changed, 161 insertions(+), 14 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index b4d80edb6..31b43c2af 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -55,7 +55,6 @@ void InterfaceObjectConfigurable::init(const JsonNode &config) : item["name"].String(); widgets[name] = buildWidget(item); } - variables = config["variables"]; } const JsonNode & InterfaceObjectConfigurable::variable(const std::string & name) const @@ -94,6 +93,16 @@ Point InterfaceObjectConfigurable::readPosition(const JsonNode & config) const return p; } +Rect InterfaceObjectConfigurable::readRect(const JsonNode & config) const +{ + Rect p; + p.x = config["x"].Integer(); + p.y = config["y"].Integer(); + p.w = config["w"].Integer(); + p.h = config["h"].Integer(); + return p; +} + ETextAlignment InterfaceObjectConfigurable::readTextAlignment(const JsonNode & config) const { if(!config.isNull()) @@ -275,6 +284,45 @@ std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode return std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style); } +std::shared_ptr<CAnimImage> InterfaceObjectConfigurable::buildImage(const JsonNode & config) const +{ + auto position = readPosition(config["position"]); + auto image = config["image"].String(); + int group = config["group"].isNull() ? 0 : config["group"].Integer(); + int frame = config["frame"].isNull() ? 0 : config["frame"].Integer(); + return std::make_shared<CAnimImage>(image, frame, group, position.x, position.y); +} + +std::shared_ptr<CFilledTexture> InterfaceObjectConfigurable::buildTexture(const JsonNode & config) const +{ + auto image = config["image"].String(); + auto rect = readRect(config); + return std::make_shared<CFilledTexture>(image, rect); +} + +std::shared_ptr<CShowableAnim> InterfaceObjectConfigurable::buildAnimation(const JsonNode & config) const +{ + auto position = readPosition(config["position"]); + auto image = config["image"].String(); + ui8 flags = 0; + if(!config["repeat"].Bool()) + flags |= CShowableAnim::EFlags::PLAY_ONCE; + + int group = config["group"].isNull() ? 0 : config["group"].Integer(); + auto anim = std::make_shared<CShowableAnim>(position.x, position.y, image, flags, 4, group); + if(!config["alpha"].isNull()) + anim->setAlpha(config["alpha"].Integer()); + if(!config["callback"].isNull()) + anim->callback = std::bind(callbacks.at(config["callback"].String()), 0); + if(!config["frames"].isNull()) + { + auto b = config["frames"]["start"].Integer(); + auto e = config["frames"]["end"].Integer(); + anim->set(group, b, e); + } + return anim; +} + std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonNode & config) const { assert(!config.isNull()); @@ -283,6 +331,18 @@ std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonN { return buildPicture(config); } + if(type == "image") + { + return buildImage(config); + } + if(type == "texture") + { + return buildTexture(config); + } + if(type == "animation") + { + return buildAnimation(config); + } if(type == "label") { return buildLabel(config); diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h index 980f8b817..4bb40e974 100644 --- a/client/gui/InterfaceObjectConfigurable.h +++ b/client/gui/InterfaceObjectConfigurable.h @@ -21,6 +21,9 @@ class CToggleButton; class CButton; class CLabelGroup; class CSlider; +class CAnimImage; +class CShowableAnim; +class CFilledTexture; class InterfaceObjectConfigurable: public CIntObject { @@ -47,6 +50,7 @@ protected: //basic serializers Point readPosition(const JsonNode &) const; + Rect readRect(const JsonNode &) const; ETextAlignment readTextAlignment(const JsonNode &) const; SDL_Color readColor(const JsonNode &) const; EFonts readFont(const JsonNode &) const; @@ -61,6 +65,10 @@ protected: std::shared_ptr<CButton> buildButton(const JsonNode &) const; std::shared_ptr<CLabelGroup> buildLabelGroup(const JsonNode &) const; std::shared_ptr<CSlider> buildSlider(const JsonNode &) const; + std::shared_ptr<CAnimImage> buildImage(const JsonNode &) const; + std::shared_ptr<CShowableAnim> buildAnimation(const JsonNode &) const; + std::shared_ptr<CFilledTexture> buildTexture(const JsonNode &) const; + //composite widgets virtual std::shared_ptr<CIntObject> buildCustomWidget(const JsonNode & config); diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 755fe5ded..c931d6f72 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -155,6 +155,7 @@ void RandomMapTab::updateMapInfoByHost() mapInfo->mapHeader->howManyTeams = playersToGen; + std::set<TeamID> occupiedTeams; for(int i = 0; i < playersToGen; ++i) { PlayerInfo player; @@ -168,11 +169,25 @@ void RandomMapTab::updateMapInfoByHost() { player.canHumanPlay = true; } - player.team = TeamID(i); + auto team = mapGenOptions->getPlayersSettings().at(PlayerColor(i)).getTeam(); + player.team = team; + occupiedTeams.insert(team); player.hasMainTown = true; player.generateHeroAtMainTown = true; mapInfo->mapHeader->players.push_back(player); } + for(auto & player : mapInfo->mapHeader->players) + { + for(int i = 0; player.team == TeamID::NO_TEAM; ++i) + { + TeamID team(i); + if(!occupiedTeams.count(team)) + { + player.team = team; + occupiedTeams.insert(team); + } + } + } mapInfoChanged(mapInfo, mapGenOptions); } @@ -456,13 +471,67 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): { OBJ_CONSTRUCTION; - pos.w = 300; - pos.h = 300; + int humanPlayers = randomMapTab.obtainMapGenOptions().getPlayerCount(); + int cpuPlayers = randomMapTab.obtainMapGenOptions().getCompOnlyPlayerCount(); + int totalPlayers = humanPlayers == CMapGenOptions::RANDOM_SIZE || cpuPlayers == CMapGenOptions::RANDOM_SIZE + ? PlayerColor::PLAYER_LIMIT_I : humanPlayers + cpuPlayers; + assert(totalPlayers <= PlayerColor::PLAYER_LIMIT_I); + auto settings = randomMapTab.obtainMapGenOptions().getPlayersSettings(); + + pos.w = 80 + totalPlayers * 32; + pos.h = 80 + totalPlayers * 32; background = std::make_shared<CFilledTexture>("Bl3DCvex", pos); center(pos); - buttonOk = std::make_shared<CButton>(Point(43, 240), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], [](){}); - buttonCancel = std::make_shared<CButton>(Point(193, 240), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [&]() + for(int plId = 0; plId < totalPlayers; ++plId) + { + players.push_back(std::make_shared<CToggleGroup>([&, totalPlayers, plId](int sel) + { + SObjectConstruction obj__i(players[plId].get()); + for(int teamId = 0; teamId < totalPlayers; ++teamId) + { + auto button = std::dynamic_pointer_cast<CToggleButton>(players[plId]->buttons[teamId]); + assert(button); + if(sel == teamId) + { + button->addOverlay(std::make_shared<CAnimImage>("ITGFLAGS", plId, 0, 8, 8)); + } + else + { + button->addOverlay(std::make_shared<CPicture>("TeamPlSl")); + } + } + })); + + SObjectConstruction obj__i(players.back().get()); + for(int teamId = 0; teamId < totalPlayers; ++teamId) + { + Point p(40 + plId * 32, 20 + teamId * 32); + placeholders.push_back(std::make_shared<CPicture>("TeamPlSl", p.x, p.y)); + auto button = std::make_shared<CToggleButton>(p, "TeamPlSl", std::pair<std::string, std::string>{"", ""}); + button->pos.w = 32; + button->pos.h = 32; + players.back()->addToggle(teamId, button); + } + + auto team = settings.at(PlayerColor(plId)).getTeam(); + if(team == TeamID::NO_TEAM) + players.back()->setSelected(plId); + else + players.back()->setSelected(team.getNum()); + } + + buttonOk = std::make_shared<CButton>(Point(40, 40 + totalPlayers * 32), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], [&]() + { + for(int plId = 0; plId < players.size(); ++plId) + { + randomMapTab.obtainMapGenOptions().setPlayerTeam(PlayerColor(plId), TeamID(players[plId]->getSelected())); + } + randomMapTab.updateMapInfoByHost(); + assert(GH.topInt().get() == this); + GH.popInt(GH.topInt()); + }); + buttonCancel = std::make_shared<CButton>(Point(120, 40 + totalPlayers * 32), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [&]() { assert(GH.topInt().get() == this); GH.popInt(GH.topInt()); diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index effe2d66f..6b307169b 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -26,6 +26,7 @@ class CToggleButton; class CLabel; class CLabelGroup; class CSlider; +class CPicture; class RandomMapTab : public InterfaceObjectConfigurable { @@ -35,6 +36,7 @@ public: void updateMapInfoByHost(); void setMapGenOptions(std::shared_ptr<CMapGenOptions> opts); void setTemplate(const CRmgTemplate *); + CMapGenOptions & obtainMapGenOptions() {return *mapGenOptions;} CFunctionList<void(std::shared_ptr<CMapInfo>, std::shared_ptr<CMapGenOptions>)> mapInfoChanged; @@ -97,5 +99,6 @@ private: std::shared_ptr<CFilledTexture> background; std::shared_ptr<CLabelGroup> labels; std::shared_ptr<CButton> buttonOk, buttonCancel; - std::vector<std::shared_ptr<CToggleGroup>> teams; + std::vector<std::shared_ptr<CToggleGroup>> players; + std::vector<std::shared_ptr<CPicture>> placeholders; }; diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index 435a89396..592eecdc0 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -449,6 +449,11 @@ void CToggleGroup::selectionChanged(int to) parent->redraw(); } +int CToggleGroup::getSelected() const +{ + return selectedID; +} + CVolumeSlider::CVolumeSlider(const Point & position, const std::string & defName, const int value, const std::pair<std::string, std::string> * const help) : CIntObject(LCLICK | RCLICK | WHEEL), value(value), diff --git a/client/widgets/Buttons.h b/client/widgets/Buttons.h index f967fa953..872cc448e 100644 --- a/client/widgets/Buttons.h +++ b/client/widgets/Buttons.h @@ -185,6 +185,7 @@ public: /// in some cases, e.g. LoadGame difficulty selection, after refreshing the UI, the ToggleGroup should /// reset all of it's child buttons to BLOCK state, then make selection again void setSelectedOnly(int id); + int getSelected() const; }; /// A typical slider for volume with an animated indicator diff --git a/client/widgets/Images.h b/client/widgets/Images.h index 0ac142125..84f069f38 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -56,7 +56,7 @@ public: }; /// area filled with specific texture -class CFilledTexture : CIntObject +class CFilledTexture : public CIntObject { SDL_Surface * texture; diff --git a/config/translate.json b/config/translate.json index 5322a50fb..35d36f18d 100644 --- a/config/translate.json +++ b/config/translate.json @@ -121,10 +121,7 @@ "defaultTemplate": "default", "templateLabel": "Template", "teamAlignmentsButton": "Setup...", - "teamAlignmentsLabel": "Team alignments", - "dirtRoad": "Dirt", - "gravelRoad": "Gravel", - "cobblestoneRoad": "Stone" + "teamAlignmentsLabel": "Team alignments" } } } diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index d653029f6..098511bfe 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -136,12 +136,14 @@ void CMapGenOptions::resetPlayersMap() { std::map<PlayerColor, TFaction> rememberTownTypes; + std::map<PlayerColor, TeamID> rememberTeam; for (auto p : players) { auto town = p.second.getStartingTown(); if (town != RANDOM_SIZE) rememberTownTypes[p.first] = town; + rememberTeam[p.first] = p.second.getTeam(); } @@ -169,6 +171,7 @@ void CMapGenOptions::resetPlayersMap() playerType = EPlayerType::COMP_ONLY; } player.setPlayerType(playerType); + player.setTeam(rememberTeam[pc]); players[pc] = player; if (vstd::contains(rememberTownTypes, pc)) diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 21e8ccc1a..9a4b1f916 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -188,6 +188,7 @@ void CMapGenerator::addPlayerInfo() enum ETeams {CPHUMAN = 0, CPUONLY = 1, AFTER_LAST = 2}; std::array<std::list<int>, 2> teamNumbers; + std::set<int> teamsTotal; int teamOffset = 0; int playerCount = 0; @@ -258,11 +259,11 @@ void CMapGenerator::addPlayerInfo() player.team = TeamID(*itTeam); teamNumbers[j].erase(itTeam); } + teamsTotal.insert(player.team.getNum()); map->map().players[pSettings.getColor().getNum()] = player; } - map->map().howManyTeams = (mapGenOptions.getTeamCount() == 0 ? mapGenOptions.getPlayerCount() : mapGenOptions.getTeamCount()) - + (mapGenOptions.getCompOnlyTeamCount() == 0 ? mapGenOptions.getCompOnlyPlayerCount() : mapGenOptions.getCompOnlyTeamCount()); + map->map().howManyTeams = teamsTotal.size(); } void CMapGenerator::genZones() From b590e4a0e4b9572622c342797820295d2208e546 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Sat, 17 Dec 2022 08:38:33 +0400 Subject: [PATCH 27/35] Fixes for multiplyer --- client/lobby/RandomMapTab.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index c931d6f72..a2270e5b3 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -276,6 +276,20 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) } if(auto w = widget<CToggleGroup>("groupMonsterStrength")) w->setSelected(opts->getMonsterStrength()); + if(auto w = widget<CButton>("templateButton")) + { + if(tmpl) + w->addTextOverlay(tmpl->getName(), EFonts::FONT_SMALL); + else + w->addTextOverlay("default", EFonts::FONT_SMALL); + } + for(auto r : VLC->terrainTypeHandler->roads()) + { + if(auto w = widget<CToggleButton>(r.name)) + { + w->setSelected(opts->isRoadEnabled(r.name)); + } + } } void RandomMapTab::setTemplate(const CRmgTemplate * tmpl) From 9b76a8000fa14e0f75974a91792c045d66b0a8d1 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Sat, 17 Dec 2022 20:38:16 +0400 Subject: [PATCH 28/35] Making team alignments widget customizable --- client/gui/InterfaceObjectConfigurable.cpp | 22 +++-- client/gui/InterfaceObjectConfigurable.h | 8 +- client/lobby/RandomMapTab.cpp | 110 ++++++++++++--------- client/lobby/RandomMapTab.h | 4 +- 4 files changed, 82 insertions(+), 62 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 31b43c2af..f6a2f141c 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -45,9 +45,14 @@ void InterfaceObjectConfigurable::addCallback(const std::string & callbackName, void InterfaceObjectConfigurable::init(const JsonNode &config) { OBJ_CONSTRUCTION; + + for(auto & item : config["variables"].Struct()) + { + variables[item.first] = item.second; + } + int unnamedObjectId = 0; const std::string unnamedObjectPrefix = "__widget_"; - for(const auto & item : config["items"].Vector()) { std::string name = item["name"].isNull() @@ -57,11 +62,6 @@ void InterfaceObjectConfigurable::init(const JsonNode &config) } } -const JsonNode & InterfaceObjectConfigurable::variable(const std::string & name) const -{ - return variables[name]; -} - std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const { if(config.isNull()) @@ -296,7 +296,7 @@ std::shared_ptr<CAnimImage> InterfaceObjectConfigurable::buildImage(const JsonNo std::shared_ptr<CFilledTexture> InterfaceObjectConfigurable::buildTexture(const JsonNode & config) const { auto image = config["image"].String(); - auto rect = readRect(config); + auto rect = readRect(config["rect"]); return std::make_shared<CFilledTexture>(image, rect); } @@ -323,9 +323,15 @@ std::shared_ptr<CShowableAnim> InterfaceObjectConfigurable::buildAnimation(const return anim; } -std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(const JsonNode & config) const +std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(JsonNode config) const { assert(!config.isNull()); + //overrides from variables + for(auto & item : config["overrides"].Struct()) + { + config[item.first] = variables[item.second.String()]; + } + auto type = config["type"].String(); if(type == "picture") { diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h index 4bb40e974..50ba9f30c 100644 --- a/client/gui/InterfaceObjectConfigurable.h +++ b/client/gui/InterfaceObjectConfigurable.h @@ -36,6 +36,7 @@ protected: void init(const JsonNode & config); void addCallback(const std::string & callbackName, std::function<void(int)> callback); + JsonNode variables; template<class T> const std::shared_ptr<T> widget(const std::string & name) const @@ -45,9 +46,7 @@ protected: return nullptr; return std::dynamic_pointer_cast<T>(iter->second); } - - const JsonNode & variable(const std::string & name) const; - + //basic serializers Point readPosition(const JsonNode &) const; Rect readRect(const JsonNode &) const; @@ -72,11 +71,10 @@ protected: //composite widgets virtual std::shared_ptr<CIntObject> buildCustomWidget(const JsonNode & config); - std::shared_ptr<CIntObject> buildWidget(const JsonNode & config) const; + std::shared_ptr<CIntObject> buildWidget(JsonNode config) const; private: std::map<std::string, std::shared_ptr<CIntObject>> widgets; std::map<std::string, std::function<void(int)>> callbacks; - JsonNode variables; }; diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index a2270e5b3..de7e29006 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -480,10 +480,11 @@ void TemplatesDropBox::setTemplate(const CRmgTemplate * tmpl) } TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): - CIntObject(), + InterfaceObjectConfigurable(), randomMapTab(randomMapTab) { - OBJ_CONSTRUCTION; + const JsonNode config(ResourceID("config/widgets/randomMapTeamsWidget.json")); + variables = config["variables"]; int humanPlayers = randomMapTab.obtainMapGenOptions().getPlayerCount(); int cpuPlayers = randomMapTab.obtainMapGenOptions().getCompOnlyPlayerCount(); @@ -491,51 +492,20 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): ? PlayerColor::PLAYER_LIMIT_I : humanPlayers + cpuPlayers; assert(totalPlayers <= PlayerColor::PLAYER_LIMIT_I); auto settings = randomMapTab.obtainMapGenOptions().getPlayersSettings(); + variables["totalPlayers"].Integer() = totalPlayers; - pos.w = 80 + totalPlayers * 32; - pos.h = 80 + totalPlayers * 32; - background = std::make_shared<CFilledTexture>("Bl3DCvex", pos); - center(pos); + pos.w = variables["windowSize"]["x"].Integer() + totalPlayers * variables["cellMargin"]["x"].Integer(); + pos.h = variables["windowSize"]["y"].Integer() + totalPlayers * variables["cellMargin"]["y"].Integer(); + variables["backgroundRect"]["x"].Integer() = pos.x; + variables["backgroundRect"]["y"].Integer() = pos.y; + variables["backgroundRect"]["w"].Integer() = pos.w; + variables["backgroundRect"]["h"].Integer() = pos.h; + variables["okButtonPosition"]["x"].Integer() = variables["buttonsOffset"]["ok"]["x"].Integer(); + variables["okButtonPosition"]["y"].Integer() = variables["buttonsOffset"]["ok"]["y"].Integer() + totalPlayers * variables["cellMargin"]["y"].Integer(); + variables["cancelButtonPosition"]["x"].Integer() = variables["buttonsOffset"]["cancel"]["x"].Integer(); + variables["cancelButtonPosition"]["y"].Integer() = variables["buttonsOffset"]["cancel"]["y"].Integer() + totalPlayers * variables["cellMargin"]["y"].Integer(); - for(int plId = 0; plId < totalPlayers; ++plId) - { - players.push_back(std::make_shared<CToggleGroup>([&, totalPlayers, plId](int sel) - { - SObjectConstruction obj__i(players[plId].get()); - for(int teamId = 0; teamId < totalPlayers; ++teamId) - { - auto button = std::dynamic_pointer_cast<CToggleButton>(players[plId]->buttons[teamId]); - assert(button); - if(sel == teamId) - { - button->addOverlay(std::make_shared<CAnimImage>("ITGFLAGS", plId, 0, 8, 8)); - } - else - { - button->addOverlay(std::make_shared<CPicture>("TeamPlSl")); - } - } - })); - - SObjectConstruction obj__i(players.back().get()); - for(int teamId = 0; teamId < totalPlayers; ++teamId) - { - Point p(40 + plId * 32, 20 + teamId * 32); - placeholders.push_back(std::make_shared<CPicture>("TeamPlSl", p.x, p.y)); - auto button = std::make_shared<CToggleButton>(p, "TeamPlSl", std::pair<std::string, std::string>{"", ""}); - button->pos.w = 32; - button->pos.h = 32; - players.back()->addToggle(teamId, button); - } - - auto team = settings.at(PlayerColor(plId)).getTeam(); - if(team == TeamID::NO_TEAM) - players.back()->setSelected(plId); - else - players.back()->setSelected(team.getNum()); - } - - buttonOk = std::make_shared<CButton>(Point(40, 40 + totalPlayers * 32), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], [&]() + addCallback("ok", [&](int) { for(int plId = 0; plId < players.size(); ++plId) { @@ -545,11 +515,57 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): assert(GH.topInt().get() == this); GH.popInt(GH.topInt()); }); - buttonCancel = std::make_shared<CButton>(Point(120, 40 + totalPlayers * 32), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [&]() + + addCallback("cancel", [&](int) { assert(GH.topInt().get() == this); GH.popInt(GH.topInt()); - }, SDLK_ESCAPE); + }); + init(config); + center(pos); + + OBJ_CONSTRUCTION; + + for(int plId = 0; plId < totalPlayers; ++plId) + { + players.push_back(std::make_shared<CToggleGroup>([&, totalPlayers, plId](int sel) + { + variables["player_id"].Integer() = plId; + SObjectConstruction obj__i(players[plId].get()); + for(int teamId = 0; teamId < totalPlayers; ++teamId) + { + auto button = std::dynamic_pointer_cast<CToggleButton>(players[plId]->buttons[teamId]); + assert(button); + if(sel == teamId) + { + button->addOverlay(buildWidget(variables["flagsAnimation"])); + } + else + { + button->addOverlay(buildWidget(variables["unchecked"])); + } + } + })); + + SObjectConstruction obj__i(players.back().get()); + for(int teamId = 0; teamId < totalPlayers; ++teamId) + { + variables["point"]["x"].Integer() = variables["cellOffset"]["x"].Integer() + plId * variables["cellMargin"]["x"].Integer(); + variables["point"]["y"].Integer() = variables["cellOffset"]["y"].Integer() + teamId * variables["cellMargin"]["y"].Integer(); + //Point p(40 + plId * 32, 20 + teamId * 32); + placeholders.push_back(buildWidget(variables["placeholder"])); + auto button = buildWidget(variables["button"]); + button->pos.w = variables["cellMargin"]["x"].Integer(); + button->pos.h = variables["cellMargin"]["y"].Integer(); + players.back()->addToggle(teamId, std::dynamic_pointer_cast<CToggleBase>(button)); + } + + auto team = settings.at(PlayerColor(plId)).getTeam(); + if(team == TeamID::NO_TEAM) + players.back()->setSelected(plId); + else + players.back()->setSelected(team.getNum()); + } } diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index 6b307169b..8909f5f1a 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -87,7 +87,7 @@ private: }; -class TeamAlignmentsWidget: public CIntObject +class TeamAlignmentsWidget: public InterfaceObjectConfigurable { public: TeamAlignmentsWidget(RandomMapTab & randomMapTab); @@ -100,5 +100,5 @@ private: std::shared_ptr<CLabelGroup> labels; std::shared_ptr<CButton> buttonOk, buttonCancel; std::vector<std::shared_ptr<CToggleGroup>> players; - std::vector<std::shared_ptr<CPicture>> placeholders; + std::vector<std::shared_ptr<CIntObject>> placeholders; }; From d7b0770b7162fec4a33c7c0b1dd63fff7ecbbe59 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Sat, 17 Dec 2022 20:50:33 +0400 Subject: [PATCH 29/35] Remove excessive code --- client/lobby/RandomMapTab.cpp | 6 +----- client/widgets/Buttons.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index de7e29006..9bf461bcb 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -544,7 +544,7 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): } else { - button->addOverlay(buildWidget(variables["unchecked"])); + button->addOverlay(nullptr); } } })); @@ -554,11 +554,7 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): { variables["point"]["x"].Integer() = variables["cellOffset"]["x"].Integer() + plId * variables["cellMargin"]["x"].Integer(); variables["point"]["y"].Integer() = variables["cellOffset"]["y"].Integer() + teamId * variables["cellMargin"]["y"].Integer(); - //Point p(40 + plId * 32, 20 + teamId * 32); - placeholders.push_back(buildWidget(variables["placeholder"])); auto button = buildWidget(variables["button"]); - button->pos.w = variables["cellMargin"]["x"].Integer(); - button->pos.h = variables["cellMargin"]["y"].Integer(); players.back()->addToggle(teamId, std::dynamic_pointer_cast<CToggleBase>(button)); } diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index 592eecdc0..98bb638d7 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -84,8 +84,11 @@ void CButton::addTextOverlay(const std::string & Text, EFonts font, SDL_Color co void CButton::addOverlay(std::shared_ptr<CIntObject> newOverlay) { overlay = newOverlay; - addChild(newOverlay.get()); - overlay->moveTo(overlay->pos.centerIn(pos).topLeft()); + if(overlay) + { + addChild(newOverlay.get()); + overlay->moveTo(overlay->pos.centerIn(pos).topLeft()); + } update(); } From 521328addd1f02955e442f6a01b967611a22f0ed Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Thu, 22 Dec 2022 00:54:06 +0400 Subject: [PATCH 30/35] Adding macros for targeted object binding --- client/gui/CGuiHandler.h | 1 + client/gui/InterfaceObjectConfigurable.cpp | 4 ++-- client/lobby/RandomMapTab.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/client/gui/CGuiHandler.h b/client/gui/CGuiHandler.h index 86526d5d6..0c4df679d 100644 --- a/client/gui/CGuiHandler.h +++ b/client/gui/CGuiHandler.h @@ -165,6 +165,7 @@ struct SSetCaptureState }; #define OBJ_CONSTRUCTION SObjectConstruction obj__i(this) +#define OBJ_CONSTRUCTION_TARGETED(obj) SObjectConstruction obj__i(obj) #define OBJECT_CONSTRUCTION_CAPTURING(actions) defActions = actions; SSetCaptureState obj__i1(true, actions); SObjectConstruction obj__i(this) #define OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(actions) SSetCaptureState obj__i1(true, actions); SObjectConstruction obj__i(this) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index f6a2f141c..92c0ad701 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -27,7 +27,7 @@ InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config, int used, Point offset): - CIntObject(used, offset) + InterfaceObjectConfigurable(used, offset) { init(config); } @@ -202,7 +202,7 @@ std::shared_ptr<CToggleGroup> InterfaceObjectConfigurable::buildToggleGroup(cons group->pos += position; if(!config["items"].isNull()) { - SObjectConstruction obj__i(group.get()); + OBJ_CONSTRUCTION_TARGETED(group.get()); int itemIdx = -1; for(const auto & item : config["items"].Vector()) { diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 9bf461bcb..9b5cd68da 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -533,7 +533,7 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): players.push_back(std::make_shared<CToggleGroup>([&, totalPlayers, plId](int sel) { variables["player_id"].Integer() = plId; - SObjectConstruction obj__i(players[plId].get()); + OBJ_CONSTRUCTION_TARGETED(players[plId].get()); for(int teamId = 0; teamId < totalPlayers; ++teamId) { auto button = std::dynamic_pointer_cast<CToggleButton>(players[plId]->buttons[teamId]); @@ -549,7 +549,7 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab): } })); - SObjectConstruction obj__i(players.back().get()); + OBJ_CONSTRUCTION_TARGETED(players.back().get()); for(int teamId = 0; teamId < totalPlayers; ++teamId) { variables["point"]["x"].Integer() = variables["cellOffset"]["x"].Integer() + plId * variables["cellMargin"]["x"].Integer(); From c7f430f051e6152e9c5c5ee43b2e94dc3da3b109 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Thu, 22 Dec 2022 01:13:32 +0400 Subject: [PATCH 31/35] Added debug lines --- client/gui/InterfaceObjectConfigurable.cpp | 37 +++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 92c0ad701..b66370f26 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -45,9 +45,10 @@ void InterfaceObjectConfigurable::addCallback(const std::string & callbackName, void InterfaceObjectConfigurable::init(const JsonNode &config) { OBJ_CONSTRUCTION; - + logGlobal->info("Building configurable interface object"); for(auto & item : config["variables"].Struct()) { + logGlobal->debug("Read variable named %s", item.first); variables[item.first] = item.second; } @@ -58,6 +59,7 @@ void InterfaceObjectConfigurable::init(const JsonNode &config) std::string name = item["name"].isNull() ? unnamedObjectPrefix + std::to_string(unnamedObjectId++) : item["name"].String(); + logGlobal->debug("Building widget with name %s", name); widgets[name] = buildWidget(item); } } @@ -69,11 +71,13 @@ std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const if(config.isNumber()) { + logGlobal->debug("Reading text from generaltext handler id:%d", config.Integer()); return CGI->generaltexth->allTexts[config.Integer()]; } const std::string delimiter = "/"; std::string s = config.String(); + logGlobal->debug("Reading text from translations by key: %s", s); JsonNode translated = CGI->generaltexth->localizedTexts; for(size_t p = s.find(delimiter); p != std::string::npos; p = s.find(delimiter)) { @@ -81,13 +85,17 @@ std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const s.erase(0, p + delimiter.length()); } if(s == config.String()) + { + logGlobal->warn("Reading non-translated text: %s", s); return s; + } return translated[s].String(); } Point InterfaceObjectConfigurable::readPosition(const JsonNode & config) const { Point p; + logGlobal->debug("Reading point"); p.x = config["x"].Integer(); p.y = config["y"].Integer(); return p; @@ -96,6 +104,7 @@ Point InterfaceObjectConfigurable::readPosition(const JsonNode & config) const Rect InterfaceObjectConfigurable::readRect(const JsonNode & config) const { Rect p; + logGlobal->debug("Reading rect"); p.x = config["x"].Integer(); p.y = config["y"].Integer(); p.w = config["w"].Integer(); @@ -105,6 +114,7 @@ Rect InterfaceObjectConfigurable::readRect(const JsonNode & config) const ETextAlignment InterfaceObjectConfigurable::readTextAlignment(const JsonNode & config) const { + logGlobal->debug("Reading text alignment"); if(!config.isNull()) { if(config.String() == "center") @@ -114,11 +124,13 @@ ETextAlignment InterfaceObjectConfigurable::readTextAlignment(const JsonNode & c if(config.String() == "right") return ETextAlignment::BOTTOMRIGHT; } + logGlobal->debug("Uknown text alignment attribute"); return ETextAlignment::CENTER; } SDL_Color InterfaceObjectConfigurable::readColor(const JsonNode & config) const { + logGlobal->debug("Reading color"); if(!config.isNull()) { if(config.String() == "yellow") @@ -134,11 +146,13 @@ SDL_Color InterfaceObjectConfigurable::readColor(const JsonNode & config) const if(config.String() == "bright-yellow") return Colors::BRIGHT_YELLOW; } + logGlobal->debug("Uknown color attribute"); return Colors::DEFAULT_KEY_COLOR; } EFonts InterfaceObjectConfigurable::readFont(const JsonNode & config) const { + logGlobal->debug("Reading font"); if(!config.isNull()) { if(config.String() == "big") @@ -150,16 +164,21 @@ EFonts InterfaceObjectConfigurable::readFont(const JsonNode & config) const if(config.String() == "tiny") return EFonts::FONT_TINY; } + logGlobal->debug("Uknown font attribute"); return EFonts::FONT_TIMES; } std::pair<std::string, std::string> InterfaceObjectConfigurable::readHintText(const JsonNode & config) const { + logGlobal->debug("Reading hint text"); std::pair<std::string, std::string> result; if(!config.isNull()) { if(config.isNumber()) + { + logGlobal->debug("Reading hint text (zelp) from generaltext handler id:%d", config.Integer()); return CGI->generaltexth->zelp[config.Integer()]; + } if(config.getType() == JsonNode::JsonType::DATA_STRUCT) { @@ -169,6 +188,7 @@ std::pair<std::string, std::string> InterfaceObjectConfigurable::readHintText(co } if(config.getType() == JsonNode::JsonType::DATA_STRING) { + logGlobal->debug("Reading non-translated hint: %s", config.String()); result.first = result.second = config.String(); } } @@ -177,6 +197,7 @@ std::pair<std::string, std::string> InterfaceObjectConfigurable::readHintText(co std::shared_ptr<CPicture> InterfaceObjectConfigurable::buildPicture(const JsonNode & config) const { + logGlobal->debug("Building widget CPicture"); auto image = config["image"].String(); auto position = readPosition(config["position"]); auto pic = std::make_shared<CPicture>(image, position.x, position.y); @@ -187,6 +208,7 @@ std::shared_ptr<CPicture> InterfaceObjectConfigurable::buildPicture(const JsonNo std::shared_ptr<CLabel> InterfaceObjectConfigurable::buildLabel(const JsonNode & config) const { + logGlobal->debug("Building widget CLabel"); auto font = readFont(config["font"]); auto alignment = readTextAlignment(config["alignment"]); auto color = readColor(config["color"]); @@ -197,6 +219,7 @@ std::shared_ptr<CLabel> InterfaceObjectConfigurable::buildLabel(const JsonNode & std::shared_ptr<CToggleGroup> InterfaceObjectConfigurable::buildToggleGroup(const JsonNode & config) const { + logGlobal->debug("Building widget CToggleGroup"); auto position = readPosition(config["position"]); auto group = std::make_shared<CToggleGroup>(0); group->pos += position; @@ -219,6 +242,7 @@ std::shared_ptr<CToggleGroup> InterfaceObjectConfigurable::buildToggleGroup(cons std::shared_ptr<CToggleButton> InterfaceObjectConfigurable::buildToggleButton(const JsonNode & config) const { + logGlobal->debug("Building widget CToggleButton"); auto position = readPosition(config["position"]); auto image = config["image"].String(); auto zelp = readHintText(config["zelp"]); @@ -238,6 +262,7 @@ std::shared_ptr<CToggleButton> InterfaceObjectConfigurable::buildToggleButton(co std::shared_ptr<CButton> InterfaceObjectConfigurable::buildButton(const JsonNode & config) const { + logGlobal->debug("Building widget CButton"); auto position = readPosition(config["position"]); auto image = config["image"].String(); auto zelp = readHintText(config["zelp"]); @@ -256,6 +281,7 @@ std::shared_ptr<CButton> InterfaceObjectConfigurable::buildButton(const JsonNode std::shared_ptr<CLabelGroup> InterfaceObjectConfigurable::buildLabelGroup(const JsonNode & config) const { + logGlobal->debug("Building widget CLabelGroup"); auto font = readFont(config["font"]); auto alignment = readTextAlignment(config["alignment"]); auto color = readColor(config["color"]); @@ -274,6 +300,7 @@ std::shared_ptr<CLabelGroup> InterfaceObjectConfigurable::buildLabelGroup(const std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode & config) const { + logGlobal->debug("Building widget CSlider"); auto position = readPosition(config["position"]); int length = config["size"].Integer(); auto style = config["style"].String() == "brown" ? CSlider::BROWN : CSlider::BLUE; @@ -286,6 +313,7 @@ std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode std::shared_ptr<CAnimImage> InterfaceObjectConfigurable::buildImage(const JsonNode & config) const { + logGlobal->debug("Building widget CAnimImage"); auto position = readPosition(config["position"]); auto image = config["image"].String(); int group = config["group"].isNull() ? 0 : config["group"].Integer(); @@ -295,6 +323,7 @@ std::shared_ptr<CAnimImage> InterfaceObjectConfigurable::buildImage(const JsonNo std::shared_ptr<CFilledTexture> InterfaceObjectConfigurable::buildTexture(const JsonNode & config) const { + logGlobal->debug("Building widget CFilledTexture"); auto image = config["image"].String(); auto rect = readRect(config["rect"]); return std::make_shared<CFilledTexture>(image, rect); @@ -302,6 +331,7 @@ std::shared_ptr<CFilledTexture> InterfaceObjectConfigurable::buildTexture(const std::shared_ptr<CShowableAnim> InterfaceObjectConfigurable::buildAnimation(const JsonNode & config) const { + logGlobal->debug("Building widget CShowableAnim"); auto position = readPosition(config["position"]); auto image = config["image"].String(); ui8 flags = 0; @@ -326,9 +356,11 @@ std::shared_ptr<CShowableAnim> InterfaceObjectConfigurable::buildAnimation(const std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(JsonNode config) const { assert(!config.isNull()); + logGlobal->debug("Building widget from config"); //overrides from variables for(auto & item : config["overrides"].Struct()) { + logGlobal->debug("Config attribute %s was overriden by variable %s", item.first, item.second.String()); config[item.first] = variables[item.second.String()]; } @@ -375,12 +407,15 @@ std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(JsonNode co } if(type == "custom") { + logGlobal->debug("Calling custom widget building function"); return const_cast<InterfaceObjectConfigurable*>(this)->buildCustomWidget(config); } + logGlobal->error("Unknown type, nullptr will be returned"); return std::shared_ptr<CIntObject>(nullptr); } std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildCustomWidget(const JsonNode & config) { + logGlobal->error("Default custom widget builder called"); return nullptr; } From 8f7025328f379173b85e2a79bf5e2266ae188d17 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Thu, 22 Dec 2022 01:37:33 +0400 Subject: [PATCH 32/35] Read default template from config, named custom types --- client/gui/InterfaceObjectConfigurable.cpp | 10 +++------- client/lobby/RandomMapTab.cpp | 17 +++++++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index b66370f26..604ea5f0d 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -405,13 +405,9 @@ std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(JsonNode co { return buildSlider(config); } - if(type == "custom") - { - logGlobal->debug("Calling custom widget building function"); - return const_cast<InterfaceObjectConfigurable*>(this)->buildCustomWidget(config); - } - logGlobal->error("Unknown type, nullptr will be returned"); - return std::shared_ptr<CIntObject>(nullptr); + + logGlobal->debug("Calling custom widget building function"); + return const_cast<InterfaceObjectConfigurable*>(this)->buildCustomWidget(config); } std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildCustomWidget(const JsonNode & config) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 9b5cd68da..b65107728 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -281,7 +281,7 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts) if(tmpl) w->addTextOverlay(tmpl->getName(), EFonts::FONT_SMALL); else - w->addTextOverlay("default", EFonts::FONT_SMALL); + w->addTextOverlay(readText(variables["defaultTemplate"]), EFonts::FONT_SMALL); } for(auto r : VLC->terrainTypeHandler->roads()) { @@ -301,7 +301,7 @@ void RandomMapTab::setTemplate(const CRmgTemplate * tmpl) if(tmpl) w->addTextOverlay(tmpl->getName(), EFonts::FONT_SMALL); else - w->addTextOverlay("default", EFonts::FONT_SMALL); + w->addTextOverlay(readText(variables["defaultTemplate"]), EFonts::FONT_SMALL); } updateMapInfoByHost(); } @@ -362,7 +362,7 @@ void TemplatesDropBox::ListItem::updateItem(int idx, const CRmgTemplate * _item) if(idx) w->setText(""); else - w->setText("default"); + w->setText(readText(dropBox.variables["defaultTemplate"])); } } } @@ -424,9 +424,14 @@ TemplatesDropBox::TemplatesDropBox(RandomMapTab & randomMapTab, int3 size): std::shared_ptr<CIntObject> TemplatesDropBox::buildCustomWidget(const JsonNode & config) { - auto position = readPosition(config["position"]); - listItems.push_back(std::make_shared<ListItem>(config, *this, position)); - return listItems.back(); + if(config["type"].String() == "templateListItem") + { + auto position = readPosition(config["position"]); + listItems.push_back(std::make_shared<ListItem>(config, *this, position)); + return listItems.back(); + } + + return InterfaceObjectConfigurable::buildCustomWidget(config); } void TemplatesDropBox::sliderMove(int slidPos) From e245dbaf9dc4e69796053fc5d9c742d25ee4ffe5 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Thu, 22 Dec 2022 02:05:29 +0400 Subject: [PATCH 33/35] Use kind-of-factory approach for widget builders --- client/gui/InterfaceObjectConfigurable.cpp | 66 +++++++--------------- client/gui/InterfaceObjectConfigurable.h | 11 +++- client/lobby/RandomMapTab.cpp | 15 ++--- client/lobby/RandomMapTab.h | 4 +- 4 files changed, 34 insertions(+), 62 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 604ea5f0d..ef6575efe 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -35,6 +35,21 @@ InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config InterfaceObjectConfigurable::InterfaceObjectConfigurable(int used, Point offset): CIntObject(used, offset) { + REGISTER_BUILDER("picture", &InterfaceObjectConfigurable::buildPicture); + REGISTER_BUILDER("image", &InterfaceObjectConfigurable::buildImage); + REGISTER_BUILDER("texture", &InterfaceObjectConfigurable::buildTexture); + REGISTER_BUILDER("animation", &InterfaceObjectConfigurable::buildAnimation); + REGISTER_BUILDER("label", &InterfaceObjectConfigurable::buildLabel); + REGISTER_BUILDER("toggleGroup", &InterfaceObjectConfigurable::buildToggleGroup); + REGISTER_BUILDER("toggleButton", &InterfaceObjectConfigurable::buildToggleButton); + REGISTER_BUILDER("button", &InterfaceObjectConfigurable::buildButton); + REGISTER_BUILDER("labelGroup", &InterfaceObjectConfigurable::buildLabelGroup); + REGISTER_BUILDER("slider", &InterfaceObjectConfigurable::buildSlider); +} + +void InterfaceObjectConfigurable::registerBuilder(const std::string & type, BuilderFunction f) +{ + builders[type] = f; } void InterfaceObjectConfigurable::addCallback(const std::string & callbackName, std::function<void(int)> callback) @@ -365,53 +380,10 @@ std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(JsonNode co } auto type = config["type"].String(); - if(type == "picture") - { - return buildPicture(config); - } - if(type == "image") - { - return buildImage(config); - } - if(type == "texture") - { - return buildTexture(config); - } - if(type == "animation") - { - return buildAnimation(config); - } - if(type == "label") - { - return buildLabel(config); - } - if(type == "toggleGroup") - { - return buildToggleGroup(config); - } - if(type == "toggleButton") - { - return buildToggleButton(config); - } - if(type == "button") - { - return buildButton(config); - } - if(type == "labelGroup") - { - return buildLabelGroup(config); - } - if(type == "slider") - { - return buildSlider(config); - } + auto buildIterator = builders.find(type); + if(buildIterator != builders.end()) + return (buildIterator->second)(config); - logGlobal->debug("Calling custom widget building function"); - return const_cast<InterfaceObjectConfigurable*>(this)->buildCustomWidget(config); -} - -std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildCustomWidget(const JsonNode & config) -{ - logGlobal->error("Default custom widget builder called"); + logGlobal->error("Builder with type %s is not registered", type); return nullptr; } diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h index 50ba9f30c..3525a8d09 100644 --- a/client/gui/InterfaceObjectConfigurable.h +++ b/client/gui/InterfaceObjectConfigurable.h @@ -25,6 +25,8 @@ class CAnimImage; class CShowableAnim; class CFilledTexture; +#define REGISTER_BUILDER(type, method) registerBuilder(type, std::bind(method, this, std::placeholders::_1)) + class InterfaceObjectConfigurable: public CIntObject { public: @@ -32,6 +34,10 @@ public: InterfaceObjectConfigurable(const JsonNode & config, int used=0, Point offset=Point()); protected: + + using BuilderFunction = std::function<std::shared_ptr<CIntObject>(const JsonNode &)>; + void registerBuilder(const std::string &, BuilderFunction); + //must be called after adding callbacks void init(const JsonNode & config); @@ -67,14 +73,13 @@ protected: std::shared_ptr<CAnimImage> buildImage(const JsonNode &) const; std::shared_ptr<CShowableAnim> buildAnimation(const JsonNode &) const; std::shared_ptr<CFilledTexture> buildTexture(const JsonNode &) const; - - + //composite widgets - virtual std::shared_ptr<CIntObject> buildCustomWidget(const JsonNode & config); std::shared_ptr<CIntObject> buildWidget(JsonNode config) const; private: + std::map<std::string, BuilderFunction> builders; std::map<std::string, std::shared_ptr<CIntObject>> widgets; std::map<std::string, std::function<void(int)>> callbacks; }; diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index b65107728..8f89ffaa1 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -399,6 +399,8 @@ TemplatesDropBox::TemplatesDropBox(RandomMapTab & randomMapTab, int3 size): InterfaceObjectConfigurable(LCLICK | HOVER), randomMapTab(randomMapTab) { + REGISTER_BUILDER("templateListItem", &TemplatesDropBox::buildListItem); + curItems = VLC->tplh->getTemplates(); vstd::erase_if(curItems, [size](const CRmgTemplate * t){return !t->matchesSize(size);}); curItems.insert(curItems.begin(), nullptr); //default template @@ -422,16 +424,11 @@ TemplatesDropBox::TemplatesDropBox(RandomMapTab & randomMapTab, int3 size): updateListItems(); } -std::shared_ptr<CIntObject> TemplatesDropBox::buildCustomWidget(const JsonNode & config) +std::shared_ptr<CIntObject> TemplatesDropBox::buildListItem(const JsonNode & config) { - if(config["type"].String() == "templateListItem") - { - auto position = readPosition(config["position"]); - listItems.push_back(std::make_shared<ListItem>(config, *this, position)); - return listItems.back(); - } - - return InterfaceObjectConfigurable::buildCustomWidget(config); + auto position = readPosition(config["position"]); + listItems.push_back(std::make_shared<ListItem>(config, *this, position)); + return listItems.back(); } void TemplatesDropBox::sliderMove(int slidPos) diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index 8909f5f1a..babd6ff41 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -72,10 +72,8 @@ public: void clickLeft(tribool down, bool previousState) override; void setTemplate(const CRmgTemplate *); -protected: - std::shared_ptr<CIntObject> buildCustomWidget(const JsonNode & config) override; - private: + std::shared_ptr<CIntObject> buildListItem(const JsonNode & config); void sliderMove(int slidPos); void updateListItems(); From cb76cc54ac2f54459693d7baca35368aa9b95d00 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Thu, 22 Dec 2022 02:08:26 +0400 Subject: [PATCH 34/35] Move logging to debug level --- client/gui/InterfaceObjectConfigurable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index ef6575efe..99639b0f1 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -60,7 +60,7 @@ void InterfaceObjectConfigurable::addCallback(const std::string & callbackName, void InterfaceObjectConfigurable::init(const JsonNode &config) { OBJ_CONSTRUCTION; - logGlobal->info("Building configurable interface object"); + logGlobal->debug("Building configurable interface object"); for(auto & item : config["variables"].Struct()) { logGlobal->debug("Read variable named %s", item.first); From ad5bf2ac4fdf2a9900cc0a02a49151f9634e72e6 Mon Sep 17 00:00:00 2001 From: nordsoft <nordsoft@rambler.ru> Date: Thu, 22 Dec 2022 02:10:56 +0400 Subject: [PATCH 35/35] Fix msvc build --- client/lobby/RandomMapTab.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/lobby/RandomMapTab.h b/client/lobby/RandomMapTab.h index babd6ff41..454f8fcb2 100644 --- a/client/lobby/RandomMapTab.h +++ b/client/lobby/RandomMapTab.h @@ -65,6 +65,8 @@ class TemplatesDropBox : public InterfaceObjectConfigurable void clickLeft(tribool down, bool previousState) override; }; + friend struct ListItem; + public: TemplatesDropBox(RandomMapTab & randomMapTab, int3 size);