diff --git a/libs/s25main/GamePlayer.cpp b/libs/s25main/GamePlayer.cpp index c444ebc90e..cd23c09feb 100644 --- a/libs/s25main/GamePlayer.cpp +++ b/libs/s25main/GamePlayer.cpp @@ -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? @@ -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; @@ -2089,6 +2094,24 @@ 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; + // checks if this ware is + if(ware->IsWaitingInWarehouse() && ware->GetGoal() + && !IsWareFineWithEmergencyProtocol(ware->type, *ware->GetGoal())) + { + ware->NotifyGoalAboutLostWare(); + static_cast(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 :-( @@ -2118,6 +2141,9 @@ void GamePlayer::TestForEmergencyProgramm() emergency = true; SendPostMessage(std::make_unique( world.GetEvMgr().GetCurrentGF(), _("The emergency program has been activated."), PostCategory::Economy)); + + // Handle wares already ordered + CancelWaresForEmergencyProtocol(); } } else { diff --git a/libs/s25main/GamePlayer.h b/libs/s25main/GamePlayer.h index ada0ba552a..04cbda484b 100644 --- a/libs/s25main/GamePlayer.h +++ b/libs/s25main/GamePlayer.h @@ -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]; } + // remove all wares that are already scheduled but ignoring emergency protocol + void CancelWaresForEmergencyProtocol(); // Testet ob Notfallprogramm aktiviert werden muss und tut dies dann void TestForEmergencyProgramm(); bool hasEmergency() const { return emergency; } diff --git a/tests/s25Main/integration/testEmergencyProtocol.cpp b/tests/s25Main/integration/testEmergencyProtocol.cpp new file mode 100644 index 0000000000..f8f419d311 --- /dev/null +++ b/tests/s25Main/integration/testEmergencyProtocol.cpp @@ -0,0 +1,87 @@ +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) +// +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "EconomyModeHandler.h" +#include "EventManager.h" +#include "GamePlayer.h" +#include "Savegame.h" +#include "SerializedGameData.h" +#include "addons/AddonEconomyModeGameLength.h" +#include "factories/BuildingFactory.h" +#include "worldFixtures/MockLocalGameState.h" +#include "worldFixtures/WorldFixture.h" +#include "worldFixtures/WorldWithGCExecution.h" +#include "worldFixtures/initGameRNG.hpp" +#include "gameTypes/GO_Type.h" +#include +#include +#include + +struct EmergencyFixture : public WorldFixture +{ + nobHQ* HQ = world.GetPlayer(0).GetHQ(); + EmergencyFixture() + { + HQ->AddToInventory(HQ->getStartInventory(StartWares::VLow), true); + MapPoint pos; + + pos = world.GetPlayer(0).GetHQPos() + MapPoint(3, 0); + world.SetBuildingSite(BuildingType::Farm, pos, 0); + world.BuildRoad(0, false, world.GetNeighbour(pos, Direction::SouthEast), + std::vector(3, Direction::West)); + + pos = world.GetPlayer(0).GetHQPos() + MapPoint(-3, 0); + world.SetBuildingSite(BuildingType::Farm, pos, 0); + world.BuildRoad(0, false, world.GetNeighbour(pos, Direction::SouthEast), + std::vector(3, Direction::East)); + + // wait until emergency protocol should be activated + RTTR_EXEC_TILL(500, HQ->GetInventory()[GoodType::Boards] == 10); + + // activate program (with 10 boards it should trigger) + world.GetPlayer(0).TestForEmergencyProgramm(); + + // wait for some more ticks to give time if not working to deliver more boards + RTTR_SKIP_GFS(200); + // check boards are still fine and protocol working + BOOST_TEST_CHECK(world.GetPlayer(0).GetHQ()->GetInventory()[GoodType::Boards] == 10); + } +}; + +BOOST_FIXTURE_TEST_CASE(EmergencyProtoclActiveWoodcutterAndSawmillCanBuild, EmergencyFixture) +{ + initGameRNG(); + + MapPoint posWoodcutter = world.GetPlayer(0).GetHQPos() + MapPoint(-1, 2); + world.SetBuildingSite(BuildingType::Woodcutter, posWoodcutter, 0); + world.BuildRoad(0, false, world.GetNeighbour(posWoodcutter, Direction::SouthEast), + std::vector(2, Direction::NorthEast)); + + MapPoint posSawmill = world.GetPlayer(0).GetHQPos() + MapPoint(-2, 4); + world.SetBuildingSite(BuildingType::Sawmill, posSawmill, 0); + world.BuildRoad(0, false, world.GetNeighbour(posSawmill, Direction::SouthEast), + std::vector(2, Direction::NorthEast)); + + // check if inventory boards are given out + RTTR_EXEC_TILL(200, HQ->GetInventory()[GoodType::Boards] < 10); + + // check if building where found + RTTR_EXEC_TILL(2000, world.GetNO(posWoodcutter)->GetType() == NodalObjectType::Building); + RTTR_EXEC_TILL(2000, world.GetNO(posSawmill)->GetType() == NodalObjectType::Building); +} + +BOOST_FIXTURE_TEST_CASE(EmergencyProtoclActiveOtherBuldingsnotBuild, EmergencyFixture) +{ + initGameRNG(); + + MapPoint pos = world.GetPlayer(0).GetHQPos() + MapPoint(-1, 2); + world.SetBuildingSite(BuildingType::Watchtower, pos, 0); + world.BuildRoad(0, false, world.GetNeighbour(pos, Direction::SouthEast), + std::vector(2, Direction::NorthEast)); + + // wait for some more ticks to give time if not working to deliver more boards + RTTR_SKIP_GFS(500); + // check boards are still fine and protocol working + BOOST_TEST_CHECK(world.GetPlayer(0).GetHQ()->GetInventory()[GoodType::Boards] == 10); +}