Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion external/libutil
11 changes: 5 additions & 6 deletions extras/ai-battle/HeadlessGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ void HeadlessGame::RecordReplay(const bfs::path& path, unsigned random_init)
mapInfo.luaData.CompressFromFile(luaPath_, &mapInfo.luaChecksum);
}

for(unsigned playerId = 0; playerId < world_.GetNumPlayers(); ++playerId)
replay_.AddPlayer(world_.GetPlayer(playerId));
for(auto& player : world_.getPlayers())
replay_.AddPlayer(player);
replay_.ggs = game_.ggs_;
if(!replay_.StartRecording(path, mapInfo, random_init))
throw std::runtime_error("Replayfile could not be opened!");
Expand All @@ -170,8 +170,8 @@ void HeadlessGame::SaveGame(const bfs::path& path) const
bfs::remove(path);

Savegame save;
for(unsigned playerId = 0; playerId < world_.GetNumPlayers(); ++playerId)
save.AddPlayer(world_.GetPlayer(playerId));
for(auto& player : world_.getPlayers())
save.AddPlayer(player);
save.ggs = game_.ggs_;
save.ggs.exploration = Exploration::Disabled; // no FOW
save.start_gf = em_.GetCurrentGF();
Expand Down Expand Up @@ -220,9 +220,8 @@ void HeadlessGame::PrintState()
printConsole("┌────────────────────────┬─────────────────┬─────────────┬───────────┬───────────┐\n");
printConsole("│ Player │ Country │ Buildings │ Military │ Gold │\n");
printConsole("├────────────────────────┼─────────────────┼─────────────┼───────────┼───────────┤\n");
for(unsigned playerId = 0; playerId < world_.GetNumPlayers(); ++playerId)
for(const auto& player : world_.getPlayers())
{
const GamePlayer& player = world_.GetPlayer(playerId);
printConsole("│ %s%-22s%s │ %15s │ %11s │ %9s │ %9s │\n", player.IsDefeated() ? "\x1b[9m" : "",
player.name.c_str(), player.IsDefeated() ? "\x1b[29m" : "",
HumanReadableNumber(player.GetStatisticCurrentValue(StatisticType::Country)).c_str(),
Expand Down
11 changes: 5 additions & 6 deletions libs/s25main/Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ namespace {
unsigned getNumAlivePlayers(const GameWorldBase& world)
{
unsigned numPlayersAlive = 0;
for(unsigned i = 0; i < world.GetNumPlayers(); ++i)
for(const auto& player : world.getPlayers())
{
if(!world.GetPlayer(i).IsDefeated())
if(!player.IsDefeated())
++numPlayersAlive;
}
return numPlayersAlive;
Expand All @@ -76,9 +76,8 @@ void Game::RunGF()
// EventManager Bescheid sagen
em_->ExecuteNextGF();
// Notfallprogramm durchlaufen lassen
for(unsigned i = 0; i < world_.GetNumPlayers(); ++i)
for(GamePlayer& player : world_.getPlayers())
{
GamePlayer& player = world_.GetPlayer(i);
if(player.isUsed())
{
// Auf Notfall testen (Wenige Bretter/Steine und keine Holzindustrie)
Expand All @@ -100,8 +99,8 @@ void Game::RunGF()

void Game::StatisticStep()
{
for(unsigned i = 0; i < world_.GetNumPlayers(); ++i)
world_.GetPlayer(i).StatisticStep();
for(auto& player : world_.getPlayers())
player.StatisticStep();

CheckObjective();
}
Expand Down
29 changes: 27 additions & 2 deletions libs/s25main/GamePlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,12 @@ void GamePlayer::FindWarehouseForAllJobs(const Job job)
}
}

static bool IsWareFineWithEmergencyProtocol(GoodType goodType, const noBaseBuilding& goal)
{
return (goodType != GoodType::Boards && goodType != GoodType::Stones)
|| goal.GetBuildingType() == BuildingType::Woodcutter || goal.GetBuildingType() == BuildingType::Sawmill;
}

Ware* GamePlayer::OrderWare(const GoodType ware, noBaseBuilding& goal)
{
/// Gibt es ein Lagerhaus mit dieser Ware?
Expand All @@ -902,8 +908,7 @@ Ware* GamePlayer::OrderWare(const GoodType ware, noBaseBuilding& goal)
else
{
// Wenn Notfallprogramm aktiv nur an Holzfäller und Sägewerke Bretter/Steine liefern
if((ware != GoodType::Boards && ware != GoodType::Stones)
|| goal.GetBuildingType() == BuildingType::Woodcutter || goal.GetBuildingType() == BuildingType::Sawmill)
if(IsWareFineWithEmergencyProtocol(ware, goal))
return wh->OrderWare(ware, goal);
else
return nullptr;
Expand Down Expand Up @@ -2089,6 +2094,23 @@ bool GamePlayer::FindHarborForUnloading(noShip* ship, const MapPoint start, Harb
return false;
}

void GamePlayer::CancelWaresForEmergencyProtocol()
{
for(auto it = ware_list.begin(); it != ware_list.end();)
{
Ware* ware = *it;
if(ware->IsWaitingInWarehouse() && ware->GetGoal()
&& !IsWareFineWithEmergencyProtocol(ware->type, *ware->GetGoal()))
{
ware->NotifyGoalAboutLostWare();
static_cast<nobBaseWarehouse*>(ware->GetLocation())->CancelWare(ware);
it = ware_list.erase(it);
continue;
}
it++;
}
}

void GamePlayer::TestForEmergencyProgramm()
{
// we are already defeated, do not even think about an emergency program - it's too late :-(
Expand Down Expand Up @@ -2118,6 +2140,9 @@ void GamePlayer::TestForEmergencyProgramm()
emergency = true;
SendPostMessage(std::make_unique<PostMsg>(
world.GetEvMgr().GetCurrentGF(), _("The emergency program has been activated."), PostCategory::Economy));

// Handle wares already ordered
CancelWaresForEmergencyProtocol();
}
} else
{
Expand Down
2 changes: 2 additions & 0 deletions libs/s25main/GamePlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ class GamePlayer : public GamePlayerInfo
const Statistic& GetStatistic(StatisticTime time) const { return statistic[time]; };
unsigned GetStatisticCurrentValue(StatisticType idx) const { return statisticCurrentData[idx]; }

// Stop wares restricted in emergency mode that are waiting in warehouse to be transported already
void CancelWaresForEmergencyProtocol();
// Testet ob Notfallprogramm aktiviert werden muss und tut dies dann
void TestForEmergencyProgramm();
bool hasEmergency() const { return emergency; }
Expand Down
12 changes: 6 additions & 6 deletions libs/s25main/SerializedGameData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,13 +258,13 @@ void SerializedGameData::MakeSnapshot(const Game& game)
PushObject(gw.getEconHandler(), true);
}
// Spieler serialisieren
for(unsigned i = 0; i < gw.GetNumPlayers(); ++i)
for(const auto& player : gw.getPlayers())
{
if(debugMode)
LOG.write("Start serializing player %1% at %2%\n") % i % GetLength();
gw.GetPlayer(i).Serialize(*this);
LOG.write("Start serializing player %1% at %2%\n") % player.GetPlayerId() % GetLength();
player.Serialize(*this);
if(debugMode)
LOG.write("Done serializing player %1% at %2%\n") % i % GetLength();
LOG.write("Done serializing player %1% at %2%\n") % player.GetPlayerId() % GetLength();
}

if(writtenEventIds.size() != writeEm->GetNumActiveEvents())
Expand Down Expand Up @@ -301,8 +301,8 @@ void SerializedGameData::ReadSnapshot(Game& game, ILocalGameState& localGameStat
std::unique_ptr<EconomyModeHandler>(PopObject<EconomyModeHandler>(GO_Type::Economymodehandler)));
}

for(unsigned i = 0; i < gw.GetNumPlayers(); ++i)
gw.GetPlayer(i).Deserialize(*this);
for(auto& player : gw.getPlayers())
player.Deserialize(*this);

// If this check fails, we did not serialize all objects or there was an async
if(readEvents.size() != em->GetNumActiveEvents())
Expand Down
4 changes: 2 additions & 2 deletions libs/s25main/ingameWindows/iwStatistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ iwStatistics::iwStatistics(const GameWorldViewer& gwv)
// Count active players
numPlayingPlayers = 0;
const GameWorldBase& world = gwv.GetWorld();
for(const auto i : helpers::range(world.GetNumPlayers()))
for(const auto& player : world.getPlayers())
{
if(world.GetPlayer(i).isUsed())
if(player.isUsed())
numPlayingPlayers++;
}

Expand Down
8 changes: 4 additions & 4 deletions libs/s25main/network/GameClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,8 @@ void GameClient::StartGame(const unsigned random_init)
{
RTTR_Assert(mapinfo.type != MapType::Savegame);
/// Startbündnisse setzen
for(unsigned i = 0; i < gameWorld.GetNumPlayers(); ++i)
gameWorld.GetPlayer(i).MakeStartPacts();
for(auto& player : gameWorld.getPlayers())
player.MakeStartPacts();

MapLoader loader(gameWorld);
if(!loader.Load(mapinfo.filepath)
Expand Down Expand Up @@ -1580,8 +1580,8 @@ bool GameClient::StartReplay(const boost::filesystem::path& path)
idx++;
}

for(unsigned i = 0; i < game->world_.GetNumPlayers(); i++)
game->world_.GetPlayer(i).ChangeDistribution(newDistributions);
for(auto& player : game->world_.getPlayers())
player.ChangeDistribution(newDistributions);
}

replayinfo->next_gf = replayinfo->replay.ReadGF();
Expand Down
10 changes: 10 additions & 0 deletions libs/s25main/world/GameWorldBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ unsigned GameWorldBase::GetNumPlayers() const
return players.size();
}

s25util::span<GamePlayer> GameWorldBase::getPlayers()
{
return players;
}

s25util::span<const GamePlayer> GameWorldBase::getPlayers() const
{
return players;
}

bool GameWorldBase::IsSinglePlayer() const
{
bool foundPlayer = false;
Expand Down
5 changes: 4 additions & 1 deletion libs/s25main/world/GameWorldBase.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org)
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

Expand All @@ -12,6 +12,7 @@
#include "notifications/NotificationManager.h"
#include "postSystem/PostManager.h"
#include "world/World.h"
#include "s25util/span.hpp"
#include <memory>
#include <set>
#include <vector>
Expand Down Expand Up @@ -158,6 +159,8 @@ class GameWorldBase : public World
GamePlayer& GetPlayer(unsigned id);
const GamePlayer& GetPlayer(unsigned id) const;
unsigned GetNumPlayers() const;
s25util::span<GamePlayer> getPlayers();
s25util::span<const GamePlayer> getPlayers() const;
bool IsSinglePlayer() const;
/// Return the game settings
const GlobalGameSettings& GetGGS() const { return gameSettings; }
Expand Down
4 changes: 2 additions & 2 deletions tests/s25Main/autoplay/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ static void playReplay(const boost::filesystem::path& replayPath, const bool isS
BOOST_TEST_REQUIRE(replay.GetMinorVersion() < 3u);
MapLoader::SetupResources(gameWorld, false);

for(unsigned i = 0; i < gameWorld.GetNumPlayers(); ++i)
gameWorld.GetPlayer(i).MakeStartPacts();
for(auto& player : gameWorld.getPlayers())
player.MakeStartPacts();
}

gameWorld.InitAfterLoad();
Expand Down
6 changes: 3 additions & 3 deletions tests/s25Main/integration/testArmor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ struct ArmorTradeFixture : public ArmoredSoldierFixture

void testExpectedFiguresInGlobalInventoryMatchWithHQInventory() const
{
for(unsigned i = 0; i < world.GetNumPlayers(); i++)
for(const auto& player : world.getPlayers())
{
auto const& playerWh = world.GetSpecObj<nobBaseWarehouse>(players[i]->GetHQPos());
auto const& globalInventoryPlayer = world.GetPlayer(i).GetInventory();
auto const& playerWh = world.GetSpecObj<nobBaseWarehouse>(player.GetHQPos());
auto const& globalInventoryPlayer = player.GetInventory();
for(unsigned i = 0; i < NUM_SOLDIER_RANKS; i++)
{
BOOST_TEST(playerWh->GetNumRealArmoredFigures(jobEnumToAmoredSoldierEnum(SOLDIER_JOBS[i]))
Expand Down
4 changes: 2 additions & 2 deletions tests/s25Main/integration/testEconomyMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ BOOST_FIXTURE_TEST_CASE(EconomyModeSerialization, EconModeFixture)
world.getEconHandler()->UpdateAmounts();

Savegame save;
for(unsigned i = 0; i < world.GetNumPlayers(); i++)
save.AddPlayer(world.GetPlayer(i));
for(const auto& player : world.getPlayers())
save.AddPlayer(player);
save.ggs = ggs;
save.start_gf = game->em_->GetCurrentGF();
save.sgd.MakeSnapshot(*game);
Expand Down
72 changes: 72 additions & 0 deletions tests/s25Main/integration/testEmergencyProtocol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#include "NodalObjectTypes.h"
#include "buildings/nobHQ.h"
#include "worldFixtures/WorldWithGCExecution.h"
#include "worldFixtures/initGameRNG.hpp"
#include <boost/test/unit_test.hpp>

/// Start with low wares and build 2 farms to trigger emergency protocol activation
struct EmergencyFixture : public WorldWithGCExecution1P
{
nobHQ* hq = world.GetPlayer(0).GetHQ();
EmergencyFixture()
{
hq->AddToInventory(hq->getStartInventory(StartWares::VLow), true);
MapPoint pos;

pos = hqPos + MapPoint(3, 0);
world.SetBuildingSite(BuildingType::Farm, pos, 0);
BuildRoadForBlds(pos, hqPos);

pos = hqPos + MapPoint(-3, 0);
world.SetBuildingSite(BuildingType::Farm, pos, 0);
BuildRoadForBlds(pos, hqPos);

// wait until emergency protocol should be activated
RTTR_EXEC_TILL(500, hq->GetInventory()[GoodType::Boards] == 10);

BOOST_TEST_REQUIRE(world.GetPlayer(0).hasEmergency());

// No more boards are carried out to the farms due to emergency protocol
RTTR_SKIP_GFS(200);
BOOST_TEST(hq->GetInventory()[GoodType::Boards] == 10);

initGameRNG();
}
};

BOOST_FIXTURE_TEST_SUITE(EmergencyProtocol, EmergencyFixture)
BOOST_AUTO_TEST_CASE(CanBuildWoodcutterAndSawmill)
{
const MapPoint posWoodcutter = hqPos + MapPoint(-1, 2);
world.SetBuildingSite(BuildingType::Woodcutter, posWoodcutter, 0);
BuildRoadForBlds(posWoodcutter, hqPos);

const MapPoint posSawmill = hqPos + MapPoint(-2, 4);
world.SetBuildingSite(BuildingType::Sawmill, posSawmill, 0);
BuildRoadForBlds(posSawmill, hqPos);

// check if inventory boards are given out
RTTR_EXEC_TILL(200, hq->GetInventory()[GoodType::Boards] < 10);

// check that buildings are built
RTTR_EXEC_TILL(2000, world.GetNO(posWoodcutter)->GetType() == NodalObjectType::Building);
RTTR_EXEC_TILL(2000, world.GetNO(posSawmill)->GetType() == NodalObjectType::Building);
}

BOOST_FIXTURE_TEST_CASE(CannotBuildOtherBuldings, EmergencyFixture)
{
const MapPoint pos = hqPos + MapPoint(-3, 0);
world.SetBuildingSite(BuildingType::Watchtower, pos, 0);

BuildRoadForBlds(pos, hqPos);

// No boards are carried out to the farms or watchtower due to emergency protocol
RTTR_SKIP_GFS(500);
BOOST_TEST(hq->GetInventory()[GoodType::Boards] == 10);
}

BOOST_AUTO_TEST_SUITE_END()
4 changes: 2 additions & 2 deletions tests/s25Main/integration/testGameCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -738,8 +738,8 @@ void InitPactsAndPost(GameWorldBase& world)
BOOST_FIXTURE_TEST_CASE(NotifyAllies, WorldWithGCExecution3P)
{
// At first there are no teams
for(unsigned i = 0; i < world.GetNumPlayers(); i++)
BOOST_TEST_REQUIRE(world.GetPlayer(i).team == Team::None);
for(const auto& player : world.getPlayers())
BOOST_TEST_REQUIRE(player.team == Team::None);
PostManager& postMgr = world.GetPostMgr();
// Add postbox for each player
for(unsigned i = 0; i < world.GetNumPlayers(); i++)
Expand Down
3 changes: 2 additions & 1 deletion tests/s25Main/integration/testProduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ BOOST_FIXTURE_TEST_CASE(MetalWorkerStopped, WorldWithGCExecution1P)
BOOST_FIXTURE_TEST_CASE(MetalWorkerOrders, WorldWithGCExecution1P)
{
GoodsAndPeopleCounts inv;
inv[GoodType::Boards] = 10;
inv[GoodType::Boards] = 20;
inv[GoodType::Stones] = 20;
inv[GoodType::Iron] = 10;
inv[Job::Metalworker] = 1;
world.GetSpecObj<nobBaseWarehouse>(hqPos)->AddToInventory(inv, true);
Expand Down
8 changes: 4 additions & 4 deletions tests/s25Main/integration/testSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ BOOST_FIXTURE_TEST_CASE(BaseSaveLoad, RandWorldFixture)

Savegame save;

for(unsigned i = 0; i < world.GetNumPlayers(); i++)
save.AddPlayer(world.GetPlayer(i));
for(const auto& player : world.getPlayers())
save.AddPlayer(player);

save.ggs = ggs;
save.start_gf = em.GetCurrentGF();
Expand Down Expand Up @@ -563,8 +563,8 @@ BOOST_FIXTURE_TEST_CASE(ReplayWithSavegame, RandWorldFixture)
map.filepath = "Map.swd";
map.luaFilepath = "Map.lua";
map.savegame = std::make_unique<Savegame>();
for(unsigned i = 0; i < world.GetNumPlayers(); i++)
map.savegame->AddPlayer(world.GetPlayer(i));
for(const auto& player : world.getPlayers())
map.savegame->AddPlayer(player);
// We can change players
std::vector<BasePlayerInfo> players(4);
players[0].ps = PlayerState::AI;
Expand Down
Loading
Loading