From ff3449b60dea0c9882a15e81ac6f5d9176f50d92 Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Thu, 26 Nov 2020 11:05:27 +0100 Subject: [PATCH 01/15] Economy Mode (a la Settlers 3 economy mode, i.e. collect (in teams) as much as possible of 7 good types) --- libs/s25main/EconomyModeHandler.cpp | 344 ++++++++++++++++++ libs/s25main/EconomyModeHandler.h | 93 +++++ libs/s25main/Game.cpp | 10 + libs/s25main/GlobalGameSettings.cpp | 1 + libs/s25main/SerializedGameData.cpp | 10 + libs/s25main/addons/AddonGameLength.h | 39 ++ libs/s25main/desktops/dskHostGame.cpp | 63 ++++ libs/s25main/desktops/dskHostGame.h | 4 + libs/s25main/gameData/const_gui_ids.h | 1 + libs/s25main/gameTypes/GO_Type.h | 3 +- libs/s25main/gameTypes/GameSettingTypes.h | 3 +- .../ingameWindows/iwEconomicProgress.cpp | 200 ++++++++++ .../ingameWindows/iwEconomicProgress.h | 45 +++ libs/s25main/ingameWindows/iwMainMenu.cpp | 13 + libs/s25main/world/GameWorldBase.cpp | 5 +- libs/s25main/world/GameWorldBase.h | 6 + libs/s25main/world/GameWorldViewer.cpp | 6 + 17 files changed, 843 insertions(+), 3 deletions(-) create mode 100644 libs/s25main/EconomyModeHandler.cpp create mode 100644 libs/s25main/EconomyModeHandler.h create mode 100644 libs/s25main/addons/AddonGameLength.h create mode 100644 libs/s25main/ingameWindows/iwEconomicProgress.cpp create mode 100644 libs/s25main/ingameWindows/iwEconomicProgress.h diff --git a/libs/s25main/EconomyModeHandler.cpp b/libs/s25main/EconomyModeHandler.cpp new file mode 100644 index 0000000000..d3d27fdfbf --- /dev/null +++ b/libs/s25main/EconomyModeHandler.cpp @@ -0,0 +1,344 @@ +// Copyright (c) 2005 - 2020 Settlers Freaks (sf-team at siedler25.org) +// +// This file is part of Return To The Roots. +// +// Return To The Roots is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// Return To The Roots is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Return To The Roots. If not, see . + +#include "EconomyModeHandler.h" + +#include "EventManager.h" +#include "GameInterface.h" +#include "GamePlayer.h" +#include "GlobalGameSettings.h" +#include "SerializedGameData.h" +#include "random/Random.h" +#include "world/GameWorld.h" +#include "world/GameWorldGame.h" +#include "gameTypes/JobTypes.h" +#include "gameData/JobConsts.h" +#include + +auto getPlayerMask = [](unsigned playerId) { return 1u << playerId; }; + +GoodType types_less[]{ + /* 1 */ GD_TONGS, // Zange + /* 2 */ GD_HAMMER, // Hammer + /* 3 */ GD_AXE, // Axt + /* 4 */ GD_SAW, // Saege + /* 5 */ GD_PICKAXE, // Spitzhacke + /* 6 */ GD_SHOVEL, // Schaufel + /* 7 */ GD_CRUCIBLE, // Schmelztiegel + /* 8 */ GD_RODANDLINE, // Angel + /* 9 */ GD_SCYTHE, // Sense + /* 12 */ GD_CLEAVER, // Beil + /* 13 */ GD_ROLLINGPIN, // Nudelholz + /* 14 */ GD_BOW, // Bogen +}; + +GoodType types_more[]{ + /* 0 */ GD_BEER, // Bier + /* 11 */ GD_WATER, // Wasser + /* 15 */ GD_BOAT, // Boot + /* 16 */ GD_SWORD, // Schwert + /* 17 */ GD_IRON, // Eisen + /* 18 */ GD_FLOUR, // Mehl + /* 19 */ GD_FISH, // Fisch + /* 20 */ GD_BREAD, // Brot + /* 22 */ GD_WOOD, // Holz + /* 23 */ GD_BOARDS, // Bretter + /* 24 */ GD_STONES, // Steine + /* 27 */ GD_GRAIN, // Getreide + /* 28 */ GD_COINS, // Mnzen + /* 29 */ GD_GOLD, // Gold + /* 30 */ GD_IRONORE, // Eisenerz + /* 31 */ GD_COAL, // Kohle + /* 32 */ GD_MEAT, // Fleisch + /* 33 */ GD_HAM, // Schinken ( Schwein ) +}; + +EconomyModeHandler::EconomyModeHandler(unsigned end_frame) : end_frame(end_frame), last_updated(0) +{ + constexpr unsigned num_types_less = sizeof(types_less) / sizeof(types_less[0]); + constexpr unsigned num_types_more = sizeof(types_more) / sizeof(types_more[0]); + + // Randomly determine *numGoodTypesToCollect* many good types, one of which is a tool + unsigned int types_found = 0; + + while(types_found < numGoodTypesToCollect - 1) + { + GoodType next_type = types_more[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), num_types_more)]; + unsigned i = 0; + for(; i < types_found; i++) + { + if(types[i] == next_type) + { + break; + } + } + if(i == types_found) + { + types[types_found] = next_type; + types_found++; + } + } + types[numGoodTypesToCollect - 1] = types_less[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), num_types_less)]; + types_found++; + + if(end_frame > 0) + event = GetEvMgr().AddEvent(this, end_frame); + else + event = nullptr; + + // Send Mission Goal + for(unsigned p = 0; p < gwg->GetNumPlayers(); ++p) + { + std::string goaltext = _("Economy Mode: Collect as much as you can of the following good types: "); + for(unsigned i = 0; i < numGoodTypesToCollect; i++) + { + if(i > 0) + goaltext += ", "; + goaltext += _(WARE_NAMES[types[i]]); + } + goaltext += ". "; + goaltext += _("Tools in the hands of workers are also counted. So are weapons, and beer, that soldiers have in " + "use. For an updating tally of the collected goods see the economic progress window."); + + gwg->GetPostMgr().SetMissionGoal(p, goaltext); + } +} + +EconomyModeHandler::EconomyModeHandler(SerializedGameData& sgd, unsigned objId) + : GameObject(sgd, objId), end_frame(sgd.PopUnsignedInt()), last_updated(0) +{ + if(!isOver()) + { + event = sgd.PopEvent(); + } else + { + event = nullptr; + } + for(auto& type : types) + { + type = (GoodType)sgd.PopUnsignedChar(); + } +} + +void EconomyModeHandler::Destroy() {} + +/// Serialisierungsfunktion +void EconomyModeHandler::Serialize(SerializedGameData& sgd) const +{ + sgd.PushUnsignedInt(end_frame); + if(!isOver()) + { + sgd.PushEvent(event); + } + for(auto type : types) + { + sgd.PushUnsignedChar(type); + } +} + +void EconomyModeHandler::FindTeams() +{ + // If we already determined who is in a team with whom skip this. For the economy mode we only count teams at game + // start + if(!teams.empty()) + return; + for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) + { + if(gwg->GetPlayer(i).isUsed()) + { + bool found_team = false; + for(const auto& team : teams) + { + if(team.inTeam(i)) + { + found_team = true; + break; + } + } + if(!found_team) + { + const GamePlayer& player = gwg->GetPlayer(i); + unsigned new_team = getPlayerMask(i); + unsigned num_players_in_team = 1; + for(unsigned j = i + 1; j < gwg->GetNumPlayers(); ++j) + { + if(gwg->GetPlayer(j).isUsed() && player.IsAlly(j)) + { + num_players_in_team++; + new_team = new_team | getPlayerMask(j); + } + } + teams.emplace_back(new_team, num_players_in_team); + } + } + } +} + +unsigned int EconomyModeHandler::SumGood(GoodType good, const Inventory& Inventory) +{ + unsigned int retVal = Inventory.goods[good]; + + // Add the tools used by workers to the good totals + for(unsigned int j = 0; j < NUM_JOB_TYPES; j++) + { + boost::optional tool = JOB_CONSTS[(Job)j].tool; + if(tool && tool == good) + { + retVal += Inventory.people[j]; + } + } + // Add the weapons and beer used by soldiers to the good totals + if(good == GD_BEER || good == GD_SWORD || good == GD_SHIELDROMANS) + { + for(const auto& it : SOLDIER_JOBS) + { + retVal += Inventory.people[it]; + } + } + + return retVal; +} + +void EconomyModeHandler::UpdateAmounts() +{ + // Return if the game is over or we already updated the amounts this game frame + if((isOver() && end_frame != 0) || last_updated == GetEvMgr().GetCurrentGF()) + { + return; + } + + // Sum up goods + for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) + { + const GamePlayer& player = gwg->GetPlayer(i); + Inventory playerInventory = player.GetInventory(); + for(unsigned int g = 0; g < numGoodTypesToCollect; g++) + { + amounts[g][i] = SumGood(types[g], playerInventory); + } + } + + // Compute Teams + FindTeams(); + + // Compute the amounts for the teams + for(unsigned int& maxTeamAmount : maxTeamAmounts) + { + maxTeamAmount = 0; + } + + for(auto& team : teams) + { + for(unsigned int& teamAmount : team.teamAmounts) + { + teamAmount = 0; + } + for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) + { + if(team.inTeam(i)) + { + for(unsigned int g = 0; g < numGoodTypesToCollect; g++) + { + team.teamAmounts[g] += GetAmount(g, i); + if(team.teamAmounts[g] > maxTeamAmounts[g]) + { + maxTeamAmounts[g] = team.teamAmounts[g]; + } + } + } + } + } + // Determine the leading teams for each good type and determine how many good type wins is the maximum. + mostWins = 0; + for(auto& team : teams) + { + team.teamWins = 0; + for(unsigned int g = 0; g < numGoodTypesToCollect; g++) + { + if(team.teamAmounts[g] >= maxTeamAmounts[g]) + { + team.teamWins++; + if(team.teamWins > mostWins) + { + mostWins = team.teamWins; + } + } + } + } + + last_updated = GetEvMgr().GetCurrentGF(); +} + +void EconomyModeHandler::HandleEvent(const unsigned) +{ + if(isOver()) + { + return; + } + + // Handle game end event + + // Update one last time + UpdateAmounts(); + + // Determine mask of all players in teams with the most good type wins + unsigned bestMask = 0; + unsigned int numWinners = 0; + for(auto& team : teams) + { + if(team.teamWins == mostWins) + { + bestMask = bestMask | team.mask; + numWinners += team.num_players_in_team; + } + } + + // Let players know who won + if(bestMask && numWinners != 1) + gwg->GetGameInterface()->GI_TeamWinner(bestMask); + else + for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) + { + if(bestMask & getPlayerMask(i)) + { + gwg->GetGameInterface()->GI_Winner(i); + } + } + + // Call function to recalculcate visibilities + for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) + { + gwg->GetGameInterface()->GI_TreatyOfAllianceChanged(i); // TODO: Is this abuse? Should we rename that function? + } + event = nullptr; +} + +bool EconomyModeHandler::globalVisibility() const +{ + return gwg->GetGGS().objective == GO_ECONOMYMODE && gwg->GetEvMgr().GetCurrentGF() >= end_frame + && GetEndFrame() > 0; +} + +bool EconomyModeHandler::isOver() const +{ + return gwg->GetGGS().objective == GO_ECONOMYMODE && end_frame < GetEvMgr().GetCurrentGF(); +} + +bool EconomyModeHandler::econTeam::inTeam(unsigned int playerId) const +{ + return mask & getPlayerMask(playerId); +} diff --git a/libs/s25main/EconomyModeHandler.h b/libs/s25main/EconomyModeHandler.h new file mode 100644 index 0000000000..9471fafc8a --- /dev/null +++ b/libs/s25main/EconomyModeHandler.h @@ -0,0 +1,93 @@ +#pragma once +#include "GameObject.h" +#include "gameTypes/GoodTypes.h" +#include "gameTypes/Inventory.h" +#include "gameTypes/JobTypes.h" +#include "gameData/MaxPlayers.h" + +#include + +class SerializedGameData; +class GameEvent; + +// Handler object to keep track of the economy mode progress and for the game end event +class EconomyModeHandler : public GameObject +{ +public: + static const unsigned int numGoodTypesToCollect = 7; + + struct econTeam + { + unsigned mask; + unsigned teamAmounts[numGoodTypesToCollect]; + unsigned num_players_in_team; + unsigned teamWins; + + econTeam(unsigned mask, unsigned num_players_in_team) noexcept + : mask(mask), num_players_in_team(num_players_in_team) + { + for(unsigned int& teamAmount : teamAmounts) + { + teamAmount = 0; + } + teamWins = 0; + } + + bool inTeam(unsigned int playerId) const; + }; + +private: + /// Frame in which the game is going to end + unsigned end_frame; + /// End game Event + const GameEvent* event; + /// Good types to collect + GoodType types[numGoodTypesToCollect]; + + // Data for economy mode progress tracking + std::vector teams; + unsigned maxTeamAmounts[numGoodTypesToCollect] = {0}; + unsigned int mostWins = 0; + unsigned amounts[numGoodTypesToCollect][MAX_PLAYERS]; + unsigned last_updated; + + unsigned int SumGood(GoodType good, const Inventory& Inventory); + + void FindTeams(); + +public: + EconomyModeHandler(unsigned end_frame); + + EconomyModeHandler(SerializedGameData& sgd, unsigned objId); + + /// Destroy + void Destroy() override; + + void Serialize(SerializedGameData& sgd) const override; + + /// Event-Handler + void HandleEvent(unsigned id) override; + + const std::vector& GetTeams() + { + FindTeams(); + return teams; + } + + // Methods to update the ware trackers + void UpdateAmounts(); + unsigned int GetAmount(unsigned int i, unsigned int player) { return amounts[i][player]; } + unsigned int GetMaxTeamAmount(unsigned int i) { return maxTeamAmounts[i]; } + + unsigned GetEndFrame() const { return end_frame; } + + // Check if the game has ended, so everything should be visible + bool globalVisibility() const; + + // Return the good types to collect + GoodType* GetTypes() { return types; } + + GO_Type GetGOT() const override { return GOT_ECONOMYMODEHANDLER; } + + bool isOver() const; +}; diff --git a/libs/s25main/Game.cpp b/libs/s25main/Game.cpp index f52b25c63a..269ffe6d61 100644 --- a/libs/s25main/Game.cpp +++ b/libs/s25main/Game.cpp @@ -19,6 +19,8 @@ #include "EventManager.h" #include "GameInterface.h" #include "GamePlayer.h" +#include "addons/AddonGameLength.h" +#include "addons/const_addons.h" #include "ai/AIPlayer.h" #include "lua/LuaInterfaceGame.h" #include @@ -41,7 +43,15 @@ void Game::Start(bool startFromSave) if(startFromSave) CheckObjective(); else + { + if(ggs_.objective == GO_ECONOMYMODE) + { + unsigned int selection = ggs_.getSelection(AddonId::GAME_LENGTH); + world_.econHandler = + new EconomyModeHandler(AddonGameLengthList[selection] / 50); // 50 is the assumed game frame length + } StatisticStep(); + } if(world_.HasLua()) world_.GetLua().EventStart(!startFromSave); } diff --git a/libs/s25main/GlobalGameSettings.cpp b/libs/s25main/GlobalGameSettings.cpp index 0b364e2819..b2363730b4 100644 --- a/libs/s25main/GlobalGameSettings.cpp +++ b/libs/s25main/GlobalGameSettings.cpp @@ -115,6 +115,7 @@ void GlobalGameSettings::registerAllAddons() registerAddon(std::make_unique()); registerAddon(std::make_unique()); + registerAddon(std::make_unique()); } void GlobalGameSettings::resetAddons() diff --git a/libs/s25main/SerializedGameData.cpp b/libs/s25main/SerializedGameData.cpp index 8f4db8fce7..ce7b19a3c8 100644 --- a/libs/s25main/SerializedGameData.cpp +++ b/libs/s25main/SerializedGameData.cpp @@ -167,6 +167,7 @@ GameObject* SerializedGameData::Create_GameObject(const GO_Type got, const unsig case GOT_SHIP: return new noShip(*this, obj_id); case GOT_SHIPBUILDINGSITE: return new noShipBuildingSite(*this, obj_id); case GOT_CHARBURNERPILE: return new noCharburnerPile(*this, obj_id); + case GOT_ECONOMYMODEHANDLER: return new EconomyModeHandler(*this, obj_id); case GOT_NOTHING: case GOT_UNKNOWN: RTTR_Assert(false); break; } @@ -229,6 +230,10 @@ void SerializedGameData::MakeSnapshot(const std::shared_ptr& game) gw.Serialize(*this); // EventManager writeEm->Serialize(*this); + if(game->ggs_.objective == GO_ECONOMYMODE) + { + PushObject(gw.econHandler, true); + } // Spieler serialisieren for(unsigned i = 0; i < gw.GetNumPlayers(); ++i) { @@ -264,6 +269,11 @@ void SerializedGameData::ReadSnapshot(const std::shared_ptr& game, ILocalG gw.Deserialize(game, localGameState, *this); em->Deserialize(*this); + if(game->ggs_.objective == GO_ECONOMYMODE) + { + gw.econHandler = PopObject(GOT_ECONOMYMODEHANDLER); + } + for(unsigned i = 0; i < gw.GetNumPlayers(); ++i) gw.GetPlayer(i).Deserialize(*this); diff --git a/libs/s25main/addons/AddonGameLength.h b/libs/s25main/addons/AddonGameLength.h new file mode 100644 index 0000000000..fa82f1cc24 --- /dev/null +++ b/libs/s25main/addons/AddonGameLength.h @@ -0,0 +1,39 @@ +// Copyright (c) 2005 - 2020 Settlers Freaks (sf-team at siedler25.org) +// +// This file is part of Return To The Roots. +// +// Return To The Roots is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// Return To The Roots is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Return To The Roots. If not, see . + +#pragma once + +#include "AddonList.h" +#include "mygettext/mygettext.h" + +/** + * Addon allows users to adjust the game length (for Economy Mode) + */ +const unsigned int AddonGameLengthList[] = {0, 15 * 60000, 30 * 60000, 60 * 60000, 90 * 60000, 120 * 60000, + 150 * 60000, 180 * 60000, 240 * 60000, 480 * 60000}; // length in ms + +class AddonGameLength : public AddonList +{ +public: + AddonGameLength() + : AddonList(AddonId::GAME_LENGTH, AddonGroup::Economy | AddonGroup::GamePlay, _("Game Length (Economy Mode)"), + _("Adjust the time after which the economy mode victory condition is checked. Reference times are " + "on fast speed."), + {_("unlimited"), _("~15min"), _("~30min"), _("~60min"), _("~90min"), _("~120min"), _("~150min"), + _("~180min"), _("~240min"), _("~480min")}) + {} +}; diff --git a/libs/s25main/desktops/dskHostGame.cpp b/libs/s25main/desktops/dskHostGame.cpp index 75948a03a9..55b15ef800 100644 --- a/libs/s25main/desktops/dskHostGame.cpp +++ b/libs/s25main/desktops/dskHostGame.cpp @@ -207,6 +207,8 @@ dskHostGame::dskHostGame(ServerType serverType, const std::shared_ptr combo->AddString(_("None")); // Kein Spielziel combo->AddString(_("Conquer 3/4 of map")); // Besitz 3/4 des Landes combo->AddString(_("Total domination")); // Alleinherrschaft + combo->AddString(_("Economy mode")); // Wirtschaftsmodus + // Lobby game? if(lobbyClient_ && lobbyClient_->IsLoggedIn()) { @@ -637,6 +639,9 @@ void dskHostGame::Msg_ButtonClick(const unsigned ctrl_id) auto* ready = GetCtrl(2); if(gameLobby->isHost()) { + if(!checkOptions()) + return; + SetPlayerReady(localPlayerId_, true); if(lua) lua->EventPlayerReady(localPlayerId_); @@ -759,6 +764,36 @@ void dskHostGame::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult UpdateGGS(); } break; + case 10: // Economy Mode - change Addon Setttings + { + if(mbr == MSR_YES) + { + gameLobby->getSettings().setSelection(AddonId::PEACEFUL, true); + if(gameLobby->getSettings().getSelection(AddonId::GAME_LENGTH) == 0) + gameLobby->getSettings().setSelection(AddonId::GAME_LENGTH, 5); + gameLobby->getSettings().setSelection(AddonId::NO_COINS_DEFAULT, true); + gameLobby->getSettings().setSelection(AddonId::LIMIT_CATAPULTS, 2); + GetCtrl(20)->SetCheck(true); // Lockteams; + UpdateGGS(); + } else if(mbr == MSR_NO) + { + forceOptions = true; + Msg_ButtonClick(2); + } + } + break; + case 11: // Peaceful Mode still active + { + if(mbr == MSR_YES) + { + gameLobby->getSettings().setSelection(AddonId::PEACEFUL, false); + } else if(mbr == MSR_NO) + { + forceOptions = true; + Msg_ButtonClick(2); + } + } + break; } } @@ -1019,3 +1054,31 @@ void dskHostGame::LC_Chat(const std::string& player, const std::string& text) lobbyChatTabAnimId = tab->GetAnimationManager().addAnimation(new BlinkButtonAnim(bt)); } } + +bool dskHostGame::checkOptions() +{ + if(forceOptions) + return true; + const GlobalGameSettings& ggs = gameLobby->getSettings(); + if(ggs.objective == GO_ECONOMYMODE && !ggs.getSelection(AddonId::PEACEFUL)) + { + WINDOWMANAGER.Show( + std::make_unique(_("Economy Mode"), + _("You chose the economy mode. Would you like to adjust settings to fit, " + "especially to peaceful mode? Choosing yes will make the adjustmenst and let " + "you review them, choosing no will start the game."), + this, MSB_YESNOCANCEL, MSB_QUESTIONGREEN, 10)); + return false; + } else if(ggs.getSelection(AddonId::PEACEFUL) + && (ggs.objective == GO_CONQUER3_4 || ggs.objective == GO_TOTALDOMINATION)) + { + WINDOWMANAGER.Show( + std::make_unique(_("Peaceful Mode"), + _("You chose a war based victory condition but peaceful mode is still active. " + "Would you like to disactivate peaceful mode before you start? Choosing no will " + "start the game, yes will let you review the changes."), + this, MSB_YESNOCANCEL, MSB_QUESTIONRED, 11)); + return false; + } + return true; +} \ No newline at end of file diff --git a/libs/s25main/desktops/dskHostGame.h b/libs/s25main/desktops/dskHostGame.h index 69eea8fb6e..df61b6d1c2 100644 --- a/libs/s25main/desktops/dskHostGame.h +++ b/libs/s25main/desktops/dskHostGame.h @@ -89,6 +89,10 @@ class dskHostGame final : public Desktop, public ClientInterface, public LobbyIn void LC_Status_Error(const std::string& error) override; void LC_Chat(const std::string& player, const std::string& text) override; + /// Addon options check with regards to peaceful mode and economy mode + bool forceOptions = false; + bool checkOptions(); + void GoBack(); bool IsSinglePlayer() { return serverType == ServerType::LOCAL; } diff --git a/libs/s25main/gameData/const_gui_ids.h b/libs/s25main/gameData/const_gui_ids.h index 172ca085b5..0394d00df5 100644 --- a/libs/s25main/gameData/const_gui_ids.h +++ b/libs/s25main/gameData/const_gui_ids.h @@ -54,6 +54,7 @@ enum GUI_ID CGI_MUSICPLAYER, CGI_INPUTWINDOW, CGI_STATISTICS, + CGI_ECONOMICPROGRESS, CGI_DIPLOMACY, CGI_SUGGESTPACT, CGI_SHIP, diff --git a/libs/s25main/gameTypes/GO_Type.h b/libs/s25main/gameTypes/GO_Type.h index 432a74089d..464285b649 100644 --- a/libs/s25main/gameTypes/GO_Type.h +++ b/libs/s25main/gameTypes/GO_Type.h @@ -85,5 +85,6 @@ enum GO_Type GOT_SHIP, GOT_CHARBURNERPILE, GOT_NOF_TRADELEADER, - GOT_NOF_TRADEDONKEY + GOT_NOF_TRADEDONKEY, + GOT_ECONOMYMODEHANDLER }; diff --git a/libs/s25main/gameTypes/GameSettingTypes.h b/libs/s25main/gameTypes/GameSettingTypes.h index b0e840c62b..6cbc6b96c4 100644 --- a/libs/s25main/gameTypes/GameSettingTypes.h +++ b/libs/s25main/gameTypes/GameSettingTypes.h @@ -29,7 +29,8 @@ enum GameObjective { GO_NONE = 0, GO_CONQUER3_4, - GO_TOTALDOMINATION + GO_TOTALDOMINATION, + GO_ECONOMYMODE }; enum StartWares { diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.cpp b/libs/s25main/ingameWindows/iwEconomicProgress.cpp new file mode 100644 index 0000000000..55d83b050b --- /dev/null +++ b/libs/s25main/ingameWindows/iwEconomicProgress.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2005 - 2020 Settlers Freaks (sf-team at siedler25.org) +// +// This file is part of Return To The Roots. +// +// Return To The Roots is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// Return To The Roots is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Return To The Roots. If not, see . + +#include "iwEconomicProgress.h" +#include "EventManager.h" +#include "GamePlayer.h" +#include "GlobalGameSettings.h" +#include "Loader.h" +#include "Settings.h" +#include "WindowManager.h" +#include "addons/const_addons.h" +#include "controls/ctrlButton.h" +#include "controls/ctrlOptionGroup.h" +#include "controls/ctrlText.h" +#include "controls/ctrlTextDeepening.h" +#include "iwHelp.h" +#include "network/GameClient.h" +#include "ogl/FontStyle.h" +#include "world/GameWorldBase.h" +#include "world/GameWorldViewer.h" +#include "gameData/const_gui_ids.h" + +iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) + : IngameWindow(CGI_ECONOMICPROGRESS, IngameWindow::posLastOrCenter, + Extent(240, 96 + 26 * EconomyModeHandler::numGoodTypesToCollect), _("Economic Progress"), + LOADER.GetImageN("resource", 41)), + gwv(gwv) +{ + const unsigned int textcolor[] = {COLOR_GREEN, COLOR_YELLOW, COLOR_RED}; + + const GameWorldBase& world = gwv.GetWorld(); + + EconomyModeHandler* eH = world.econHandler; + + headline = + AddText(1, DrawPoint(11 + 27, 46), _("Player"), COLOR_GREEN, FontStyle::LEFT | FontStyle::BOTTOM, NormalFont); + + const std::vector& teams = eH->GetTeams(); + unsigned int num_teams = teams.size(); + + // Teamreihenfolge bestimmen (eigenes Team zuerst) + unsigned int mainTeam = 0; + for(unsigned int i = 0; i < teams.size(); i++) + { + if(teams[i].inTeam(gwv.GetPlayer().GetPlayerId())) + { + mainTeam = i; + break; + } + } + teamorder.push_back(mainTeam); + for(unsigned int i = 0; i < teams.size(); i++) + { + if(i != mainTeam) + { + teamorder.push_back(i); + } + } + // Eventuell Fenster vergroessern + if(num_teams > 2) + { + Extent size = this->GetSize(); + size.x += (num_teams - 2) * 63; + this->Resize(size); + } + // Die Warentabelle + const Extent btSize(26, 26); + for(unsigned int i = 0; i < eH->numGoodTypesToCollect; i++) + { + GoodType good = eH->GetTypes()[i]; + + const DrawPoint btPos(11, 48 + 26 * i); + AddImage(100 + i, btPos + btSize / 2, LOADER.GetMapImageN(2298), _(WARE_NAMES[good])); + const DrawPoint warePos = btPos + btSize / 2; + AddImage(200 + i, warePos, LOADER.GetMapImageN(WARES_TEX_MAP_OFFSET + good)); + + for(unsigned j = 0; j < 1 + num_teams; j++) + { + const DrawPoint txtPos = btPos + DrawPoint(26 + 63 * j, 0); + AddTextDeepening(300 + 10 * j + i, txtPos, Extent(63, btSize.y), TC_GREY, "?", NormalFont, + textcolor[j < 2 ? j : 2]); + } + } + + // Spielzeit-Fortschrittsanzeige + AddText(2, DrawPoint(11 + 30, 240), _("Percent elapsed:"), COLOR_ORANGE, FontStyle::LEFT | FontStyle::TOP, + NormalFont); + elapsedTime = + AddText(3, DrawPoint(30 + 63 * 3, 240), "%", COLOR_ORANGE, FontStyle::RIGHT | FontStyle::TOP, NormalFont); + + // Hilfe-Button + AddImageButton(25, DrawPoint(11, 235), Extent(26, 26), TC_GREY, LOADER.GetImageN("io", 225), _("Help")); +} + +iwEconomicProgress::~iwEconomicProgress() = default; + +void iwEconomicProgress::Draw_() +{ + IngameWindow::Draw_(); + + // Teamfarben zeichnen + const std::vector& teams = gwv.GetWorld().econHandler->GetTeams(); + for(unsigned int t = 0; t < teams.size(); t++) + { + DrawPoint drawPt = GetDrawPos() + DrawPoint(37 + (t + 1) * 63, 22); + unsigned int height = 24; + unsigned int ystep = height / teams[teamorder[t]].num_players_in_team; + unsigned int ypos = 0; + for(unsigned i = 0; i < gwv.GetWorld().GetNumPlayers(); ++i) + { + if(teams[teamorder[t]].inTeam(i)) + { + if(height - ypos < 2 * ystep) + ystep = height - ypos; + DrawRectangle(Rect(drawPt + DrawPoint(0, ypos), Extent(62, ystep)), gwv.GetWorld().GetPlayer(i).color); + ypos += ystep; + } + } + } +} + +void iwEconomicProgress::Msg_ButtonClick(const unsigned ctrl_id) +{ + switch(ctrl_id) + { + break; + case 25: // Hilfe + { + WINDOWMANAGER.ReplaceWindow( + std::make_unique(_("This window shows the wares that should be collected for " + "the economy mode. Displayed is the current inventory of " + "the player, his team and after game end of the opponent " + "team."))); + } + break; + } +} + +void iwEconomicProgress::Msg_PaintBefore() +{ + IngameWindow::Msg_PaintBefore(); + + const GameWorldBase& world = gwv.GetWorld(); + EconomyModeHandler* eH = world.econHandler; + const std::vector& teams = eH->GetTeams(); + eH->UpdateAmounts(); // make sure the amounts are current + const GamePlayer& mainPlayer = gwv.GetPlayer(); + + for(unsigned int i = 0; i < eH->numGoodTypesToCollect; i++) + { + for(unsigned j = 0; j < 1 + teams.size(); j++) + { + auto* text = GetCtrl(300 + 10 * j + i); + if(j == 0) + { + text->SetText(std::to_string(eH->GetAmount(i, mainPlayer.GetPlayerId()))); + } else if(j == 1 || eH->isOver()) + { + text->SetText(std::to_string(teams[teamorder[j - 1]].teamAmounts[i])); + + // White color at game end to mark good types that the team has won + if(eH->isOver() && teams[teamorder[j - 1]].teamAmounts[i] >= eH->GetMaxTeamAmount(i)) + { + text->SetTextColor(COLOR_WHITE); + } else + { + if(j == 1) + text->SetTextColor(COLOR_YELLOW); + else + text->SetTextColor(COLOR_RED); + } + } else + { + text->SetText("???"); + } + } + } + + // Refresh game progress tracker + unsigned elapsed = 100; + if(!eH->isOver() && eH->GetEndFrame() > 0) + { + elapsed = (100 * world.GetEvMgr().GetCurrentGF()) / eH->GetEndFrame(); + } + elapsedTime->SetText(std::to_string(elapsed) + "%"); +} diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.h b/libs/s25main/ingameWindows/iwEconomicProgress.h new file mode 100644 index 0000000000..0459d10ef1 --- /dev/null +++ b/libs/s25main/ingameWindows/iwEconomicProgress.h @@ -0,0 +1,45 @@ +// Copyright (c) 2005 - 2017 Settlers Freaks (sf-team at siedler25.org) +// +// This file is part of Return To The Roots. +// +// Return To The Roots is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// Return To The Roots is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Return To The Roots. If not, see . + +#pragma once + +#include "../EconomyModeHandler.h" +#include "IngameWindow.h" +#include "gameTypes/StatisticTypes.h" + +class ctrlText; +class GameWorldViewer; + +/// Fenster fuer den Wirtschaftsmodus-Fortschritt +class iwEconomicProgress : public IngameWindow +{ +public: + iwEconomicProgress(const GameWorldViewer& gwv); + ~iwEconomicProgress() override; + +private: + const GameWorldViewer& gwv; + ctrlText* headline; + ctrlText* elapsedTime; + + std::vector teamorder; + + void Draw_() override; + + void Msg_ButtonClick(unsigned ctrl_id) override; + void Msg_PaintBefore() override; +}; diff --git a/libs/s25main/ingameWindows/iwMainMenu.cpp b/libs/s25main/ingameWindows/iwMainMenu.cpp index 7ba180f04e..ef3cd9d789 100644 --- a/libs/s25main/ingameWindows/iwMainMenu.cpp +++ b/libs/s25main/ingameWindows/iwMainMenu.cpp @@ -28,6 +28,7 @@ #include "iwBuildings.h" #include "iwDiplomacy.h" #include "iwDistribution.h" +#include "iwEconomicProgress.h" #include "iwInventory.h" #include "iwMerchandiseStatistics.h" #include "iwMilitary.h" @@ -79,6 +80,13 @@ iwMainMenu::iwMainMenu(GameWorldView& gwv, GameCommandFactory& gcFactory) // Diplomatie (todo: besseres Bild suchen) AddImageButton(11, DrawPoint(68, 166), Extent(53, 44), TC_GREY, LOADER.GetImageN("io", 190), _("Diplomacy")); + if(gwv.GetWorld().econHandler) + { + // Wirtschaftsmodus + AddImageButton(12, DrawPoint(124, 166), Extent(53, 44), TC_GREY, LOADER.GetImageN("io", 196), + _("Economic Progress")); + } + // AI-Debug #ifdef NDEBUG bool enableAIDebug = gwv.GetWorld().GetGGS().isEnabled(AddonId::AI_DEBUG_WINDOW); @@ -164,6 +172,11 @@ void iwMainMenu::Msg_ButtonClick(const unsigned ctrl_id) WINDOWMANAGER.ToggleWindow(std::make_unique(gwv.GetViewer(), gcFactory)); } break; + case 12: // Wirtschaftsmodusfortschritt + { + WINDOWMANAGER.ToggleWindow(std::make_unique(gwv.GetViewer())); + } + break; case 13: // AI Debug { if(auto* wnd = WINDOWMANAGER.FindNonModalWindow(CGI_AI_DEBUG)) diff --git a/libs/s25main/world/GameWorldBase.cpp b/libs/s25main/world/GameWorldBase.cpp index 7d5f156d09..fa088e75d2 100644 --- a/libs/s25main/world/GameWorldBase.cpp +++ b/libs/s25main/world/GameWorldBase.cpp @@ -42,7 +42,10 @@ GameWorldBase::GameWorldBase(std::vector players, const GlobalGameSe gameSettings(gameSettings), em(em), gi(nullptr) {} -GameWorldBase::~GameWorldBase() = default; +GameWorldBase::~GameWorldBase() +{ + delete econHandler; +} void GameWorldBase::Init(const MapExtent& mapSize, DescIdx lt) { diff --git a/libs/s25main/world/GameWorldBase.h b/libs/s25main/world/GameWorldBase.h index 8cf435f5da..5bcc06211c 100644 --- a/libs/s25main/world/GameWorldBase.h +++ b/libs/s25main/world/GameWorldBase.h @@ -17,6 +17,7 @@ #pragma once +#include "EconomyModeHandler.h" #include "buildings/nobBaseMilitary.h" #include "enum_cast.hpp" #include "helpers/OptionalEnum.h" @@ -67,6 +68,11 @@ class GameWorldBase : public World std::vector players; const GlobalGameSettings& gameSettings; EventManager& em; + +public: + EconomyModeHandler* econHandler = nullptr; + +private: std::unique_ptr lua; protected: diff --git a/libs/s25main/world/GameWorldViewer.cpp b/libs/s25main/world/GameWorldViewer.cpp index 8db22de936..5a99ad916e 100644 --- a/libs/s25main/world/GameWorldViewer.cpp +++ b/libs/s25main/world/GameWorldViewer.cpp @@ -111,6 +111,12 @@ Visibility GameWorldViewer::GetVisibility(const MapPoint pt) const if(GAMECLIENT.IsReplayModeOn() && GAMECLIENT.IsReplayFOWDisabled()) return VIS_VISIBLE; + // Im Wirtschaftsmodus und Spiel vorbei? Dann auch alles sichtbar + if(GetWorld().econHandler && GetWorld().econHandler->globalVisibility()) + { + return VIS_VISIBLE; + } + // Spieler schon tot? Dann auch alles sichtbar? if(GetPlayer().IsDefeated()) return VIS_VISIBLE; From 0c7dc4f9e4e9c8ce6e1744208fee1aad335053c8 Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Sat, 28 Nov 2020 14:13:05 +0100 Subject: [PATCH 02/15] Many small code improvements, correct remaining time calculation and display, different approach towards making whole map visible, renamed game length addon. Thanks for the extensive review, @Flamefire. --- libs/s25main/EconomyModeHandler.cpp | 275 +++++++++--------- libs/s25main/EconomyModeHandler.h | 97 +++--- libs/s25main/Game.cpp | 9 +- libs/s25main/GameInterface.h | 3 + libs/s25main/GlobalGameSettings.cpp | 2 +- libs/s25main/SerializedGameData.cpp | 4 +- .../addons/AddonEconomyModeGameLength.h | 47 +++ libs/s25main/addons/AddonGameLength.h | 39 --- libs/s25main/addons/Addons.h | 1 + libs/s25main/addons/const_addons.h | 2 +- libs/s25main/desktops/dskGameInterface.cpp | 8 + libs/s25main/desktops/dskGameInterface.h | 3 + libs/s25main/desktops/dskHostGame.cpp | 4 +- libs/s25main/gameData/GameConsts.h | 2 +- .../ingameWindows/iwEconomicProgress.cpp | 109 ++++--- .../ingameWindows/iwEconomicProgress.h | 12 +- libs/s25main/ingameWindows/iwMainMenu.cpp | 2 +- libs/s25main/network/GameClient.cpp | 2 +- libs/s25main/world/GameWorldBase.cpp | 5 +- libs/s25main/world/GameWorldBase.h | 2 +- libs/s25main/world/GameWorldViewer.cpp | 6 - libs/s25main/world/World.cpp | 12 + libs/s25main/world/World.h | 3 + 23 files changed, 348 insertions(+), 301 deletions(-) create mode 100644 libs/s25main/addons/AddonEconomyModeGameLength.h delete mode 100644 libs/s25main/addons/AddonGameLength.h diff --git a/libs/s25main/EconomyModeHandler.cpp b/libs/s25main/EconomyModeHandler.cpp index d3d27fdfbf..7e411e4aaf 100644 --- a/libs/s25main/EconomyModeHandler.cpp +++ b/libs/s25main/EconomyModeHandler.cpp @@ -22,6 +22,7 @@ #include "GamePlayer.h" #include "GlobalGameSettings.h" #include "SerializedGameData.h" +#include "helpers/containerUtils.h" #include "random/Random.h" #include "world/GameWorld.h" #include "world/GameWorldGame.h" @@ -29,109 +30,107 @@ #include "gameData/JobConsts.h" #include -auto getPlayerMask = [](unsigned playerId) { return 1u << playerId; }; - -GoodType types_less[]{ - /* 1 */ GD_TONGS, // Zange - /* 2 */ GD_HAMMER, // Hammer - /* 3 */ GD_AXE, // Axt - /* 4 */ GD_SAW, // Saege - /* 5 */ GD_PICKAXE, // Spitzhacke - /* 6 */ GD_SHOVEL, // Schaufel - /* 7 */ GD_CRUCIBLE, // Schmelztiegel - /* 8 */ GD_RODANDLINE, // Angel - /* 9 */ GD_SCYTHE, // Sense - /* 12 */ GD_CLEAVER, // Beil - /* 13 */ GD_ROLLINGPIN, // Nudelholz - /* 14 */ GD_BOW, // Bogen +std::array specialGoodPool = { + /* 1 */ GD_TONGS, + /* 2 */ GD_HAMMER, + /* 3 */ GD_AXE, + /* 4 */ GD_SAW, + /* 5 */ GD_PICKAXE, + /* 6 */ GD_SHOVEL, + /* 7 */ GD_CRUCIBLE, + /* 8 */ GD_RODANDLINE, + /* 9 */ GD_SCYTHE, + /* 12 */ GD_CLEAVER, + /* 13 */ GD_ROLLINGPIN, + /* 14 */ GD_BOW, }; -GoodType types_more[]{ - /* 0 */ GD_BEER, // Bier - /* 11 */ GD_WATER, // Wasser - /* 15 */ GD_BOAT, // Boot - /* 16 */ GD_SWORD, // Schwert - /* 17 */ GD_IRON, // Eisen - /* 18 */ GD_FLOUR, // Mehl - /* 19 */ GD_FISH, // Fisch - /* 20 */ GD_BREAD, // Brot - /* 22 */ GD_WOOD, // Holz - /* 23 */ GD_BOARDS, // Bretter - /* 24 */ GD_STONES, // Steine - /* 27 */ GD_GRAIN, // Getreide - /* 28 */ GD_COINS, // Mnzen - /* 29 */ GD_GOLD, // Gold - /* 30 */ GD_IRONORE, // Eisenerz - /* 31 */ GD_COAL, // Kohle - /* 32 */ GD_MEAT, // Fleisch - /* 33 */ GD_HAM, // Schinken ( Schwein ) +std::array commonGoodPool = { + /* 0 */ GD_BEER, + /* 11 */ GD_WATER, + /* 15 */ GD_BOAT, + /* 16 */ GD_SWORD, + /* 17 */ GD_IRON, + /* 18 */ GD_FLOUR, + /* 19 */ GD_FISH, + /* 20 */ GD_BREAD, + /* 22 */ GD_WOOD, + /* 23 */ GD_BOARDS, + /* 24 */ GD_STONES, + /* 27 */ GD_GRAIN, + /* 28 */ GD_COINS, + /* 29 */ GD_GOLD, + /* 30 */ GD_IRONORE, + /* 31 */ GD_COAL, + /* 32 */ GD_MEAT, + /* 33 */ GD_HAM, }; -EconomyModeHandler::EconomyModeHandler(unsigned end_frame) : end_frame(end_frame), last_updated(0) +const unsigned int numGoodTypesToCollect = 7; + +EconomyModeHandler::EconomyModeHandler(unsigned endFrame) : endFrame(endFrame), gfLastUpdated(0) { - constexpr unsigned num_types_less = sizeof(types_less) / sizeof(types_less[0]); - constexpr unsigned num_types_more = sizeof(types_more) / sizeof(types_more[0]); + // Randomly determine *numGoodTypesToCollect* many good types, one of which is a special good (=tool) - // Randomly determine *numGoodTypesToCollect* many good types, one of which is a tool - unsigned int types_found = 0; + static_assert(numGoodTypesToCollect > 0, "There have to be goods to be collected"); + static_assert(commonGoodPool.size() >= numGoodTypesToCollect - 1, "There have to be enough commond goods"); + static_assert(specialGoodPool.size() >= 1, "There has to be at least 1 special good"); + goodsToCollect.clear(); + goodsToCollect.resize(numGoodTypesToCollect); + auto nextSlot = begin(goodsToCollect); - while(types_found < numGoodTypesToCollect - 1) + while(nextSlot != end(goodsToCollect) - 1) { - GoodType next_type = types_more[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), num_types_more)]; - unsigned i = 0; - for(; i < types_found; i++) + GoodType nextGoodType = commonGoodPool[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), commonGoodPool.size())]; + if(std::find(begin(goodsToCollect), nextSlot, nextGoodType) == nextSlot) { - if(types[i] == next_type) - { - break; - } - } - if(i == types_found) - { - types[types_found] = next_type; - types_found++; + *nextSlot = nextGoodType; + nextSlot++; } } - types[numGoodTypesToCollect - 1] = types_less[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), num_types_less)]; - types_found++; + *nextSlot = specialGoodPool[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), specialGoodPool.size())]; - if(end_frame > 0) - event = GetEvMgr().AddEvent(this, end_frame); - else - event = nullptr; + // Schedule end game event and trust the event manager to keep track of it + if(!isInfinite()) + GetEvMgr().AddEvent(this, endFrame); + + // Find and set up Teams and trackers + DetermineTeams(); + amountsThePlayersCollected.resize(goodsToCollect.size()); + maxAmountsATeamCollected.resize(goodsToCollect.size()); // Send Mission Goal for(unsigned p = 0; p < gwg->GetNumPlayers(); ++p) { - std::string goaltext = _("Economy Mode: Collect as much as you can of the following good types: "); + std::string goalText = _("Economy Mode: Collect as much as you can of the following good types: "); for(unsigned i = 0; i < numGoodTypesToCollect; i++) { if(i > 0) - goaltext += ", "; - goaltext += _(WARE_NAMES[types[i]]); + goalText += ", "; + goalText += _(WARE_NAMES[goodsToCollect[i]]); } - goaltext += ". "; - goaltext += _("Tools in the hands of workers are also counted. So are weapons, and beer, that soldiers have in " + goalText += ". "; + goalText += _("Tools in the hands of workers are also counted. So are weapons, and beer, that soldiers have in " "use. For an updating tally of the collected goods see the economic progress window."); - gwg->GetPostMgr().SetMissionGoal(p, goaltext); + gwg->GetPostMgr().SetMissionGoal(p, goalText); } } EconomyModeHandler::EconomyModeHandler(SerializedGameData& sgd, unsigned objId) - : GameObject(sgd, objId), end_frame(sgd.PopUnsignedInt()), last_updated(0) + : GameObject(sgd, objId), endFrame(sgd.PopUnsignedInt()), gfLastUpdated(0) { - if(!isOver()) - { - event = sgd.PopEvent(); - } else - { - event = nullptr; - } - for(auto& type : types) + sgd.PopContainer(goodsToCollect); + + std::vector teamBitMasks; + sgd.PopContainer(teamBitMasks); + for(auto& teamMask : teamBitMasks) { - type = (GoodType)sgd.PopUnsignedChar(); + economyModeTeams.emplace_back(teamMask, goodsToCollect.size()); } + + amountsThePlayersCollected.resize(goodsToCollect.size()); + maxAmountsATeamCollected.resize(goodsToCollect.size()); } void EconomyModeHandler::Destroy() {} @@ -139,56 +138,54 @@ void EconomyModeHandler::Destroy() {} /// Serialisierungsfunktion void EconomyModeHandler::Serialize(SerializedGameData& sgd) const { - sgd.PushUnsignedInt(end_frame); - if(!isOver()) + sgd.PushUnsignedInt(endFrame); + sgd.PushContainer(goodsToCollect); + std::vector teamBitMasks; + for(const EconomyModeHandler::EconTeam& curTeam : economyModeTeams) { - sgd.PushEvent(event); - } - for(auto type : types) - { - sgd.PushUnsignedChar(type); + teamBitMasks.push_back(curTeam.playersInTeam.to_ulong()); } + sgd.PushContainer(teamBitMasks); } -void EconomyModeHandler::FindTeams() +void EconomyModeHandler::DetermineTeams() { // If we already determined who is in a team with whom skip this. For the economy mode we only count teams at game // start - if(!teams.empty()) + if(!economyModeTeams.empty()) return; for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) { if(gwg->GetPlayer(i).isUsed()) { - bool found_team = false; - for(const auto& team : teams) + bool foundTeam = false; + for(const auto& team : economyModeTeams) { if(team.inTeam(i)) { - found_team = true; + foundTeam = true; break; } } - if(!found_team) + if(!foundTeam) { const GamePlayer& player = gwg->GetPlayer(i); - unsigned new_team = getPlayerMask(i); - unsigned num_players_in_team = 1; + std::bitset newTeam; + newTeam.set(i); for(unsigned j = i + 1; j < gwg->GetNumPlayers(); ++j) { if(gwg->GetPlayer(j).isUsed() && player.IsAlly(j)) { - num_players_in_team++; - new_team = new_team | getPlayerMask(j); + newTeam.set(j); } } - teams.emplace_back(new_team, num_players_in_team); + economyModeTeams.emplace_back(newTeam, goodsToCollect.size()); } } } } -unsigned int EconomyModeHandler::SumGood(GoodType good, const Inventory& Inventory) +unsigned int EconomyModeHandler::SumUpGood(GoodType good, const Inventory& Inventory) { unsigned int retVal = Inventory.goods[good]; @@ -196,7 +193,7 @@ unsigned int EconomyModeHandler::SumGood(GoodType good, const Inventory& Invento for(unsigned int j = 0; j < NUM_JOB_TYPES; j++) { boost::optional tool = JOB_CONSTS[(Job)j].tool; - if(tool && tool == good) + if(tool == good) { retVal += Inventory.people[j]; } @@ -216,7 +213,7 @@ unsigned int EconomyModeHandler::SumGood(GoodType good, const Inventory& Invento void EconomyModeHandler::UpdateAmounts() { // Return if the game is over or we already updated the amounts this game frame - if((isOver() && end_frame != 0) || last_updated == GetEvMgr().GetCurrentGF()) + if(isOver() || gfLastUpdated == GetEvMgr().GetCurrentGF()) { return; } @@ -226,61 +223,54 @@ void EconomyModeHandler::UpdateAmounts() { const GamePlayer& player = gwg->GetPlayer(i); Inventory playerInventory = player.GetInventory(); - for(unsigned int g = 0; g < numGoodTypesToCollect; g++) + for(unsigned int g = 0; g < goodsToCollect.size(); g++) { - amounts[g][i] = SumGood(types[g], playerInventory); + amountsThePlayersCollected[g][i] = SumUpGood(goodsToCollect[g], playerInventory); } } // Compute Teams - FindTeams(); + DetermineTeams(); // Compute the amounts for the teams - for(unsigned int& maxTeamAmount : maxTeamAmounts) - { - maxTeamAmount = 0; - } - - for(auto& team : teams) + std::fill(maxAmountsATeamCollected.begin(), maxAmountsATeamCollected.end(), 0); + for(auto& team : economyModeTeams) { - for(unsigned int& teamAmount : team.teamAmounts) - { - teamAmount = 0; - } + std::fill(team.amountsTheTeamCollected.begin(), team.amountsTheTeamCollected.end(), 0); for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) { if(team.inTeam(i)) { - for(unsigned int g = 0; g < numGoodTypesToCollect; g++) + for(unsigned int g = 0; g < goodsToCollect.size(); g++) { - team.teamAmounts[g] += GetAmount(g, i); - if(team.teamAmounts[g] > maxTeamAmounts[g]) + team.amountsTheTeamCollected[g] += GetAmount(g, i); + if(team.amountsTheTeamCollected[g] > maxAmountsATeamCollected[g]) { - maxTeamAmounts[g] = team.teamAmounts[g]; + maxAmountsATeamCollected[g] = team.amountsTheTeamCollected[g]; } } } } } // Determine the leading teams for each good type and determine how many good type wins is the maximum. - mostWins = 0; - for(auto& team : teams) + mostGoodTypeWins = 0; + for(auto& team : economyModeTeams) { - team.teamWins = 0; - for(unsigned int g = 0; g < numGoodTypesToCollect; g++) + team.goodTypeWins = 0; + for(unsigned int g = 0; g < goodsToCollect.size(); g++) { - if(team.teamAmounts[g] >= maxTeamAmounts[g]) + if(team.amountsTheTeamCollected[g] >= maxAmountsATeamCollected[g]) { - team.teamWins++; - if(team.teamWins > mostWins) + team.goodTypeWins++; + if(team.goodTypeWins > mostGoodTypeWins) { - mostWins = team.teamWins; + mostGoodTypeWins = team.goodTypeWins; } } } } - last_updated = GetEvMgr().GetCurrentGF(); + gfLastUpdated = GetEvMgr().GetCurrentGF(); } void EconomyModeHandler::HandleEvent(const unsigned) @@ -295,50 +285,47 @@ void EconomyModeHandler::HandleEvent(const unsigned) // Update one last time UpdateAmounts(); - // Determine mask of all players in teams with the most good type wins - unsigned bestMask = 0; - unsigned int numWinners = 0; - for(auto& team : teams) + // Determine bitmask of all players in teams with the most good type wins + std::bitset bestMask; + for(auto& team : economyModeTeams) { - if(team.teamWins == mostWins) + if(team.goodTypeWins == mostGoodTypeWins) { - bestMask = bestMask | team.mask; - numWinners += team.num_players_in_team; + bestMask |= team.playersInTeam; } } // Let players know who won - if(bestMask && numWinners != 1) - gwg->GetGameInterface()->GI_TeamWinner(bestMask); + if(bestMask.count() > 1) + gwg->GetGameInterface()->GI_TeamWinner(bestMask.to_ulong()); else for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) { - if(bestMask & getPlayerMask(i)) + if(bestMask[i]) { gwg->GetGameInterface()->GI_Winner(i); } } - // Call function to recalculcate visibilities - for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) - { - gwg->GetGameInterface()->GI_TreatyOfAllianceChanged(i); // TODO: Is this abuse? Should we rename that function? - } - event = nullptr; + gwg->MakeWholeMapVisibleForAllPlayers(); + gwg->GetGameInterface()->GI_UpdateMapVisibility(); } -bool EconomyModeHandler::globalVisibility() const +bool EconomyModeHandler::isOver() const { - return gwg->GetGGS().objective == GO_ECONOMYMODE && gwg->GetEvMgr().GetCurrentGF() >= end_frame - && GetEndFrame() > 0; + return gwg->GetGGS().objective == GO_ECONOMYMODE && !isInfinite() && endFrame < GetEvMgr().GetCurrentGF(); } -bool EconomyModeHandler::isOver() const +EconomyModeHandler::EconTeam::EconTeam(SerializedGameData& sgd, unsigned int numGoodTypesToCollect) + : playersInTeam(sgd.PopSignedInt()), amountsTheTeamCollected(numGoodTypesToCollect, 0), goodTypeWins(0) +{} + +void EconomyModeHandler::EconTeam::Serialize(SerializedGameData& sgd) const { - return gwg->GetGGS().objective == GO_ECONOMYMODE && end_frame < GetEvMgr().GetCurrentGF(); + sgd.PushUnsignedInt(playersInTeam.to_ulong()); } -bool EconomyModeHandler::econTeam::inTeam(unsigned int playerId) const +bool EconomyModeHandler::EconTeam::inTeam(unsigned int playerId) const { - return mask & getPlayerMask(playerId); + return playersInTeam[playerId]; } diff --git a/libs/s25main/EconomyModeHandler.h b/libs/s25main/EconomyModeHandler.h index 9471fafc8a..74dc5a1ab3 100644 --- a/libs/s25main/EconomyModeHandler.h +++ b/libs/s25main/EconomyModeHandler.h @@ -5,6 +5,7 @@ #include "gameTypes/JobTypes.h" #include "gameData/MaxPlayers.h" +#include #include class SerializedGameData; @@ -14,49 +15,58 @@ class GameEvent; class EconomyModeHandler : public GameObject { public: - static const unsigned int numGoodTypesToCollect = 7; - - struct econTeam + // Object to hold data on the teams competing in the economy mode + struct EconTeam { - unsigned mask; - unsigned teamAmounts[numGoodTypesToCollect]; - unsigned num_players_in_team; - unsigned teamWins; - - econTeam(unsigned mask, unsigned num_players_in_team) noexcept - : mask(mask), num_players_in_team(num_players_in_team) - { - for(unsigned int& teamAmount : teamAmounts) - { - teamAmount = 0; - } - teamWins = 0; - } + // The players that are in the team + std::bitset playersInTeam; + + // The amounts the team collected for each good + std::vector amountsTheTeamCollected; + + // The number of good types the team is the leader in + unsigned goodTypeWins; + + EconTeam(std::bitset playersInTeam, unsigned int numGoodTypesToCollect) noexcept + : playersInTeam(playersInTeam), amountsTheTeamCollected(numGoodTypesToCollect, 0), goodTypeWins(0) + {} + + EconTeam(SerializedGameData& sgd, unsigned int numGoodTypesToCollect); + + void Serialize(SerializedGameData& sgd) const; + // Returns true if the player is in the team bool inTeam(unsigned int playerId) const; }; private: /// Frame in which the game is going to end - unsigned end_frame; - /// End game Event - const GameEvent* event; + unsigned endFrame; /// Good types to collect - GoodType types[numGoodTypesToCollect]; + std::vector goodsToCollect; // Data for economy mode progress tracking - std::vector teams; - unsigned maxTeamAmounts[numGoodTypesToCollect] = {0}; - unsigned int mostWins = 0; - unsigned amounts[numGoodTypesToCollect][MAX_PLAYERS]; - unsigned last_updated; + std::vector economyModeTeams; + // Maximal amounts collected any team separately for each good type to collect + std::vector maxAmountsATeamCollected; + // Amounts collected by each player for each good type to collect + std::vector> amountsThePlayersCollected; - unsigned int SumGood(GoodType good, const Inventory& Inventory); + // Number of Good types the best team is currently leading in + unsigned int mostGoodTypeWins; - void FindTeams(); + // Gameframe in which the economy mode progress trackingd data has been updated last + unsigned gfLastUpdated; + + // Sum up all forms of the given good in the inventory (tools, weapons and beer are also counted in the hands of + // workers and soldiers) + unsigned int SumUpGood(GoodType good, const Inventory& Inventory); + + // Determine the teams for the economy mode + void DetermineTeams(); public: - EconomyModeHandler(unsigned end_frame); + EconomyModeHandler(unsigned endFrame); EconomyModeHandler(SerializedGameData& sgd, unsigned objId); @@ -68,26 +78,35 @@ class EconomyModeHandler : public GameObject /// Event-Handler void HandleEvent(unsigned id) override; - const std::vector& GetTeams() + // Return vector of the teams (and their collected amounts) + const std::vector& GetTeams() { - FindTeams(); - return teams; + DetermineTeams(); + return economyModeTeams; } - // Methods to update the ware trackers + // Method to update the ware trackers void UpdateAmounts(); - unsigned int GetAmount(unsigned int i, unsigned int player) { return amounts[i][player]; } - unsigned int GetMaxTeamAmount(unsigned int i) { return maxTeamAmounts[i]; } - unsigned GetEndFrame() const { return end_frame; } + // Get the amounts collected by a player + unsigned int GetAmount(unsigned int goodNumber, unsigned int playerId) + { + return amountsThePlayersCollected[goodNumber][playerId]; + } + + // Get the amount of good the leading team (with regards to that good) has collected + unsigned int GetMaxTeamAmount(unsigned int goodNumber) { return maxAmountsATeamCollected[goodNumber]; } - // Check if the game has ended, so everything should be visible - bool globalVisibility() const; + // Get Game frame in which the economy mode winners will be determined + unsigned GetEndFrame() const { return endFrame; } // Return the good types to collect - GoodType* GetTypes() { return types; } + const std::vector& GetGoodTypesToCollect() const { return goodsToCollect; } GO_Type GetGOT() const override { return GOT_ECONOMYMODEHANDLER; } + // Miscellaneous status checks bool isOver() const; + bool isInfinite() const { return endFrame == 0; } + bool showAllTeamAmounts() const { return isOver() || isInfinite(); } }; diff --git a/libs/s25main/Game.cpp b/libs/s25main/Game.cpp index 269ffe6d61..315df36db7 100644 --- a/libs/s25main/Game.cpp +++ b/libs/s25main/Game.cpp @@ -19,10 +19,11 @@ #include "EventManager.h" #include "GameInterface.h" #include "GamePlayer.h" -#include "addons/AddonGameLength.h" +#include "addons/AddonEconomyModeGameLength.h" #include "addons/const_addons.h" #include "ai/AIPlayer.h" #include "lua/LuaInterfaceGame.h" +#include "network/GameClient.h" #include Game::Game(const GlobalGameSettings& settings, unsigned startGF, const std::vector& players) @@ -46,9 +47,9 @@ void Game::Start(bool startFromSave) { if(ggs_.objective == GO_ECONOMYMODE) { - unsigned int selection = ggs_.getSelection(AddonId::GAME_LENGTH); - world_.econHandler = - new EconomyModeHandler(AddonGameLengthList[selection] / 50); // 50 is the assumed game frame length + unsigned int selection = ggs_.getSelection(AddonId::ECONOMY_MODE_GAME_LENGTH); + world_.econHandler = std::make_unique( + std::chrono::minutes(AddonEconomyModeGameLengthList[selection]) / GAMECLIENT.GetGFLength()); } StatisticStep(); } diff --git a/libs/s25main/GameInterface.h b/libs/s25main/GameInterface.h index d2363a3060..32d4b0f491 100644 --- a/libs/s25main/GameInterface.h +++ b/libs/s25main/GameInterface.h @@ -37,6 +37,9 @@ class GameInterface /// Bündnisvertrag wurde abgeschlossen oder abgebrochen --> Minimap updaten virtual void GI_TreatyOfAllianceChanged(unsigned playerId) = 0; + /// Update minimap and colors for whole map + virtual void GI_UpdateMapVisibility() {} + virtual void GI_Winner(unsigned playerId) = 0; virtual void GI_TeamWinner(unsigned playerId) = 0; diff --git a/libs/s25main/GlobalGameSettings.cpp b/libs/s25main/GlobalGameSettings.cpp index b2363730b4..acba94a775 100644 --- a/libs/s25main/GlobalGameSettings.cpp +++ b/libs/s25main/GlobalGameSettings.cpp @@ -115,7 +115,7 @@ void GlobalGameSettings::registerAllAddons() registerAddon(std::make_unique()); registerAddon(std::make_unique()); - registerAddon(std::make_unique()); + registerAddon(std::make_unique()); } void GlobalGameSettings::resetAddons() diff --git a/libs/s25main/SerializedGameData.cpp b/libs/s25main/SerializedGameData.cpp index ce7b19a3c8..77965af108 100644 --- a/libs/s25main/SerializedGameData.cpp +++ b/libs/s25main/SerializedGameData.cpp @@ -232,7 +232,7 @@ void SerializedGameData::MakeSnapshot(const std::shared_ptr& game) writeEm->Serialize(*this); if(game->ggs_.objective == GO_ECONOMYMODE) { - PushObject(gw.econHandler, true); + PushObject(gw.econHandler.get(), true); } // Spieler serialisieren for(unsigned i = 0; i < gw.GetNumPlayers(); ++i) @@ -271,7 +271,7 @@ void SerializedGameData::ReadSnapshot(const std::shared_ptr& game, ILocalG em->Deserialize(*this); if(game->ggs_.objective == GO_ECONOMYMODE) { - gw.econHandler = PopObject(GOT_ECONOMYMODEHANDLER); + gw.econHandler.reset(PopObject(GOT_ECONOMYMODEHANDLER)); } for(unsigned i = 0; i < gw.GetNumPlayers(); ++i) diff --git a/libs/s25main/addons/AddonEconomyModeGameLength.h b/libs/s25main/addons/AddonEconomyModeGameLength.h new file mode 100644 index 0000000000..982c33d91a --- /dev/null +++ b/libs/s25main/addons/AddonEconomyModeGameLength.h @@ -0,0 +1,47 @@ +// Copyright (c) 2005 - 2020 Settlers Freaks (sf-team at siedler25.org) +// +// This file is part of Return To The Roots. +// +// Return To The Roots is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// Return To The Roots is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Return To The Roots. If not, see . + +#pragma once + +#include "AddonList.h" +#include "mygettext/mygettext.h" +#include + +/** + * Addon allows users to adjust the game length (for Economy Mode) + */ +const unsigned int AddonEconomyModeGameLengthList[] = {0, 15, 30, 60, 90, 120, 150, 180, 240, 480}; // length in minutes + +class AddonEconomyModeGameLength : public AddonList +{ +public: + AddonEconomyModeGameLength() + : AddonList(AddonId::ECONOMY_MODE_GAME_LENGTH, AddonGroup::Economy | AddonGroup::GamePlay, + _("Economy Mode: Game Length"), + _("Adjust the time after which the economy mode victory condition is checked."), + {_("unlimited"), (boost::format(_("%1%min")) % AddonEconomyModeGameLengthList[1]).str(), + (boost::format(_("%1%min")) % AddonEconomyModeGameLengthList[2]).str(), + (boost::format(_("%1%min")) % AddonEconomyModeGameLengthList[3]).str(), + (boost::format(_("%1%min")) % AddonEconomyModeGameLengthList[4]).str(), + (boost::format(_("%1%min")) % AddonEconomyModeGameLengthList[5]).str(), + (boost::format(_("%1%min")) % AddonEconomyModeGameLengthList[6]).str(), + (boost::format(_("%1%min")) % AddonEconomyModeGameLengthList[7]).str(), + (boost::format(_("%1%min")) % AddonEconomyModeGameLengthList[8]).str(), + (boost::format(_("%1%min")) % AddonEconomyModeGameLengthList[9]).str()}, + 5) + {} +}; diff --git a/libs/s25main/addons/AddonGameLength.h b/libs/s25main/addons/AddonGameLength.h deleted file mode 100644 index fa82f1cc24..0000000000 --- a/libs/s25main/addons/AddonGameLength.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2005 - 2020 Settlers Freaks (sf-team at siedler25.org) -// -// This file is part of Return To The Roots. -// -// Return To The Roots is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 2 of the License, or -// (at your option) any later version. -// -// Return To The Roots is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Return To The Roots. If not, see . - -#pragma once - -#include "AddonList.h" -#include "mygettext/mygettext.h" - -/** - * Addon allows users to adjust the game length (for Economy Mode) - */ -const unsigned int AddonGameLengthList[] = {0, 15 * 60000, 30 * 60000, 60 * 60000, 90 * 60000, 120 * 60000, - 150 * 60000, 180 * 60000, 240 * 60000, 480 * 60000}; // length in ms - -class AddonGameLength : public AddonList -{ -public: - AddonGameLength() - : AddonList(AddonId::GAME_LENGTH, AddonGroup::Economy | AddonGroup::GamePlay, _("Game Length (Economy Mode)"), - _("Adjust the time after which the economy mode victory condition is checked. Reference times are " - "on fast speed."), - {_("unlimited"), _("~15min"), _("~30min"), _("~60min"), _("~90min"), _("~120min"), _("~150min"), - _("~180min"), _("~240min"), _("~480min")}) - {} -}; diff --git a/libs/s25main/addons/Addons.h b/libs/s25main/addons/Addons.h index 4b62952388..b9f7fce9b9 100644 --- a/libs/s25main/addons/Addons.h +++ b/libs/s25main/addons/Addons.h @@ -69,4 +69,5 @@ #include "addons/AddonFrontierDistanceReachable.h" #include "addons/AddonDurableGeologistSigns.h" +#include "addons/AddonEconomyModeGameLength.h" #include "addons/AddonPeacefulMode.h" diff --git a/libs/s25main/addons/const_addons.h b/libs/s25main/addons/const_addons.h index 1006a88b9c..8db1e73517 100644 --- a/libs/s25main/addons/const_addons.h +++ b/libs/s25main/addons/const_addons.h @@ -81,7 +81,7 @@ ENUM_WITH_STRING(AddonId, LIMIT_CATAPULTS = 0x00000000, INEXHAUSTIBLE_MINES = 0x FRONTIER_DISTANCE_REACHABLE = 0x00D0000, COINS_CAPTURED_BLD = 0x00D0001, DEMOLISH_BLD_WO_RES = 0x00D0002, - PEACEFULMODE = 0x00E0000, DURABLE_GEOLOGIST_SIGNS = 0x00E0001) + PEACEFULMODE = 0x00E0000, DURABLE_GEOLOGIST_SIGNS = 0x00E0001, ECONOMY_MODE_GAME_LENGTH = 0x00E0002) //-V:AddonId:801 enum class AddonGroup : unsigned diff --git a/libs/s25main/desktops/dskGameInterface.cpp b/libs/s25main/desktops/dskGameInterface.cpp index 6a9da1da31..a7d1f1fef0 100644 --- a/libs/s25main/desktops/dskGameInterface.cpp +++ b/libs/s25main/desktops/dskGameInterface.cpp @@ -1234,6 +1234,14 @@ void dskGameInterface::GI_UpdateMinimap(const MapPoint pt) minimap.UpdateNode(pt); } +void dskGameInterface::GI_UpdateMapVisibility() +{ + // recalculate visibility + worldViewer.RecalcAllColors(); + // update minimap + minimap.UpdateAll(); +} + /** * Bündnisvertrag wurde abgeschlossen oder abgebrochen --> Minimap updaten */ diff --git a/libs/s25main/desktops/dskGameInterface.h b/libs/s25main/desktops/dskGameInterface.h index 72219520f2..58f36e6932 100644 --- a/libs/s25main/desktops/dskGameInterface.h +++ b/libs/s25main/desktops/dskGameInterface.h @@ -85,6 +85,9 @@ class dskGameInterface : void GI_PlayerDefeated(unsigned playerId) override; /// Es wurde etwas Minimap entscheidendes geändert --> Minimap updaten void GI_UpdateMinimap(MapPoint pt) override; + /// Update minimap and colors for whole map + void GI_UpdateMapVisibility() override; + /// Bündnisvertrag wurde abgeschlossen oder abgebrochen --> Minimap updaten void GI_TreatyOfAllianceChanged(unsigned playerId) override; void GI_Winner(unsigned playerId) override; diff --git a/libs/s25main/desktops/dskHostGame.cpp b/libs/s25main/desktops/dskHostGame.cpp index 55b15ef800..a3c4917468 100644 --- a/libs/s25main/desktops/dskHostGame.cpp +++ b/libs/s25main/desktops/dskHostGame.cpp @@ -769,8 +769,8 @@ void dskHostGame::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult if(mbr == MSR_YES) { gameLobby->getSettings().setSelection(AddonId::PEACEFUL, true); - if(gameLobby->getSettings().getSelection(AddonId::GAME_LENGTH) == 0) - gameLobby->getSettings().setSelection(AddonId::GAME_LENGTH, 5); + if(gameLobby->getSettings().getSelection(AddonId::ECONOMY_MODE_GAME_LENGTH) == 0) + gameLobby->getSettings().setSelection(AddonId::ECONOMY_MODE_GAME_LENGTH, 5); gameLobby->getSettings().setSelection(AddonId::NO_COINS_DEFAULT, true); gameLobby->getSettings().setSelection(AddonId::LIMIT_CATAPULTS, 2); GetCtrl(20)->SetCheck(true); // Lockteams; diff --git a/libs/s25main/gameData/GameConsts.h b/libs/s25main/gameData/GameConsts.h index 02487f0bd8..1eb99eb8c9 100644 --- a/libs/s25main/gameData/GameConsts.h +++ b/libs/s25main/gameData/GameConsts.h @@ -32,7 +32,7 @@ const unsigned char INVALID_DIR = 0xFF; const unsigned SUPPRESS_UNUSED NO_MAX_LEN = std::numeric_limits::max(); /// Number of "classical" objectives in a friendly match -const unsigned NUM_OBJECTIVES = 3; +const unsigned NUM_OBJECTIVES = 4; /// tournament modes const unsigned NUM_TOURNAMENT_MODESS = 5; const std::array SUPPRESS_UNUSED TOURNAMENT_MODES_DURATION = {30, 60, 90, 120, 240}; diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.cpp b/libs/s25main/ingameWindows/iwEconomicProgress.cpp index 55d83b050b..c10cee412f 100644 --- a/libs/s25main/ingameWindows/iwEconomicProgress.cpp +++ b/libs/s25main/ingameWindows/iwEconomicProgress.cpp @@ -16,6 +16,7 @@ // along with Return To The Roots. If not, see . #include "iwEconomicProgress.h" +#include "EconomyModeHandler.h" #include "EventManager.h" #include "GamePlayer.h" #include "GlobalGameSettings.h" @@ -29,59 +30,60 @@ #include "controls/ctrlTextDeepening.h" #include "iwHelp.h" #include "network/GameClient.h" -#include "ogl/FontStyle.h" #include "world/GameWorldBase.h" #include "world/GameWorldViewer.h" #include "gameData/const_gui_ids.h" iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) - : IngameWindow(CGI_ECONOMICPROGRESS, IngameWindow::posLastOrCenter, - Extent(240, 96 + 26 * EconomyModeHandler::numGoodTypesToCollect), _("Economic Progress"), - LOADER.GetImageN("resource", 41)), + : IngameWindow(CGI_ECONOMICPROGRESS, IngameWindow::posLastOrCenter, Extent(240, 96 + 26 * 7), + _("Economic Progress"), LOADER.GetImageN("resource", 41)), gwv(gwv) { const unsigned int textcolor[] = {COLOR_GREEN, COLOR_YELLOW, COLOR_RED}; const GameWorldBase& world = gwv.GetWorld(); - EconomyModeHandler* eH = world.econHandler; + EconomyModeHandler* eH = world.econHandler.get(); - headline = - AddText(1, DrawPoint(11 + 27, 46), _("Player"), COLOR_GREEN, FontStyle::LEFT | FontStyle::BOTTOM, NormalFont); + const unsigned int numGoodTypesToCollect = eH->GetGoodTypesToCollect().size(); - const std::vector& teams = eH->GetTeams(); - unsigned int num_teams = teams.size(); + AddText(1, DrawPoint(11 + 27, 46), _("Player"), COLOR_GREEN, FontStyle::LEFT | FontStyle::BOTTOM, NormalFont); - // Teamreihenfolge bestimmen (eigenes Team zuerst) + const std::vector& economyModeTeams = eH->GetTeams(); + unsigned int num_teams = economyModeTeams.size(); + + // determine team display order (main player team first) unsigned int mainTeam = 0; - for(unsigned int i = 0; i < teams.size(); i++) + for(unsigned int i = 0; i < economyModeTeams.size(); i++) { - if(teams[i].inTeam(gwv.GetPlayer().GetPlayerId())) + if(economyModeTeams[i].inTeam(gwv.GetPlayer().GetPlayerId())) { mainTeam = i; break; } } - teamorder.push_back(mainTeam); - for(unsigned int i = 0; i < teams.size(); i++) + teamOrder.push_back(mainTeam); + for(unsigned int i = 0; i < economyModeTeams.size(); i++) { if(i != mainTeam) { - teamorder.push_back(i); + teamOrder.push_back(i); } } - // Eventuell Fenster vergroessern - if(num_teams > 2) + // potentially resize window + if(num_teams > 2 || numGoodTypesToCollect != 7) { Extent size = this->GetSize(); - size.x += (num_teams - 2) * 63; + size.x += (std::max((int)num_teams, 2) - 2) * 63; + size.y += ((int)numGoodTypesToCollect - 7) * 26; this->Resize(size); } - // Die Warentabelle + // the goods table const Extent btSize(26, 26); - for(unsigned int i = 0; i < eH->numGoodTypesToCollect; i++) + auto& goodTypes = eH->GetGoodTypesToCollect(); + for(unsigned int i = 0; i < goodTypes.size(); i++) { - GoodType good = eH->GetTypes()[i]; + GoodType good = eH->GetGoodTypesToCollect()[i]; const DrawPoint btPos(11, 48 + 26 * i); AddImage(100 + i, btPos + btSize / 2, LOADER.GetMapImageN(2298), _(WARE_NAMES[good])); @@ -96,13 +98,16 @@ iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) } } - // Spielzeit-Fortschrittsanzeige - AddText(2, DrawPoint(11 + 30, 240), _("Percent elapsed:"), COLOR_ORANGE, FontStyle::LEFT | FontStyle::TOP, - NormalFont); - elapsedTime = - AddText(3, DrawPoint(30 + 63 * 3, 240), "%", COLOR_ORANGE, FontStyle::RIGHT | FontStyle::TOP, NormalFont); + if(!eH->isInfinite()) + { + // game progress display + AddText(2, DrawPoint(11 + 30, 240), _("Time remaining:"), COLOR_ORANGE, FontStyle::LEFT | FontStyle::TOP, + NormalFont); + txtRemainingTime = + AddText(3, DrawPoint(30 + 63 * 3, 240), "%", COLOR_ORANGE, FontStyle::RIGHT | FontStyle::TOP, NormalFont); + } - // Hilfe-Button + // help button AddImageButton(25, DrawPoint(11, 235), Extent(26, 26), TC_GREY, LOADER.GetImageN("io", 225), _("Help")); } @@ -112,17 +117,17 @@ void iwEconomicProgress::Draw_() { IngameWindow::Draw_(); - // Teamfarben zeichnen - const std::vector& teams = gwv.GetWorld().econHandler->GetTeams(); - for(unsigned int t = 0; t < teams.size(); t++) + // draw team colors + const std::vector& economyModeTeams = gwv.GetWorld().econHandler->GetTeams(); + for(unsigned int t = 0; t < economyModeTeams.size(); t++) { DrawPoint drawPt = GetDrawPos() + DrawPoint(37 + (t + 1) * 63, 22); unsigned int height = 24; - unsigned int ystep = height / teams[teamorder[t]].num_players_in_team; + unsigned int ystep = height / economyModeTeams[teamOrder[t]].playersInTeam.count(); unsigned int ypos = 0; for(unsigned i = 0; i < gwv.GetWorld().GetNumPlayers(); ++i) { - if(teams[teamorder[t]].inTeam(i)) + if(economyModeTeams[teamOrder[t]].inTeam(i)) { if(height - ypos < 2 * ystep) ystep = height - ypos; @@ -138,13 +143,13 @@ void iwEconomicProgress::Msg_ButtonClick(const unsigned ctrl_id) switch(ctrl_id) { break; - case 25: // Hilfe + case 25: // help { WINDOWMANAGER.ReplaceWindow( std::make_unique(_("This window shows the wares that should be collected for " "the economy mode. Displayed is the current inventory of " "the player, his team and after game end of the opponent " - "team."))); + "teams."))); } break; } @@ -155,25 +160,29 @@ void iwEconomicProgress::Msg_PaintBefore() IngameWindow::Msg_PaintBefore(); const GameWorldBase& world = gwv.GetWorld(); - EconomyModeHandler* eH = world.econHandler; - const std::vector& teams = eH->GetTeams(); - eH->UpdateAmounts(); // make sure the amounts are current + EconomyModeHandler* eH = world.econHandler.get(); + const std::vector& economyModeTeams = eH->GetTeams(); const GamePlayer& mainPlayer = gwv.GetPlayer(); - for(unsigned int i = 0; i < eH->numGoodTypesToCollect; i++) + // make sure the amounts are current + eH->UpdateAmounts(); + + // update table elements + for(unsigned int i = 0; i < eH->GetGoodTypesToCollect().size(); i++) { - for(unsigned j = 0; j < 1 + teams.size(); j++) + for(unsigned j = 0; j < 1 + economyModeTeams.size(); j++) { auto* text = GetCtrl(300 + 10 * j + i); if(j == 0) { text->SetText(std::to_string(eH->GetAmount(i, mainPlayer.GetPlayerId()))); - } else if(j == 1 || eH->isOver()) + } else if(j == 1 || eH->showAllTeamAmounts()) { - text->SetText(std::to_string(teams[teamorder[j - 1]].teamAmounts[i])); + text->SetText(std::to_string(economyModeTeams[teamOrder[j - 1]].amountsTheTeamCollected[i])); - // White color at game end to mark good types that the team has won - if(eH->isOver() && teams[teamorder[j - 1]].teamAmounts[i] >= eH->GetMaxTeamAmount(i)) + // White color when all teams are shown to mark good types that the team has won or is leading in + if(eH->showAllTeamAmounts() + && economyModeTeams[teamOrder[j - 1]].amountsTheTeamCollected[i] >= eH->GetMaxTeamAmount(i)) { text->SetTextColor(COLOR_WHITE); } else @@ -190,11 +199,15 @@ void iwEconomicProgress::Msg_PaintBefore() } } - // Refresh game progress tracker - unsigned elapsed = 100; - if(!eH->isOver() && eH->GetEndFrame() > 0) + if(!eH->isInfinite()) { - elapsed = (100 * world.GetEvMgr().GetCurrentGF()) / eH->GetEndFrame(); + // Refresh game progress tracker + if(!eH->isOver()) + { + txtRemainingTime->SetText(GAMECLIENT.FormatGFTime(eH->GetEndFrame() - world.GetEvMgr().GetCurrentGF())); + } else + { + txtRemainingTime->SetText(_("0")); + } } - elapsedTime->SetText(std::to_string(elapsed) + "%"); } diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.h b/libs/s25main/ingameWindows/iwEconomicProgress.h index 0459d10ef1..97d8c2497b 100644 --- a/libs/s25main/ingameWindows/iwEconomicProgress.h +++ b/libs/s25main/ingameWindows/iwEconomicProgress.h @@ -1,4 +1,4 @@ -// Copyright (c) 2005 - 2017 Settlers Freaks (sf-team at siedler25.org) +// Copyright (c) 2005 - 2020 Settlers Freaks (sf-team at siedler25.org) // // This file is part of Return To The Roots. // @@ -17,14 +17,12 @@ #pragma once -#include "../EconomyModeHandler.h" #include "IngameWindow.h" -#include "gameTypes/StatisticTypes.h" class ctrlText; class GameWorldViewer; -/// Fenster fuer den Wirtschaftsmodus-Fortschritt +/// Window for displaying the economic mode progress class iwEconomicProgress : public IngameWindow { public: @@ -33,10 +31,10 @@ class iwEconomicProgress : public IngameWindow private: const GameWorldViewer& gwv; - ctrlText* headline; - ctrlText* elapsedTime; + ctrlText* txtRemainingTime; - std::vector teamorder; + // Order in which the teams are displayed + std::vector teamOrder; void Draw_() override; diff --git a/libs/s25main/ingameWindows/iwMainMenu.cpp b/libs/s25main/ingameWindows/iwMainMenu.cpp index ef3cd9d789..0265dc7101 100644 --- a/libs/s25main/ingameWindows/iwMainMenu.cpp +++ b/libs/s25main/ingameWindows/iwMainMenu.cpp @@ -82,7 +82,7 @@ iwMainMenu::iwMainMenu(GameWorldView& gwv, GameCommandFactory& gcFactory) if(gwv.GetWorld().econHandler) { - // Wirtschaftsmodus + // Economy Mode AddImageButton(12, DrawPoint(124, 166), Extent(53, 44), TC_GREY, LOADER.GetImageN("io", 196), _("Economic Progress")); } diff --git a/libs/s25main/network/GameClient.cpp b/libs/s25main/network/GameClient.cpp index 37dba704a8..fa905a3d2e 100644 --- a/libs/s25main/network/GameClient.cpp +++ b/libs/s25main/network/GameClient.cpp @@ -1708,7 +1708,7 @@ std::string GameClient::FormatGFTime(const unsigned gf) const // Angaben rausfiltern hours numHours = duration_cast(numSeconds); numSeconds -= numHours; - minutes numMinutes = duration_cast(numSeconds); + minutes numMinutes = duration_cast(numSeconds); numSeconds -= numMinutes; // ganze Stunden mit dabei? Dann entsprechend anderes format, ansonsten ignorieren wir die einfach diff --git a/libs/s25main/world/GameWorldBase.cpp b/libs/s25main/world/GameWorldBase.cpp index fa088e75d2..7d5f156d09 100644 --- a/libs/s25main/world/GameWorldBase.cpp +++ b/libs/s25main/world/GameWorldBase.cpp @@ -42,10 +42,7 @@ GameWorldBase::GameWorldBase(std::vector players, const GlobalGameSe gameSettings(gameSettings), em(em), gi(nullptr) {} -GameWorldBase::~GameWorldBase() -{ - delete econHandler; -} +GameWorldBase::~GameWorldBase() = default; void GameWorldBase::Init(const MapExtent& mapSize, DescIdx lt) { diff --git a/libs/s25main/world/GameWorldBase.h b/libs/s25main/world/GameWorldBase.h index 5bcc06211c..e16d3f6cb8 100644 --- a/libs/s25main/world/GameWorldBase.h +++ b/libs/s25main/world/GameWorldBase.h @@ -70,7 +70,7 @@ class GameWorldBase : public World EventManager& em; public: - EconomyModeHandler* econHandler = nullptr; + std::unique_ptr econHandler; private: std::unique_ptr lua; diff --git a/libs/s25main/world/GameWorldViewer.cpp b/libs/s25main/world/GameWorldViewer.cpp index 5a99ad916e..8db22de936 100644 --- a/libs/s25main/world/GameWorldViewer.cpp +++ b/libs/s25main/world/GameWorldViewer.cpp @@ -111,12 +111,6 @@ Visibility GameWorldViewer::GetVisibility(const MapPoint pt) const if(GAMECLIENT.IsReplayModeOn() && GAMECLIENT.IsReplayFOWDisabled()) return VIS_VISIBLE; - // Im Wirtschaftsmodus und Spiel vorbei? Dann auch alles sichtbar - if(GetWorld().econHandler && GetWorld().econHandler->globalVisibility()) - { - return VIS_VISIBLE; - } - // Spieler schon tot? Dann auch alles sichtbar? if(GetPlayer().IsDefeated()) return VIS_VISIBLE; diff --git a/libs/s25main/world/World.cpp b/libs/s25main/world/World.cpp index 22b8a063b1..7efe67165c 100644 --- a/libs/s25main/world/World.cpp +++ b/libs/s25main/world/World.cpp @@ -463,3 +463,15 @@ void World::RecalcShadow(const MapPoint pt) shadingS2 = 0; GetNodeInt(pt).shadow = shadingS2; } + +void World::MakeWholeMapVisibleForAllPlayers() +{ + for(auto& mapNode : nodes) + { + for(auto& fowNode : mapNode.fow) + { + fowNode.visibility = VIS_VISIBLE; + deletePtr(fowNode.object); + } + } +} diff --git a/libs/s25main/world/World.h b/libs/s25main/world/World.h index a2d9fc6f2c..4da6d15c1d 100644 --- a/libs/s25main/world/World.h +++ b/libs/s25main/world/World.h @@ -116,6 +116,9 @@ class World : public MapBase void ChangeAltitude(MapPoint pt, unsigned char altitude); + // Make whole map visible with no additional checks or notices + void MakeWholeMapVisibleForAllPlayers(); + /// Checks if the point completely belongs to a player (if false but point itself belongs to player then it is a /// border) if owner is != 0 it checks if the points specific ownership bool IsPlayerTerritory(MapPoint pt, unsigned char owner = 0) const; From 1a1531ac982bd857eda92385bf2b7b6f77927583 Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Sat, 28 Nov 2020 15:35:05 +0100 Subject: [PATCH 03/15] some more of @FlameFire's suggestions --- libs/s25main/EconomyModeHandler.cpp | 105 +++++++++--------- libs/s25main/EconomyModeHandler.h | 14 +-- .../ingameWindows/iwEconomicProgress.cpp | 28 ++--- .../ingameWindows/iwEconomicProgress.h | 2 +- 4 files changed, 75 insertions(+), 74 deletions(-) diff --git a/libs/s25main/EconomyModeHandler.cpp b/libs/s25main/EconomyModeHandler.cpp index 7e411e4aaf..f08833965c 100644 --- a/libs/s25main/EconomyModeHandler.cpp +++ b/libs/s25main/EconomyModeHandler.cpp @@ -29,52 +29,53 @@ #include "gameTypes/JobTypes.h" #include "gameData/JobConsts.h" #include - -std::array specialGoodPool = { - /* 1 */ GD_TONGS, - /* 2 */ GD_HAMMER, - /* 3 */ GD_AXE, - /* 4 */ GD_SAW, - /* 5 */ GD_PICKAXE, - /* 6 */ GD_SHOVEL, - /* 7 */ GD_CRUCIBLE, - /* 8 */ GD_RODANDLINE, - /* 9 */ GD_SCYTHE, - /* 12 */ GD_CLEAVER, - /* 13 */ GD_ROLLINGPIN, - /* 14 */ GD_BOW, -}; - -std::array commonGoodPool = { - /* 0 */ GD_BEER, - /* 11 */ GD_WATER, - /* 15 */ GD_BOAT, - /* 16 */ GD_SWORD, - /* 17 */ GD_IRON, - /* 18 */ GD_FLOUR, - /* 19 */ GD_FISH, - /* 20 */ GD_BREAD, - /* 22 */ GD_WOOD, - /* 23 */ GD_BOARDS, - /* 24 */ GD_STONES, - /* 27 */ GD_GRAIN, - /* 28 */ GD_COINS, - /* 29 */ GD_GOLD, - /* 30 */ GD_IRONORE, - /* 31 */ GD_COAL, - /* 32 */ GD_MEAT, - /* 33 */ GD_HAM, -}; - -const unsigned int numGoodTypesToCollect = 7; +#include EconomyModeHandler::EconomyModeHandler(unsigned endFrame) : endFrame(endFrame), gfLastUpdated(0) { + const std::vector specialGoodPool({ + GD_TONGS, + GD_HAMMER, + GD_AXE, + GD_SAW, + GD_PICKAXE, + GD_SHOVEL, + GD_CRUCIBLE, + GD_RODANDLINE, + GD_SCYTHE, + GD_CLEAVER, + GD_ROLLINGPIN, + GD_BOW, + }); + + const std::vector commonGoodPool({ + GD_BEER, + GD_WATER, + GD_BOAT, + GD_SWORD, + GD_IRON, + GD_FLOUR, + GD_FISH, + GD_BREAD, + GD_WOOD, + GD_BOARDS, + GD_STONES, + GD_GRAIN, + GD_COINS, + GD_GOLD, + GD_IRONORE, + GD_COAL, + GD_MEAT, + GD_HAM, + }); + + constexpr unsigned numGoodTypesToCollect = 7; + // Randomly determine *numGoodTypesToCollect* many good types, one of which is a special good (=tool) static_assert(numGoodTypesToCollect > 0, "There have to be goods to be collected"); - static_assert(commonGoodPool.size() >= numGoodTypesToCollect - 1, "There have to be enough commond goods"); - static_assert(specialGoodPool.size() >= 1, "There has to be at least 1 special good"); + RTTR_Assert(commonGoodPool.size() >= numGoodTypesToCollect - 1); // "There have to be enough commond goods" + RTTR_Assert(specialGoodPool.size() >= 1); // There have to be enough special goods goodsToCollect.clear(); goodsToCollect.resize(numGoodTypesToCollect); auto nextSlot = begin(goodsToCollect); @@ -122,7 +123,7 @@ EconomyModeHandler::EconomyModeHandler(SerializedGameData& sgd, unsigned objId) { sgd.PopContainer(goodsToCollect); - std::vector teamBitMasks; + std::vector teamBitMasks; sgd.PopContainer(teamBitMasks); for(auto& teamMask : teamBitMasks) { @@ -140,7 +141,7 @@ void EconomyModeHandler::Serialize(SerializedGameData& sgd) const { sgd.PushUnsignedInt(endFrame); sgd.PushContainer(goodsToCollect); - std::vector teamBitMasks; + std::vector teamBitMasks; for(const EconomyModeHandler::EconTeam& curTeam : economyModeTeams) { teamBitMasks.push_back(curTeam.playersInTeam.to_ulong()); @@ -161,7 +162,7 @@ void EconomyModeHandler::DetermineTeams() bool foundTeam = false; for(const auto& team : economyModeTeams) { - if(team.inTeam(i)) + if(team.containsPlayer(i)) { foundTeam = true; break; @@ -185,12 +186,12 @@ void EconomyModeHandler::DetermineTeams() } } -unsigned int EconomyModeHandler::SumUpGood(GoodType good, const Inventory& Inventory) +unsigned EconomyModeHandler::SumUpGood(GoodType good, const Inventory& Inventory) { - unsigned int retVal = Inventory.goods[good]; + unsigned retVal = Inventory.goods[good]; // Add the tools used by workers to the good totals - for(unsigned int j = 0; j < NUM_JOB_TYPES; j++) + for(unsigned j = 0; j < NUM_JOB_TYPES; j++) { boost::optional tool = JOB_CONSTS[(Job)j].tool; if(tool == good) @@ -223,7 +224,7 @@ void EconomyModeHandler::UpdateAmounts() { const GamePlayer& player = gwg->GetPlayer(i); Inventory playerInventory = player.GetInventory(); - for(unsigned int g = 0; g < goodsToCollect.size(); g++) + for(unsigned g = 0; g < goodsToCollect.size(); g++) { amountsThePlayersCollected[g][i] = SumUpGood(goodsToCollect[g], playerInventory); } @@ -239,9 +240,9 @@ void EconomyModeHandler::UpdateAmounts() std::fill(team.amountsTheTeamCollected.begin(), team.amountsTheTeamCollected.end(), 0); for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) { - if(team.inTeam(i)) + if(team.containsPlayer(i)) { - for(unsigned int g = 0; g < goodsToCollect.size(); g++) + for(unsigned g = 0; g < goodsToCollect.size(); g++) { team.amountsTheTeamCollected[g] += GetAmount(g, i); if(team.amountsTheTeamCollected[g] > maxAmountsATeamCollected[g]) @@ -257,7 +258,7 @@ void EconomyModeHandler::UpdateAmounts() for(auto& team : economyModeTeams) { team.goodTypeWins = 0; - for(unsigned int g = 0; g < goodsToCollect.size(); g++) + for(unsigned g = 0; g < goodsToCollect.size(); g++) { if(team.amountsTheTeamCollected[g] >= maxAmountsATeamCollected[g]) { @@ -316,7 +317,7 @@ bool EconomyModeHandler::isOver() const return gwg->GetGGS().objective == GO_ECONOMYMODE && !isInfinite() && endFrame < GetEvMgr().GetCurrentGF(); } -EconomyModeHandler::EconTeam::EconTeam(SerializedGameData& sgd, unsigned int numGoodTypesToCollect) +EconomyModeHandler::EconTeam::EconTeam(SerializedGameData& sgd, unsigned numGoodTypesToCollect) : playersInTeam(sgd.PopSignedInt()), amountsTheTeamCollected(numGoodTypesToCollect, 0), goodTypeWins(0) {} @@ -325,7 +326,7 @@ void EconomyModeHandler::EconTeam::Serialize(SerializedGameData& sgd) const sgd.PushUnsignedInt(playersInTeam.to_ulong()); } -bool EconomyModeHandler::EconTeam::inTeam(unsigned int playerId) const +bool EconomyModeHandler::EconTeam::containsPlayer(unsigned playerId) const { return playersInTeam[playerId]; } diff --git a/libs/s25main/EconomyModeHandler.h b/libs/s25main/EconomyModeHandler.h index 74dc5a1ab3..8af7121fd4 100644 --- a/libs/s25main/EconomyModeHandler.h +++ b/libs/s25main/EconomyModeHandler.h @@ -27,16 +27,16 @@ class EconomyModeHandler : public GameObject // The number of good types the team is the leader in unsigned goodTypeWins; - EconTeam(std::bitset playersInTeam, unsigned int numGoodTypesToCollect) noexcept + EconTeam(std::bitset playersInTeam, unsigned numGoodTypesToCollect) noexcept : playersInTeam(playersInTeam), amountsTheTeamCollected(numGoodTypesToCollect, 0), goodTypeWins(0) {} - EconTeam(SerializedGameData& sgd, unsigned int numGoodTypesToCollect); + EconTeam(SerializedGameData& sgd, unsigned numGoodTypesToCollect); void Serialize(SerializedGameData& sgd) const; // Returns true if the player is in the team - bool inTeam(unsigned int playerId) const; + bool containsPlayer(unsigned playerId) const; }; private: @@ -53,14 +53,14 @@ class EconomyModeHandler : public GameObject std::vector> amountsThePlayersCollected; // Number of Good types the best team is currently leading in - unsigned int mostGoodTypeWins; + unsigned mostGoodTypeWins; // Gameframe in which the economy mode progress trackingd data has been updated last unsigned gfLastUpdated; // Sum up all forms of the given good in the inventory (tools, weapons and beer are also counted in the hands of // workers and soldiers) - unsigned int SumUpGood(GoodType good, const Inventory& Inventory); + unsigned SumUpGood(GoodType good, const Inventory& Inventory); // Determine the teams for the economy mode void DetermineTeams(); @@ -89,13 +89,13 @@ class EconomyModeHandler : public GameObject void UpdateAmounts(); // Get the amounts collected by a player - unsigned int GetAmount(unsigned int goodNumber, unsigned int playerId) + unsigned GetAmount(unsigned goodNumber, unsigned playerId) { return amountsThePlayersCollected[goodNumber][playerId]; } // Get the amount of good the leading team (with regards to that good) has collected - unsigned int GetMaxTeamAmount(unsigned int goodNumber) { return maxAmountsATeamCollected[goodNumber]; } + unsigned GetMaxTeamAmount(unsigned goodNumber) { return maxAmountsATeamCollected[goodNumber]; } // Get Game frame in which the economy mode winners will be determined unsigned GetEndFrame() const { return endFrame; } diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.cpp b/libs/s25main/ingameWindows/iwEconomicProgress.cpp index c10cee412f..96c660e35d 100644 --- a/libs/s25main/ingameWindows/iwEconomicProgress.cpp +++ b/libs/s25main/ingameWindows/iwEconomicProgress.cpp @@ -39,31 +39,31 @@ iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) _("Economic Progress"), LOADER.GetImageN("resource", 41)), gwv(gwv) { - const unsigned int textcolor[] = {COLOR_GREEN, COLOR_YELLOW, COLOR_RED}; + const unsigned textcolor[] = {COLOR_GREEN, COLOR_YELLOW, COLOR_RED}; const GameWorldBase& world = gwv.GetWorld(); EconomyModeHandler* eH = world.econHandler.get(); - const unsigned int numGoodTypesToCollect = eH->GetGoodTypesToCollect().size(); + const unsigned numGoodTypesToCollect = eH->GetGoodTypesToCollect().size(); AddText(1, DrawPoint(11 + 27, 46), _("Player"), COLOR_GREEN, FontStyle::LEFT | FontStyle::BOTTOM, NormalFont); const std::vector& economyModeTeams = eH->GetTeams(); - unsigned int num_teams = economyModeTeams.size(); + unsigned num_teams = economyModeTeams.size(); // determine team display order (main player team first) - unsigned int mainTeam = 0; - for(unsigned int i = 0; i < economyModeTeams.size(); i++) + unsigned mainTeam = 0; + for(unsigned i = 0; i < economyModeTeams.size(); i++) { - if(economyModeTeams[i].inTeam(gwv.GetPlayer().GetPlayerId())) + if(economyModeTeams[i].containsPlayer(gwv.GetPlayer().GetPlayerId())) { mainTeam = i; break; } } teamOrder.push_back(mainTeam); - for(unsigned int i = 0; i < economyModeTeams.size(); i++) + for(unsigned i = 0; i < economyModeTeams.size(); i++) { if(i != mainTeam) { @@ -81,7 +81,7 @@ iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) // the goods table const Extent btSize(26, 26); auto& goodTypes = eH->GetGoodTypesToCollect(); - for(unsigned int i = 0; i < goodTypes.size(); i++) + for(unsigned i = 0; i < goodTypes.size(); i++) { GoodType good = eH->GetGoodTypesToCollect()[i]; @@ -119,15 +119,15 @@ void iwEconomicProgress::Draw_() // draw team colors const std::vector& economyModeTeams = gwv.GetWorld().econHandler->GetTeams(); - for(unsigned int t = 0; t < economyModeTeams.size(); t++) + for(unsigned t = 0; t < economyModeTeams.size(); t++) { DrawPoint drawPt = GetDrawPos() + DrawPoint(37 + (t + 1) * 63, 22); - unsigned int height = 24; - unsigned int ystep = height / economyModeTeams[teamOrder[t]].playersInTeam.count(); - unsigned int ypos = 0; + unsigned height = 24; + unsigned ystep = height / economyModeTeams[teamOrder[t]].playersInTeam.count(); + unsigned ypos = 0; for(unsigned i = 0; i < gwv.GetWorld().GetNumPlayers(); ++i) { - if(economyModeTeams[teamOrder[t]].inTeam(i)) + if(economyModeTeams[teamOrder[t]].containsPlayer(i)) { if(height - ypos < 2 * ystep) ystep = height - ypos; @@ -168,7 +168,7 @@ void iwEconomicProgress::Msg_PaintBefore() eH->UpdateAmounts(); // update table elements - for(unsigned int i = 0; i < eH->GetGoodTypesToCollect().size(); i++) + for(unsigned i = 0; i < eH->GetGoodTypesToCollect().size(); i++) { for(unsigned j = 0; j < 1 + economyModeTeams.size(); j++) { diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.h b/libs/s25main/ingameWindows/iwEconomicProgress.h index 97d8c2497b..42f405836d 100644 --- a/libs/s25main/ingameWindows/iwEconomicProgress.h +++ b/libs/s25main/ingameWindows/iwEconomicProgress.h @@ -34,7 +34,7 @@ class iwEconomicProgress : public IngameWindow ctrlText* txtRemainingTime; // Order in which the teams are displayed - std::vector teamOrder; + std::vector teamOrder; void Draw_() override; From 16df88b031204bd3aa2e03a5269ddf11e2a1e0d9 Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Sat, 28 Nov 2020 16:32:40 +0100 Subject: [PATCH 04/15] removed peaceful mode check for economy mode --- libs/s25main/desktops/dskHostGame.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/s25main/desktops/dskHostGame.cpp b/libs/s25main/desktops/dskHostGame.cpp index a3c4917468..47b3b756b8 100644 --- a/libs/s25main/desktops/dskHostGame.cpp +++ b/libs/s25main/desktops/dskHostGame.cpp @@ -768,7 +768,7 @@ void dskHostGame::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult { if(mbr == MSR_YES) { - gameLobby->getSettings().setSelection(AddonId::PEACEFUL, true); + gameLobby->getSettings().setSelection(AddonId::PEACEFULMODE, true); if(gameLobby->getSettings().getSelection(AddonId::ECONOMY_MODE_GAME_LENGTH) == 0) gameLobby->getSettings().setSelection(AddonId::ECONOMY_MODE_GAME_LENGTH, 5); gameLobby->getSettings().setSelection(AddonId::NO_COINS_DEFAULT, true); @@ -786,7 +786,7 @@ void dskHostGame::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult { if(mbr == MSR_YES) { - gameLobby->getSettings().setSelection(AddonId::PEACEFUL, false); + gameLobby->getSettings().setSelection(AddonId::PEACEFULMODE, false); } else if(mbr == MSR_NO) { forceOptions = true; @@ -1060,7 +1060,7 @@ bool dskHostGame::checkOptions() if(forceOptions) return true; const GlobalGameSettings& ggs = gameLobby->getSettings(); - if(ggs.objective == GO_ECONOMYMODE && !ggs.getSelection(AddonId::PEACEFUL)) + if(ggs.objective == GO_ECONOMYMODE && !ggs.getSelection(AddonId::PEACEFULMODE)) { WINDOWMANAGER.Show( std::make_unique(_("Economy Mode"), @@ -1069,7 +1069,7 @@ bool dskHostGame::checkOptions() "you review them, choosing no will start the game."), this, MSB_YESNOCANCEL, MSB_QUESTIONGREEN, 10)); return false; - } else if(ggs.getSelection(AddonId::PEACEFUL) + } else if(ggs.getSelection(AddonId::PEACEFULMODE) && (ggs.objective == GO_CONQUER3_4 || ggs.objective == GO_TOTALDOMINATION)) { WINDOWMANAGER.Show( @@ -1081,4 +1081,4 @@ bool dskHostGame::checkOptions() return false; } return true; -} \ No newline at end of file +} From 686c2ca047da33f939e950f20c2c3514e6e0fcc9 Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Sun, 29 Nov 2020 00:14:33 +0100 Subject: [PATCH 05/15] clang-tidy issues --- libs/s25main/EconomyModeHandler.cpp | 2 +- libs/s25main/ingameWindows/iwEconomicProgress.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/s25main/EconomyModeHandler.cpp b/libs/s25main/EconomyModeHandler.cpp index f08833965c..bf75ce054e 100644 --- a/libs/s25main/EconomyModeHandler.cpp +++ b/libs/s25main/EconomyModeHandler.cpp @@ -75,7 +75,7 @@ EconomyModeHandler::EconomyModeHandler(unsigned endFrame) : endFrame(endFrame), static_assert(numGoodTypesToCollect > 0, "There have to be goods to be collected"); RTTR_Assert(commonGoodPool.size() >= numGoodTypesToCollect - 1); // "There have to be enough commond goods" - RTTR_Assert(specialGoodPool.size() >= 1); // There have to be enough special goods + RTTR_Assert(!specialGoodPool.empty()); // There have to be enough special goods goodsToCollect.clear(); goodsToCollect.resize(numGoodTypesToCollect); auto nextSlot = begin(goodsToCollect); diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.cpp b/libs/s25main/ingameWindows/iwEconomicProgress.cpp index 96c660e35d..4f9d574805 100644 --- a/libs/s25main/ingameWindows/iwEconomicProgress.cpp +++ b/libs/s25main/ingameWindows/iwEconomicProgress.cpp @@ -80,10 +80,10 @@ iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) } // the goods table const Extent btSize(26, 26); - auto& goodTypes = eH->GetGoodTypesToCollect(); - for(unsigned i = 0; i < goodTypes.size(); i++) + const std::vector& goodsToCollect = eH->GetGoodTypesToCollect(); + for(unsigned i = 0; i < goodsToCollect.size(); i++) { - GoodType good = eH->GetGoodTypesToCollect()[i]; + GoodType good = goodsToCollect[i]; const DrawPoint btPos(11, 48 + 26 * i); AddImage(100 + i, btPos + btSize / 2, LOADER.GetMapImageN(2298), _(WARE_NAMES[good])); From ab4388cae1e7f004e1339ef5c50ac1de5588d56d Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Sun, 29 Nov 2020 20:51:18 +0100 Subject: [PATCH 06/15] Some more code cleanup --- libs/common/include/helpers/make_array.h | 27 +++++ libs/s25main/EconomyModeHandler.cpp | 52 ++------- libs/s25main/EconomyModeHandler.h | 49 ++++----- .../ingameWindows/iwEconomicProgress.cpp | 101 +++++++++--------- .../ingameWindows/iwEconomicProgress.h | 5 +- 5 files changed, 116 insertions(+), 118 deletions(-) create mode 100644 libs/common/include/helpers/make_array.h diff --git a/libs/common/include/helpers/make_array.h b/libs/common/include/helpers/make_array.h new file mode 100644 index 0000000000..40957a8050 --- /dev/null +++ b/libs/common/include/helpers/make_array.h @@ -0,0 +1,27 @@ +// Copyright (c) 2018 - 2020 Settlers Freaks (sf-team at siedler25.org) +// +// This file is part of Return To The Roots. +// +// Return To The Roots is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// Return To The Roots is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Return To The Roots. If not, see . + +#pragma once + +#include + +template +constexpr auto make_array(Types&&... t) +{ + using ResultType = std::conditional_t::value, std::common_type_t, D>; + return std::array{std::forward(t)...}; +} \ No newline at end of file diff --git a/libs/s25main/EconomyModeHandler.cpp b/libs/s25main/EconomyModeHandler.cpp index bf75ce054e..da0ba026f5 100644 --- a/libs/s25main/EconomyModeHandler.cpp +++ b/libs/s25main/EconomyModeHandler.cpp @@ -23,6 +23,7 @@ #include "GlobalGameSettings.h" #include "SerializedGameData.h" #include "helpers/containerUtils.h" +#include "helpers/make_array.h" #include "random/Random.h" #include "world/GameWorld.h" #include "world/GameWorldGame.h" @@ -33,49 +34,21 @@ EconomyModeHandler::EconomyModeHandler(unsigned endFrame) : endFrame(endFrame), gfLastUpdated(0) { - const std::vector specialGoodPool({ - GD_TONGS, - GD_HAMMER, - GD_AXE, - GD_SAW, - GD_PICKAXE, - GD_SHOVEL, - GD_CRUCIBLE, - GD_RODANDLINE, - GD_SCYTHE, - GD_CLEAVER, - GD_ROLLINGPIN, - GD_BOW, - }); - - const std::vector commonGoodPool({ - GD_BEER, - GD_WATER, - GD_BOAT, - GD_SWORD, - GD_IRON, - GD_FLOUR, - GD_FISH, - GD_BREAD, - GD_WOOD, - GD_BOARDS, - GD_STONES, - GD_GRAIN, - GD_COINS, - GD_GOLD, - GD_IRONORE, - GD_COAL, - GD_MEAT, - GD_HAM, - }); + const auto specialGoodPool = + make_array(GD_TONGS, GD_HAMMER, GD_AXE, GD_SAW, GD_PICKAXE, GD_SHOVEL, GD_CRUCIBLE, GD_RODANDLINE, + GD_SCYTHE, GD_CLEAVER, GD_ROLLINGPIN, GD_BOW); + + const auto commonGoodPool = + make_array(GD_BEER, GD_WATER, GD_BOAT, GD_SWORD, GD_IRON, GD_FLOUR, GD_FISH, GD_BREAD, GD_WOOD, + GD_BOARDS, GD_STONES, GD_GRAIN, GD_COINS, GD_GOLD, GD_IRONORE, GD_COAL, GD_MEAT, GD_HAM); constexpr unsigned numGoodTypesToCollect = 7; // Randomly determine *numGoodTypesToCollect* many good types, one of which is a special good (=tool) static_assert(numGoodTypesToCollect > 0, "There have to be goods to be collected"); - RTTR_Assert(commonGoodPool.size() >= numGoodTypesToCollect - 1); // "There have to be enough commond goods" - RTTR_Assert(!specialGoodPool.empty()); // There have to be enough special goods + static_assert(commonGoodPool.size() >= numGoodTypesToCollect - 1, "There have to be enough commond goods"); + static_assert(!specialGoodPool.empty(), "There have to be enough special goods"); goodsToCollect.clear(); goodsToCollect.resize(numGoodTypesToCollect); auto nextSlot = begin(goodsToCollect); @@ -83,6 +56,7 @@ EconomyModeHandler::EconomyModeHandler(unsigned endFrame) : endFrame(endFrame), while(nextSlot != end(goodsToCollect) - 1) { GoodType nextGoodType = commonGoodPool[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), commonGoodPool.size())]; + // No duplicates should be in goodsToCollect, so only add a good if it isn't one of the already found goods if(std::find(begin(goodsToCollect), nextSlot, nextGoodType) == nextSlot) { *nextSlot = nextGoodType; @@ -151,10 +125,6 @@ void EconomyModeHandler::Serialize(SerializedGameData& sgd) const void EconomyModeHandler::DetermineTeams() { - // If we already determined who is in a team with whom skip this. For the economy mode we only count teams at game - // start - if(!economyModeTeams.empty()) - return; for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) { if(gwg->GetPlayer(i).isUsed()) diff --git a/libs/s25main/EconomyModeHandler.h b/libs/s25main/EconomyModeHandler.h index 8af7121fd4..409f256cd7 100644 --- a/libs/s25main/EconomyModeHandler.h +++ b/libs/s25main/EconomyModeHandler.h @@ -11,20 +11,20 @@ class SerializedGameData; class GameEvent; -// Handler object to keep track of the economy mode progress and for the game end event +/// Handler object to keep track of the economy mode progress and for the game end event class EconomyModeHandler : public GameObject { public: - // Object to hold data on the teams competing in the economy mode + /// Object to hold data on the teams competing in the economy mode struct EconTeam { - // The players that are in the team + /// The players that are in the team std::bitset playersInTeam; - // The amounts the team collected for each good + /// The amounts the team collected for each good std::vector amountsTheTeamCollected; - // The number of good types the team is the leader in + /// The number of good types the team is the leader in unsigned goodTypeWins; EconTeam(std::bitset playersInTeam, unsigned numGoodTypesToCollect) noexcept @@ -35,7 +35,7 @@ class EconomyModeHandler : public GameObject void Serialize(SerializedGameData& sgd) const; - // Returns true if the player is in the team + /// Returns true if the player is in the team bool containsPlayer(unsigned playerId) const; }; @@ -45,24 +45,24 @@ class EconomyModeHandler : public GameObject /// Good types to collect std::vector goodsToCollect; - // Data for economy mode progress tracking + /// Data for economy mode progress tracking std::vector economyModeTeams; - // Maximal amounts collected any team separately for each good type to collect + /// Maximal amounts collected any team separately for each good type to collect std::vector maxAmountsATeamCollected; - // Amounts collected by each player for each good type to collect + /// Amounts collected by each player for each good type to collect std::vector> amountsThePlayersCollected; - // Number of Good types the best team is currently leading in + /// Number of Good types the best team is currently leading in unsigned mostGoodTypeWins; - // Gameframe in which the economy mode progress trackingd data has been updated last + /// Gameframe in which the economy mode progress trackingd data has been updated last unsigned gfLastUpdated; - // Sum up all forms of the given good in the inventory (tools, weapons and beer are also counted in the hands of - // workers and soldiers) + /// Sum up all forms of the given good in the inventory (tools, weapons and beer are also counted in the hands of + /// workers and soldiers) unsigned SumUpGood(GoodType good, const Inventory& Inventory); - // Determine the teams for the economy mode + /// Determine the teams for the economy mode (only called at game start) void DetermineTeams(); public: @@ -70,7 +70,6 @@ class EconomyModeHandler : public GameObject EconomyModeHandler(SerializedGameData& sgd, unsigned objId); - /// Destroy void Destroy() override; void Serialize(SerializedGameData& sgd) const override; @@ -78,34 +77,30 @@ class EconomyModeHandler : public GameObject /// Event-Handler void HandleEvent(unsigned id) override; - // Return vector of the teams (and their collected amounts) - const std::vector& GetTeams() - { - DetermineTeams(); - return economyModeTeams; - } + /// Return vector of the teams (and their collected amounts) + const std::vector& GetTeams() { return economyModeTeams; } - // Method to update the ware trackers + /// Method to update the ware trackers void UpdateAmounts(); - // Get the amounts collected by a player + /// Get the amounts collected by a player unsigned GetAmount(unsigned goodNumber, unsigned playerId) { return amountsThePlayersCollected[goodNumber][playerId]; } - // Get the amount of good the leading team (with regards to that good) has collected + /// Get the amount of good the leading team (with regards to that good) has collected unsigned GetMaxTeamAmount(unsigned goodNumber) { return maxAmountsATeamCollected[goodNumber]; } - // Get Game frame in which the economy mode winners will be determined + /// Get Game frame in which the economy mode winners will be determined unsigned GetEndFrame() const { return endFrame; } - // Return the good types to collect + /// Return the good types to collect const std::vector& GetGoodTypesToCollect() const { return goodsToCollect; } GO_Type GetGOT() const override { return GOT_ECONOMYMODEHANDLER; } - // Miscellaneous status checks + /// Miscellaneous status checks bool isOver() const; bool isInfinite() const { return endFrame == 0; } bool showAllTeamAmounts() const { return isOver() || isInfinite(); } diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.cpp b/libs/s25main/ingameWindows/iwEconomicProgress.cpp index 4f9d574805..f9f59bcb51 100644 --- a/libs/s25main/ingameWindows/iwEconomicProgress.cpp +++ b/libs/s25main/ingameWindows/iwEconomicProgress.cpp @@ -34,81 +34,86 @@ #include "world/GameWorldViewer.h" #include "gameData/const_gui_ids.h" +constexpr Extent wareIconSize(26, 26); +constexpr unsigned txtBoxWidth = 63; +constexpr Extent padding1(11, 21); // Left, Top +constexpr Extent padding2(11, 18); // Right, Bottom +constexpr unsigned teamRectHeight = 24; +constexpr unsigned extraSpacing = 2; + iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) - : IngameWindow(CGI_ECONOMICPROGRESS, IngameWindow::posLastOrCenter, Extent(240, 96 + 26 * 7), - _("Economic Progress"), LOADER.GetImageN("resource", 41)), + : IngameWindow(CGI_ECONOMICPROGRESS, IngameWindow::posLastOrCenter, + Extent(0, 0) /*will be resized inside the constructor*/, _("Economic progress"), + LOADER.GetImageN("resource", 41)), gwv(gwv) { const unsigned textcolor[] = {COLOR_GREEN, COLOR_YELLOW, COLOR_RED}; - const GameWorldBase& world = gwv.GetWorld(); - EconomyModeHandler* eH = world.econHandler.get(); - const unsigned numGoodTypesToCollect = eH->GetGoodTypesToCollect().size(); - - AddText(1, DrawPoint(11 + 27, 46), _("Player"), COLOR_GREEN, FontStyle::LEFT | FontStyle::BOTTOM, NormalFont); - const std::vector& economyModeTeams = eH->GetTeams(); unsigned num_teams = economyModeTeams.size(); + // resize window + Extent size(padding1.x + wareIconSize.x + (std::max((int)num_teams, 2) + 1) * txtBoxWidth + padding2.x, + padding1.y + teamRectHeight + ((int)numGoodTypesToCollect + 1) * wareIconSize.y + extraSpacing * 2 + + padding2.y); + this->Resize(size); + + AddText(1, DrawPoint(padding1.x + wareIconSize.x, padding1.y + teamRectHeight), _("Player"), COLOR_GREEN, + FontStyle::LEFT | FontStyle::BOTTOM, NormalFont); + // determine team display order (main player team first) - unsigned mainTeam = 0; - for(unsigned i = 0; i < economyModeTeams.size(); i++) + for(auto& curTeam : economyModeTeams) { - if(economyModeTeams[i].containsPlayer(gwv.GetPlayer().GetPlayerId())) + if(curTeam.containsPlayer(gwv.GetPlayer().GetPlayerId())) { - mainTeam = i; + teamOrder.push_back(&curTeam); break; } } - teamOrder.push_back(mainTeam); - for(unsigned i = 0; i < economyModeTeams.size(); i++) + for(auto& curTeam : economyModeTeams) { - if(i != mainTeam) + if(&curTeam != teamOrder[0]) { - teamOrder.push_back(i); + teamOrder.push_back(&curTeam); } } - // potentially resize window - if(num_teams > 2 || numGoodTypesToCollect != 7) - { - Extent size = this->GetSize(); - size.x += (std::max((int)num_teams, 2) - 2) * 63; - size.y += ((int)numGoodTypesToCollect - 7) * 26; - this->Resize(size); - } + // the goods table - const Extent btSize(26, 26); const std::vector& goodsToCollect = eH->GetGoodTypesToCollect(); + DrawPoint curBoxPos(padding1.x, padding1.y + teamRectHeight + extraSpacing); for(unsigned i = 0; i < goodsToCollect.size(); i++) { GoodType good = goodsToCollect[i]; - const DrawPoint btPos(11, 48 + 26 * i); - AddImage(100 + i, btPos + btSize / 2, LOADER.GetMapImageN(2298), _(WARE_NAMES[good])); - const DrawPoint warePos = btPos + btSize / 2; + AddImage(100 + i, curBoxPos + wareIconSize / 2, LOADER.GetMapImageN(2298), _(WARE_NAMES[good])); + const DrawPoint warePos = curBoxPos + wareIconSize / 2; AddImage(200 + i, warePos, LOADER.GetMapImageN(WARES_TEX_MAP_OFFSET + good)); + DrawPoint curTxtPos = curBoxPos + DrawPoint(wareIconSize.x, 0); for(unsigned j = 0; j < 1 + num_teams; j++) { - const DrawPoint txtPos = btPos + DrawPoint(26 + 63 * j, 0); - AddTextDeepening(300 + 10 * j + i, txtPos, Extent(63, btSize.y), TC_GREY, "?", NormalFont, - textcolor[j < 2 ? j : 2]); + AddTextDeepening(300 + (MAX_PLAYERS + 2) * j + i, curTxtPos, Extent(txtBoxWidth, wareIconSize.y), TC_GREY, + "?", NormalFont, textcolor[j < 2 ? j : 2]); + curTxtPos.x += txtBoxWidth; } + curBoxPos.y += wareIconSize.y; } if(!eH->isInfinite()) { // game progress display - AddText(2, DrawPoint(11 + 30, 240), _("Time remaining:"), COLOR_ORANGE, FontStyle::LEFT | FontStyle::TOP, - NormalFont); + AddText(2, DrawPoint(wareIconSize.x + extraSpacing + padding1.x, this->GetSize().y - padding2.y), + _("Time remaining:"), COLOR_ORANGE, FontStyle::LEFT | FontStyle::BOTTOM, NormalFont); txtRemainingTime = - AddText(3, DrawPoint(30 + 63 * 3, 240), "%", COLOR_ORANGE, FontStyle::RIGHT | FontStyle::TOP, NormalFont); + AddText(3, DrawPoint(wareIconSize.x + extraSpacing + txtBoxWidth * 3, this->GetSize().y - padding2.y), "%", + COLOR_ORANGE, FontStyle::RIGHT | FontStyle::BOTTOM, NormalFont); } // help button - AddImageButton(25, DrawPoint(11, 235), Extent(26, 26), TC_GREY, LOADER.GetImageN("io", 225), _("Help")); + AddImageButton(25, DrawPoint(padding1.x, this->GetSize().y - wareIconSize.y - padding2.y), wareIconSize, TC_GREY, + LOADER.GetImageN("io", 225), _("Help")); } iwEconomicProgress::~iwEconomicProgress() = default; @@ -119,22 +124,23 @@ void iwEconomicProgress::Draw_() // draw team colors const std::vector& economyModeTeams = gwv.GetWorld().econHandler->GetTeams(); - for(unsigned t = 0; t < economyModeTeams.size(); t++) + DrawPoint curTeamRectPos = GetDrawPos() + DrawPoint(padding1.x + wareIconSize.x + txtBoxWidth, padding1.y); + for(auto& team : teamOrder) { - DrawPoint drawPt = GetDrawPos() + DrawPoint(37 + (t + 1) * 63, 22); - unsigned height = 24; - unsigned ystep = height / economyModeTeams[teamOrder[t]].playersInTeam.count(); + unsigned ystep = teamRectHeight / team->playersInTeam.count(); unsigned ypos = 0; for(unsigned i = 0; i < gwv.GetWorld().GetNumPlayers(); ++i) { - if(economyModeTeams[teamOrder[t]].containsPlayer(i)) + if(team->containsPlayer(i)) { - if(height - ypos < 2 * ystep) - ystep = height - ypos; - DrawRectangle(Rect(drawPt + DrawPoint(0, ypos), Extent(62, ystep)), gwv.GetWorld().GetPlayer(i).color); + if(teamRectHeight - ypos < 2 * ystep) + ystep = teamRectHeight - ypos; + DrawRectangle(Rect(curTeamRectPos + DrawPoint(0, ypos), Extent(txtBoxWidth - 1, ystep)), + gwv.GetWorld().GetPlayer(i).color); ypos += ystep; } } + curTeamRectPos.x += txtBoxWidth; } } @@ -172,17 +178,16 @@ void iwEconomicProgress::Msg_PaintBefore() { for(unsigned j = 0; j < 1 + economyModeTeams.size(); j++) { - auto* text = GetCtrl(300 + 10 * j + i); + auto* text = GetCtrl(300 + (MAX_PLAYERS + 2) * j + i); if(j == 0) { text->SetText(std::to_string(eH->GetAmount(i, mainPlayer.GetPlayerId()))); } else if(j == 1 || eH->showAllTeamAmounts()) { - text->SetText(std::to_string(economyModeTeams[teamOrder[j - 1]].amountsTheTeamCollected[i])); + text->SetText(std::to_string(teamOrder[j - 1]->amountsTheTeamCollected[i])); // White color when all teams are shown to mark good types that the team has won or is leading in - if(eH->showAllTeamAmounts() - && economyModeTeams[teamOrder[j - 1]].amountsTheTeamCollected[i] >= eH->GetMaxTeamAmount(i)) + if(eH->showAllTeamAmounts() && teamOrder[j - 1]->amountsTheTeamCollected[i] >= eH->GetMaxTeamAmount(i)) { text->SetTextColor(COLOR_WHITE); } else @@ -207,7 +212,7 @@ void iwEconomicProgress::Msg_PaintBefore() txtRemainingTime->SetText(GAMECLIENT.FormatGFTime(eH->GetEndFrame() - world.GetEvMgr().GetCurrentGF())); } else { - txtRemainingTime->SetText(_("0")); + txtRemainingTime->SetText(GAMECLIENT.FormatGFTime(0)); } } } diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.h b/libs/s25main/ingameWindows/iwEconomicProgress.h index 42f405836d..e3429597f2 100644 --- a/libs/s25main/ingameWindows/iwEconomicProgress.h +++ b/libs/s25main/ingameWindows/iwEconomicProgress.h @@ -17,6 +17,7 @@ #pragma once +#include "EconomyModeHandler.h" #include "IngameWindow.h" class ctrlText; @@ -33,8 +34,8 @@ class iwEconomicProgress : public IngameWindow const GameWorldViewer& gwv; ctrlText* txtRemainingTime; - // Order in which the teams are displayed - std::vector teamOrder; + /// Order in which the teams are displayed + std::vector teamOrder; void Draw_() override; From 293c0631ed4be4895ceed09f1382ed38614b2605 Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Sun, 29 Nov 2020 20:38:32 +0100 Subject: [PATCH 07/15] removed unused variable --- .../s25main/ingameWindows/iwEconomicProgress.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.cpp b/libs/s25main/ingameWindows/iwEconomicProgress.cpp index f9f59bcb51..f265d79244 100644 --- a/libs/s25main/ingameWindows/iwEconomicProgress.cpp +++ b/libs/s25main/ingameWindows/iwEconomicProgress.cpp @@ -50,21 +50,18 @@ iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) const unsigned textcolor[] = {COLOR_GREEN, COLOR_YELLOW, COLOR_RED}; const GameWorldBase& world = gwv.GetWorld(); EconomyModeHandler* eH = world.econHandler.get(); - const unsigned numGoodTypesToCollect = eH->GetGoodTypesToCollect().size(); - const std::vector& economyModeTeams = eH->GetTeams(); - unsigned num_teams = economyModeTeams.size(); // resize window - Extent size(padding1.x + wareIconSize.x + (std::max((int)num_teams, 2) + 1) * txtBoxWidth + padding2.x, - padding1.y + teamRectHeight + ((int)numGoodTypesToCollect + 1) * wareIconSize.y + extraSpacing * 2 - + padding2.y); + Extent size(padding1.x + wareIconSize.x + (std::max((int)eH->GetTeams().size(), 2) + 1) * txtBoxWidth + padding2.x, + padding1.y + teamRectHeight + ((int)eH->GetGoodTypesToCollect().size() + 1) * wareIconSize.y + + extraSpacing * 2 + padding2.y); this->Resize(size); AddText(1, DrawPoint(padding1.x + wareIconSize.x, padding1.y + teamRectHeight), _("Player"), COLOR_GREEN, FontStyle::LEFT | FontStyle::BOTTOM, NormalFont); // determine team display order (main player team first) - for(auto& curTeam : economyModeTeams) + for(const auto& curTeam : eH->GetTeams()) { if(curTeam.containsPlayer(gwv.GetPlayer().GetPlayerId())) { @@ -72,7 +69,7 @@ iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) break; } } - for(auto& curTeam : economyModeTeams) + for(const auto& curTeam : eH->GetTeams()) { if(&curTeam != teamOrder[0]) { @@ -92,7 +89,7 @@ iwEconomicProgress::iwEconomicProgress(const GameWorldViewer& gwv) AddImage(200 + i, warePos, LOADER.GetMapImageN(WARES_TEX_MAP_OFFSET + good)); DrawPoint curTxtPos = curBoxPos + DrawPoint(wareIconSize.x, 0); - for(unsigned j = 0; j < 1 + num_teams; j++) + for(unsigned j = 0; j < 1 + eH->GetTeams().size(); j++) { AddTextDeepening(300 + (MAX_PLAYERS + 2) * j + i, curTxtPos, Extent(txtBoxWidth, wareIconSize.y), TC_GREY, "?", NormalFont, textcolor[j < 2 ? j : 2]); @@ -123,7 +120,6 @@ void iwEconomicProgress::Draw_() IngameWindow::Draw_(); // draw team colors - const std::vector& economyModeTeams = gwv.GetWorld().econHandler->GetTeams(); DrawPoint curTeamRectPos = GetDrawPos() + DrawPoint(padding1.x + wareIconSize.x + txtBoxWidth, padding1.y); for(auto& team : teamOrder) { From ddca852191020ab1461a58c08e7ecf61d51a8f2d Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Mon, 30 Nov 2020 01:18:24 +0100 Subject: [PATCH 08/15] Test case and small make_array changes --- libs/common/include/helpers/make_array.h | 5 +- libs/s25main/EconomyModeHandler.cpp | 12 +- tests/s25Main/integration/testEconomyMode.cpp | 144 ++++++++++++++++++ 3 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 tests/s25Main/integration/testEconomyMode.cpp diff --git a/libs/common/include/helpers/make_array.h b/libs/common/include/helpers/make_array.h index 40957a8050..0ab9147949 100644 --- a/libs/common/include/helpers/make_array.h +++ b/libs/common/include/helpers/make_array.h @@ -18,10 +18,13 @@ #pragma once #include +namespace helpers { template constexpr auto make_array(Types&&... t) { using ResultType = std::conditional_t::value, std::common_type_t, D>; return std::array{std::forward(t)...}; -} \ No newline at end of file +} + +} // namespace helpers \ No newline at end of file diff --git a/libs/s25main/EconomyModeHandler.cpp b/libs/s25main/EconomyModeHandler.cpp index da0ba026f5..ba0fd9bfac 100644 --- a/libs/s25main/EconomyModeHandler.cpp +++ b/libs/s25main/EconomyModeHandler.cpp @@ -34,13 +34,13 @@ EconomyModeHandler::EconomyModeHandler(unsigned endFrame) : endFrame(endFrame), gfLastUpdated(0) { - const auto specialGoodPool = - make_array(GD_TONGS, GD_HAMMER, GD_AXE, GD_SAW, GD_PICKAXE, GD_SHOVEL, GD_CRUCIBLE, GD_RODANDLINE, - GD_SCYTHE, GD_CLEAVER, GD_ROLLINGPIN, GD_BOW); + constexpr auto specialGoodPool = + helpers::make_array(GD_TONGS, GD_HAMMER, GD_AXE, GD_SAW, GD_PICKAXE, GD_SHOVEL, GD_CRUCIBLE, GD_RODANDLINE, + GD_SCYTHE, GD_CLEAVER, GD_ROLLINGPIN, GD_BOW); - const auto commonGoodPool = - make_array(GD_BEER, GD_WATER, GD_BOAT, GD_SWORD, GD_IRON, GD_FLOUR, GD_FISH, GD_BREAD, GD_WOOD, - GD_BOARDS, GD_STONES, GD_GRAIN, GD_COINS, GD_GOLD, GD_IRONORE, GD_COAL, GD_MEAT, GD_HAM); + constexpr auto commonGoodPool = + helpers::make_array(GD_BEER, GD_WATER, GD_BOAT, GD_SWORD, GD_IRON, GD_FLOUR, GD_FISH, GD_BREAD, GD_WOOD, + GD_BOARDS, GD_STONES, GD_GRAIN, GD_COINS, GD_GOLD, GD_IRONORE, GD_COAL, GD_MEAT, GD_HAM); constexpr unsigned numGoodTypesToCollect = 7; diff --git a/tests/s25Main/integration/testEconomyMode.cpp b/tests/s25Main/integration/testEconomyMode.cpp new file mode 100644 index 0000000000..6bf3ad26b0 --- /dev/null +++ b/tests/s25Main/integration/testEconomyMode.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2016 - 2017 Settlers Freaks (sf-team at siedler25.org) +// +// This file is part of Return To The Roots. +// +// Return To The Roots is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// Return To The Roots is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Return To The Roots. If not, see . + +#include "EconomyModeHandler.h" +#include "EventManager.h" +#include "GamePlayer.h" +#include "Savegame.h" +#include "SerializedGameData.h" +#include "buildings/nobBaseWarehouse.h" +#include "worldFixtures/MockLocalGameState.h" +#include "worldFixtures/WorldFixture.h" +#include "worldFixtures/WorldWithGCExecution.h" +#include "world/GameWorld.h" +#include "gameTypes/GO_Type.h" +#include "s25util/tmpFile.h" +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(EconomyModeTestSuite) + +namespace { + +BOOST_FIXTURE_TEST_CASE(EconomyMode3Players, WorldWithGCExecution3P) +{ + auto& amountsToAdd = helpers::make_array(63, 100, 85); + + RTTR_Assert(world.GetNumPlayers() == 3); + world.GetPlayer(0).team = TM_TEAM2; + world.GetPlayer(1).team = TM_TEAM1; + world.GetPlayer(2).team = TM_TEAM2; + std::array hqPos; + for(unsigned playerIdx = 0; playerIdx < 3; ++playerIdx) + { + world.GetPlayer(playerIdx).MakeStartPacts(); + hqPos[playerIdx] = world.GetPlayer(playerIdx).GetHQPos(); + } + this->ggs.objective = GO_ECONOMYMODE; + + world.econHandler = std::make_unique(4); + BOOST_REQUIRE(world.econHandler); + const auto goodsToCollect = world.econHandler->GetGoodTypesToCollect(); + + BOOST_REQUIRE(!goodsToCollect.empty()); + BOOST_REQUIRE_EQUAL(world.econHandler->GetEndFrame(), (unsigned)4); + BOOST_REQUIRE(!world.econHandler->isOver()); + BOOST_REQUIRE(!world.econHandler->isInfinite()); + BOOST_REQUIRE(world.GetEvMgr().ObjectHasEvents(*world.econHandler)); + + world.GetEvMgr().ExecuteNextGF(); + world.econHandler->UpdateAmounts(); + + // Individual amount counting + unsigned initialAmount = world.econHandler->GetAmount(0, 0); + BOOST_REQUIRE_EQUAL(initialAmount, world.econHandler->GetAmount(0, 1)); + BOOST_REQUIRE_EQUAL(initialAmount, world.econHandler->GetAmount(0, 2)); + + for(unsigned playerIdx = 0; playerIdx < 3; ++playerIdx) + { + Inventory inv; + inv.Add(goodsToCollect[0], amountsToAdd[playerIdx]); + world.GetSpecObj(hqPos[playerIdx])->AddGoods(inv, true); + } + world.GetEvMgr().ExecuteNextGF(); + world.econHandler->UpdateAmounts(); + for(unsigned playerIdx = 0; playerIdx < 3; ++playerIdx) + { + BOOST_REQUIRE_EQUAL(world.econHandler->GetAmount(0, playerIdx), initialAmount + amountsToAdd[playerIdx]); + } + + // Teams behaviour + auto econTeams = world.econHandler->GetTeams(); + + // People get assigned to the correct teams? + BOOST_REQUIRE_EQUAL(econTeams.size(), (size_t)2); + unsigned smallTeam = econTeams[0].playersInTeam.count() - 1; + unsigned bigTeam = 2 - econTeams[0].playersInTeam.count(); + BOOST_REQUIRE_EQUAL(econTeams[smallTeam].playersInTeam.count(), (size_t)1); + BOOST_REQUIRE_EQUAL(econTeams[bigTeam].playersInTeam.count(), (size_t)2); + BOOST_REQUIRE(econTeams[smallTeam].containsPlayer(1)); + BOOST_REQUIRE(econTeams[bigTeam].containsPlayer(0)); + BOOST_REQUIRE(econTeams[bigTeam].containsPlayer(2)); + + // Team amounts get calculated correctly? + BOOST_REQUIRE_EQUAL(econTeams[smallTeam].amountsTheTeamCollected[0], initialAmount + amountsToAdd[1]); + BOOST_REQUIRE_EQUAL(econTeams[bigTeam].amountsTheTeamCollected[0], + 2 * initialAmount + amountsToAdd[0] + amountsToAdd[2]); + BOOST_REQUIRE(econTeams[smallTeam].goodTypeWins < econTeams[bigTeam].goodTypeWins); + BOOST_REQUIRE_EQUAL(world.econHandler->GetMaxTeamAmount(0), econTeams[bigTeam].amountsTheTeamCollected[0]); + + // Serialization/Deserialization + Savegame save; + for(unsigned i = 0; i < world.GetNumPlayers(); i++) + save.AddPlayer(world.GetPlayer(i)); + save.ggs = ggs; + save.start_gf = em.GetCurrentGF(); + save.sgd.MakeSnapshot(game); + TmpFile tmpFile; + BOOST_TEST_REQUIRE(save.Save(tmpFile.filePath, "MapTitle")); + Savegame loadSave; + BOOST_TEST_REQUIRE(loadSave.Load(tmpFile.filePath, SaveGameDataToLoad::All)); + BOOST_TEST_REQUIRE(loadSave.GetNumPlayers() == (unsigned)3); + std::vector players; + for(unsigned j = 0; j < 3; j++) + players.push_back(PlayerInfo(loadSave.GetPlayer(j))); + std::shared_ptr sharedGame(new Game(save.ggs, loadSave.start_gf, players)); + GameWorld& newWorld = sharedGame->world_; + MockLocalGameState localGameState; + save.sgd.ReadSnapshot(sharedGame, localGameState); + BOOST_TEST_REQUIRE(newWorld.econHandler); + auto& goodsToCollectAfter = newWorld.econHandler->GetGoodTypesToCollect(); + auto& econTeamsAfter = newWorld.econHandler->GetTeams(); + BOOST_REQUIRE(goodsToCollect == goodsToCollectAfter); + BOOST_REQUIRE_EQUAL(econTeams.size(), econTeamsAfter.size()); + newWorld.GetEvMgr().ExecuteNextGF(); + newWorld.econHandler->UpdateAmounts(); + for(auto& teamPair : boost::combine(econTeams, econTeamsAfter)) + { + auto& before = teamPair.get_head(); + auto& after = teamPair.get_tail().get_head(); + BOOST_REQUIRE_EQUAL(before.playersInTeam, after.playersInTeam); + BOOST_REQUIRE(before.amountsTheTeamCollected == after.amountsTheTeamCollected); + BOOST_REQUIRE_EQUAL(before.goodTypeWins, after.goodTypeWins); + } + BOOST_REQUIRE(newWorld.GetEvMgr().ObjectHasEvents(*newWorld.econHandler)); +} + +} // namespace + +BOOST_AUTO_TEST_SUITE_END() From 1cf3e803d9e8a7978f8114a0b83fa63184bf5e03 Mon Sep 17 00:00:00 2001 From: Jonathan Steinbuch Date: Mon, 30 Nov 2020 01:46:11 +0100 Subject: [PATCH 09/15] solve warnings in test case --- tests/s25Main/integration/testEconomyMode.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/s25Main/integration/testEconomyMode.cpp b/tests/s25Main/integration/testEconomyMode.cpp index 6bf3ad26b0..e354946496 100644 --- a/tests/s25Main/integration/testEconomyMode.cpp +++ b/tests/s25Main/integration/testEconomyMode.cpp @@ -37,7 +37,7 @@ namespace { BOOST_FIXTURE_TEST_CASE(EconomyMode3Players, WorldWithGCExecution3P) { - auto& amountsToAdd = helpers::make_array(63, 100, 85); + constexpr auto amountsToAdd = helpers::make_array(63, 100, 85); RTTR_Assert(world.GetNumPlayers() == 3); world.GetPlayer(0).team = TM_TEAM2; @@ -52,7 +52,6 @@ BOOST_FIXTURE_TEST_CASE(EconomyMode3Players, WorldWithGCExecution3P) this->ggs.objective = GO_ECONOMYMODE; world.econHandler = std::make_unique(4); - BOOST_REQUIRE(world.econHandler); const auto goodsToCollect = world.econHandler->GetGoodTypesToCollect(); BOOST_REQUIRE(!goodsToCollect.empty()); @@ -83,7 +82,7 @@ BOOST_FIXTURE_TEST_CASE(EconomyMode3Players, WorldWithGCExecution3P) } // Teams behaviour - auto econTeams = world.econHandler->GetTeams(); + const auto econTeams = world.econHandler->GetTeams(); // People get assigned to the correct teams? BOOST_REQUIRE_EQUAL(econTeams.size(), (size_t)2); @@ -121,17 +120,17 @@ BOOST_FIXTURE_TEST_CASE(EconomyMode3Players, WorldWithGCExecution3P) GameWorld& newWorld = sharedGame->world_; MockLocalGameState localGameState; save.sgd.ReadSnapshot(sharedGame, localGameState); - BOOST_TEST_REQUIRE(newWorld.econHandler); - auto& goodsToCollectAfter = newWorld.econHandler->GetGoodTypesToCollect(); - auto& econTeamsAfter = newWorld.econHandler->GetTeams(); + BOOST_REQUIRE(newWorld.econHandler); + const auto& goodsToCollectAfter = newWorld.econHandler->GetGoodTypesToCollect(); + const auto& econTeamsAfter = newWorld.econHandler->GetTeams(); BOOST_REQUIRE(goodsToCollect == goodsToCollectAfter); BOOST_REQUIRE_EQUAL(econTeams.size(), econTeamsAfter.size()); newWorld.GetEvMgr().ExecuteNextGF(); newWorld.econHandler->UpdateAmounts(); - for(auto& teamPair : boost::combine(econTeams, econTeamsAfter)) + for(const auto& teamPair : boost::combine(econTeams, econTeamsAfter)) { - auto& before = teamPair.get_head(); - auto& after = teamPair.get_tail().get_head(); + const auto& before = teamPair.get_head(); + const auto& after = teamPair.get_tail().get_head(); BOOST_REQUIRE_EQUAL(before.playersInTeam, after.playersInTeam); BOOST_REQUIRE(before.amountsTheTeamCollected == after.amountsTheTeamCollected); BOOST_REQUIRE_EQUAL(before.goodTypeWins, after.goodTypeWins); From 3acc6cef02ca3d154395aa77a184c29f8e61e0ec Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Mon, 30 Nov 2020 12:22:35 +0100 Subject: [PATCH 10/15] small style improvements to the test case --- tests/s25Main/integration/testEconomyMode.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/s25Main/integration/testEconomyMode.cpp b/tests/s25Main/integration/testEconomyMode.cpp index e354946496..e468c55712 100644 --- a/tests/s25Main/integration/testEconomyMode.cpp +++ b/tests/s25Main/integration/testEconomyMode.cpp @@ -55,7 +55,7 @@ BOOST_FIXTURE_TEST_CASE(EconomyMode3Players, WorldWithGCExecution3P) const auto goodsToCollect = world.econHandler->GetGoodTypesToCollect(); BOOST_REQUIRE(!goodsToCollect.empty()); - BOOST_REQUIRE_EQUAL(world.econHandler->GetEndFrame(), (unsigned)4); + BOOST_REQUIRE_EQUAL(world.econHandler->GetEndFrame(), 4u); BOOST_REQUIRE(!world.econHandler->isOver()); BOOST_REQUIRE(!world.econHandler->isInfinite()); BOOST_REQUIRE(world.GetEvMgr().ObjectHasEvents(*world.econHandler)); @@ -85,11 +85,11 @@ BOOST_FIXTURE_TEST_CASE(EconomyMode3Players, WorldWithGCExecution3P) const auto econTeams = world.econHandler->GetTeams(); // People get assigned to the correct teams? - BOOST_REQUIRE_EQUAL(econTeams.size(), (size_t)2); + BOOST_REQUIRE_EQUAL(econTeams.size(), 2u); unsigned smallTeam = econTeams[0].playersInTeam.count() - 1; unsigned bigTeam = 2 - econTeams[0].playersInTeam.count(); - BOOST_REQUIRE_EQUAL(econTeams[smallTeam].playersInTeam.count(), (size_t)1); - BOOST_REQUIRE_EQUAL(econTeams[bigTeam].playersInTeam.count(), (size_t)2); + BOOST_REQUIRE_EQUAL(econTeams[smallTeam].playersInTeam.count(), 1u); + BOOST_REQUIRE_EQUAL(econTeams[bigTeam].playersInTeam.count(), 2u); BOOST_REQUIRE(econTeams[smallTeam].containsPlayer(1)); BOOST_REQUIRE(econTeams[bigTeam].containsPlayer(0)); BOOST_REQUIRE(econTeams[bigTeam].containsPlayer(2)); @@ -112,11 +112,11 @@ BOOST_FIXTURE_TEST_CASE(EconomyMode3Players, WorldWithGCExecution3P) BOOST_TEST_REQUIRE(save.Save(tmpFile.filePath, "MapTitle")); Savegame loadSave; BOOST_TEST_REQUIRE(loadSave.Load(tmpFile.filePath, SaveGameDataToLoad::All)); - BOOST_TEST_REQUIRE(loadSave.GetNumPlayers() == (unsigned)3); + BOOST_TEST_REQUIRE(loadSave.GetNumPlayers() == 3u); std::vector players; for(unsigned j = 0; j < 3; j++) players.push_back(PlayerInfo(loadSave.GetPlayer(j))); - std::shared_ptr sharedGame(new Game(save.ggs, loadSave.start_gf, players)); + auto sharedGame = std::make_shared(save.ggs, loadSave.start_gf, players); GameWorld& newWorld = sharedGame->world_; MockLocalGameState localGameState; save.sgd.ReadSnapshot(sharedGame, localGameState); @@ -129,8 +129,8 @@ BOOST_FIXTURE_TEST_CASE(EconomyMode3Players, WorldWithGCExecution3P) newWorld.econHandler->UpdateAmounts(); for(const auto& teamPair : boost::combine(econTeams, econTeamsAfter)) { - const auto& before = teamPair.get_head(); - const auto& after = teamPair.get_tail().get_head(); + const EconomyModeHandler::EconTeam& before = boost::get<0>(teamPair); + const EconomyModeHandler::EconTeam& after = boost::get<1>(teamPair); BOOST_REQUIRE_EQUAL(before.playersInTeam, after.playersInTeam); BOOST_REQUIRE(before.amountsTheTeamCollected == after.amountsTheTeamCollected); BOOST_REQUIRE_EQUAL(before.goodTypeWins, after.goodTypeWins); From f743cc97fc8edcff82d1ae5887fb702c04a09b07 Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Mon, 30 Nov 2020 13:04:00 +0100 Subject: [PATCH 11/15] Put in the economy mode message box again but made the text much more verbose --- libs/s25main/desktops/dskHostGame.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libs/s25main/desktops/dskHostGame.cpp b/libs/s25main/desktops/dskHostGame.cpp index 47b3b756b8..001eeee150 100644 --- a/libs/s25main/desktops/dskHostGame.cpp +++ b/libs/s25main/desktops/dskHostGame.cpp @@ -769,11 +769,8 @@ void dskHostGame::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult if(mbr == MSR_YES) { gameLobby->getSettings().setSelection(AddonId::PEACEFULMODE, true); - if(gameLobby->getSettings().getSelection(AddonId::ECONOMY_MODE_GAME_LENGTH) == 0) - gameLobby->getSettings().setSelection(AddonId::ECONOMY_MODE_GAME_LENGTH, 5); gameLobby->getSettings().setSelection(AddonId::NO_COINS_DEFAULT, true); gameLobby->getSettings().setSelection(AddonId::LIMIT_CATAPULTS, 2); - GetCtrl(20)->SetCheck(true); // Lockteams; UpdateGGS(); } else if(mbr == MSR_NO) { @@ -782,7 +779,7 @@ void dskHostGame::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult } } break; - case 11: // Peaceful Mode still active + case 11: // Peaceful mode still active but we have an attack based victory condition { if(mbr == MSR_YES) { @@ -1062,12 +1059,18 @@ bool dskHostGame::checkOptions() const GlobalGameSettings& ggs = gameLobby->getSettings(); if(ggs.objective == GO_ECONOMYMODE && !ggs.getSelection(AddonId::PEACEFULMODE)) { - WINDOWMANAGER.Show( - std::make_unique(_("Economy Mode"), - _("You chose the economy mode. Would you like to adjust settings to fit, " - "especially to peaceful mode? Choosing yes will make the adjustmenst and let " - "you review them, choosing no will start the game."), - this, MSB_YESNOCANCEL, MSB_QUESTIONGREEN, 10)); + WINDOWMANAGER.Show(std::make_unique( + _("Economy mode"), + _("You chose the economy mode. In economy mode the player or team that collects the most of certain goods " + "wins (check the economic progress window in game).\n\nSome players like to play this objective in " + "peaceful mode. Would you like to adjust settings for a peaceful " + "game?\n" + "Choosing Yes will activate peaceful mode, ban catapults and disable buildings receiving coins by default. " + "After clicking Yes you will be able to review the changes and then start the game by clicking the Start " + "game button again. If you like to play economy mode with attacks, " + "choosing No will start the game without any " + "changes."), + this, MSB_YESNOCANCEL, MSB_QUESTIONGREEN, 10)); return false; } else if(ggs.getSelection(AddonId::PEACEFULMODE) && (ggs.objective == GO_CONQUER3_4 || ggs.objective == GO_TOTALDOMINATION)) From f9a6af53e56c3a01a06cdb146d7f474b0c34761d Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Mon, 30 Nov 2020 13:32:01 +0100 Subject: [PATCH 12/15] changed economy mode message box text --- libs/s25main/desktops/dskHostGame.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/libs/s25main/desktops/dskHostGame.cpp b/libs/s25main/desktops/dskHostGame.cpp index 001eeee150..c9fd4b9e22 100644 --- a/libs/s25main/desktops/dskHostGame.cpp +++ b/libs/s25main/desktops/dskHostGame.cpp @@ -1057,30 +1057,28 @@ bool dskHostGame::checkOptions() if(forceOptions) return true; const GlobalGameSettings& ggs = gameLobby->getSettings(); - if(ggs.objective == GO_ECONOMYMODE && !ggs.getSelection(AddonId::PEACEFULMODE)) + if(ggs.objective == GO_ECONOMYMODE && !ggs.isEnabled(AddonId::PEACEFULMODE)) { WINDOWMANAGER.Show(std::make_unique( _("Economy mode"), _("You chose the economy mode. In economy mode the player or team that collects the most of certain goods " - "wins (check the economic progress window in game).\n\nSome players like to play this objective in " - "peaceful mode. Would you like to adjust settings for a peaceful " - "game?\n" + "wins (check the Economic progress window in game).\n\n" + "Some players like to play this objective in peaceful mode. Would you like to adjust settings for a " + "peaceful game?\n" "Choosing Yes will activate peaceful mode, ban catapults and disable buildings receiving coins by default. " "After clicking Yes you will be able to review the changes and then start the game by clicking the Start " - "game button again. If you like to play economy mode with attacks, " - "choosing No will start the game without any " - "changes."), + "game button again.\n" + "Choosing No will start the game without any changes."), this, MSB_YESNOCANCEL, MSB_QUESTIONGREEN, 10)); return false; - } else if(ggs.getSelection(AddonId::PEACEFULMODE) + } else if(ggs.isEnabled(AddonId::PEACEFULMODE) && (ggs.objective == GO_CONQUER3_4 || ggs.objective == GO_TOTALDOMINATION)) { - WINDOWMANAGER.Show( - std::make_unique(_("Peaceful Mode"), - _("You chose a war based victory condition but peaceful mode is still active. " - "Would you like to disactivate peaceful mode before you start? Choosing no will " - "start the game, yes will let you review the changes."), - this, MSB_YESNOCANCEL, MSB_QUESTIONRED, 11)); + WINDOWMANAGER.Show(std::make_unique( + _("Peaceful mode"), + _("You chose a war based victory condition but peaceful mode is still active. Would you like to deactivate " + "peaceful mode before you start? Choosing No will start the game, Yes will let you review the changes."), + this, MSB_YESNOCANCEL, MSB_QUESTIONRED, 11)); return false; } return true; From f6b007e80cab1c3ffebfa57a1153920806fb3ce2 Mon Sep 17 00:00:00 2001 From: JonathanSteinbuch Date: Fri, 4 Dec 2020 20:24:25 +0100 Subject: [PATCH 13/15] Reverted the "performance optimization" changes, added two Asserts, removed team determining every frame --- libs/s25main/EconomyModeHandler.cpp | 4 +--- libs/s25main/ingameWindows/iwEconomicProgress.cpp | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/s25main/EconomyModeHandler.cpp b/libs/s25main/EconomyModeHandler.cpp index ba0fd9bfac..1b39464df1 100644 --- a/libs/s25main/EconomyModeHandler.cpp +++ b/libs/s25main/EconomyModeHandler.cpp @@ -125,6 +125,7 @@ void EconomyModeHandler::Serialize(SerializedGameData& sgd) const void EconomyModeHandler::DetermineTeams() { + RTTR_Assert(economyModeTeams.empty()); for(unsigned i = 0; i < gwg->GetNumPlayers(); ++i) { if(gwg->GetPlayer(i).isUsed()) @@ -200,9 +201,6 @@ void EconomyModeHandler::UpdateAmounts() } } - // Compute Teams - DetermineTeams(); - // Compute the amounts for the teams std::fill(maxAmountsATeamCollected.begin(), maxAmountsATeamCollected.end(), 0); for(auto& team : economyModeTeams) diff --git a/libs/s25main/ingameWindows/iwEconomicProgress.cpp b/libs/s25main/ingameWindows/iwEconomicProgress.cpp index f265d79244..afffcd2bd2 100644 --- a/libs/s25main/ingameWindows/iwEconomicProgress.cpp +++ b/libs/s25main/ingameWindows/iwEconomicProgress.cpp @@ -166,6 +166,8 @@ void iwEconomicProgress::Msg_PaintBefore() const std::vector& economyModeTeams = eH->GetTeams(); const GamePlayer& mainPlayer = gwv.GetPlayer(); + RTTR_Assert(economyModeTeams.size() == teamOrder.size()); + // make sure the amounts are current eH->UpdateAmounts(); From 47e9ee72004dde61b5f3f7ee3e5480eba84ed498 Mon Sep 17 00:00:00 2001 From: Jonathan Steinbuch Date: Sun, 20 Dec 2020 21:57:05 +0100 Subject: [PATCH 14/15] Added credit line --- libs/s25main/desktops/dskCredits.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/s25main/desktops/dskCredits.cpp b/libs/s25main/desktops/dskCredits.cpp index 35e56e9b40..eeda329473 100644 --- a/libs/s25main/desktops/dskCredits.cpp +++ b/libs/s25main/desktops/dskCredits.cpp @@ -114,6 +114,7 @@ dskCredits::dskCredits() : Desktop(LOADER.GetImageN("setup013", 0)), itCurEntry( entry.lines.push_back("Divan"); entry.lines.push_back("Christoph Erhardt (Airhardt)"); entry.lines.push_back("Siegfried Oleg Pammer (siegi44)"); + entry.lines.push_back("Jonathan Steinbuch"); entry.lines.push_back("Lienhart Woitok (liwo)"); entry.lines.push_back(""); entry.lines.push_back(_("all developers who contributed via Github")); From 099b96e14d1134133a9d908912dc9dc7c99a61f2 Mon Sep 17 00:00:00 2001 From: Jonathan Steinbuch Date: Mon, 21 Dec 2020 10:03:57 +0100 Subject: [PATCH 15/15] small changes to loop determining which goods to collect --- libs/s25main/EconomyModeHandler.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/libs/s25main/EconomyModeHandler.cpp b/libs/s25main/EconomyModeHandler.cpp index 1b39464df1..97b4da4441 100644 --- a/libs/s25main/EconomyModeHandler.cpp +++ b/libs/s25main/EconomyModeHandler.cpp @@ -49,21 +49,18 @@ EconomyModeHandler::EconomyModeHandler(unsigned endFrame) : endFrame(endFrame), static_assert(numGoodTypesToCollect > 0, "There have to be goods to be collected"); static_assert(commonGoodPool.size() >= numGoodTypesToCollect - 1, "There have to be enough commond goods"); static_assert(!specialGoodPool.empty(), "There have to be enough special goods"); - goodsToCollect.clear(); - goodsToCollect.resize(numGoodTypesToCollect); - auto nextSlot = begin(goodsToCollect); + goodsToCollect.reserve(numGoodTypesToCollect); - while(nextSlot != end(goodsToCollect) - 1) + while(goodsToCollect.size() < numGoodTypesToCollect - 1) { GoodType nextGoodType = commonGoodPool[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), commonGoodPool.size())]; // No duplicates should be in goodsToCollect, so only add a good if it isn't one of the already found goods - if(std::find(begin(goodsToCollect), nextSlot, nextGoodType) == nextSlot) + if(!helpers::contains(goodsToCollect, nextGoodType)) { - *nextSlot = nextGoodType; - nextSlot++; + goodsToCollect.push_back(nextGoodType); } } - *nextSlot = specialGoodPool[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), specialGoodPool.size())]; + goodsToCollect.push_back(specialGoodPool[RANDOM.Rand(__FILE__, __LINE__, GetObjId(), specialGoodPool.size())]); // Schedule end game event and trust the event manager to keep track of it if(!isInfinite())