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 InterfaceObjectConfigurable::buildSlider(const JsonNode return std::make_shared(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style); } +std::shared_ptr 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(image, frame, group, position.x, position.y); +} + +std::shared_ptr InterfaceObjectConfigurable::buildTexture(const JsonNode & config) const +{ + auto image = config["image"].String(); + auto rect = readRect(config); + return std::make_shared(image, rect); +} + +std::shared_ptr 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(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 InterfaceObjectConfigurable::buildWidget(const JsonNode & config) const { assert(!config.isNull()); @@ -283,6 +331,18 @@ std::shared_ptr 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 buildButton(const JsonNode &) const; std::shared_ptr buildLabelGroup(const JsonNode &) const; std::shared_ptr buildSlider(const JsonNode &) const; + std::shared_ptr buildImage(const JsonNode &) const; + std::shared_ptr buildAnimation(const JsonNode &) const; + std::shared_ptr buildTexture(const JsonNode &) const; + //composite widgets virtual std::shared_ptr 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 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("Bl3DCvex", pos); center(pos); - buttonOk = std::make_shared(Point(43, 240), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], [](){}); - buttonCancel = std::make_shared(Point(193, 240), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [&]() + for(int plId = 0; plId < totalPlayers; ++plId) + { + players.push_back(std::make_shared([&, totalPlayers, plId](int sel) + { + SObjectConstruction obj__i(players[plId].get()); + for(int teamId = 0; teamId < totalPlayers; ++teamId) + { + auto button = std::dynamic_pointer_cast(players[plId]->buttons[teamId]); + assert(button); + if(sel == teamId) + { + button->addOverlay(std::make_shared("ITGFLAGS", plId, 0, 8, 8)); + } + else + { + button->addOverlay(std::make_shared("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("TeamPlSl", p.x, p.y)); + auto button = std::make_shared(p, "TeamPlSl", std::pair{"", ""}); + 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(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(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 opts); void setTemplate(const CRmgTemplate *); + CMapGenOptions & obtainMapGenOptions() {return *mapGenOptions;} CFunctionList, std::shared_ptr)> mapInfoChanged; @@ -97,5 +99,6 @@ private: std::shared_ptr background; std::shared_ptr labels; std::shared_ptr buttonOk, buttonCancel; - std::vector> teams; + std::vector> players; + std::vector> 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 * 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 rememberTownTypes; + std::map 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, 2> teamNumbers; + std::set 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()