From ea2ee5f504145668b525c25aadacce132cab15f7 Mon Sep 17 00:00:00 2001 From: Manu Date: Fri, 1 May 2026 09:47:51 +0200 Subject: [PATCH 1/6] Fix inexhaustible granite mine production --- libs/s25main/figures/nofMiner.cpp | 27 ++++++++----- libs/s25main/figures/nofMiner.h | 1 + tests/s25Main/integration/testProduction.cpp | 42 ++++++++++++++++++++ 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/libs/s25main/figures/nofMiner.cpp b/libs/s25main/figures/nofMiner.cpp index 912619b58a..65836a8f4e 100644 --- a/libs/s25main/figures/nofMiner.cpp +++ b/libs/s25main/figures/nofMiner.cpp @@ -71,23 +71,32 @@ helpers::OptionalEnum nofMiner::ProduceWare() bool nofMiner::AreWaresAvailable() const { - return nofWorkman::AreWaresAvailable() && FindPointWithResource(GetRequiredResType()).isValid(); + return nofWorkman::AreWaresAvailable() + && (CanMineWithoutResource() || FindPointWithResource(GetRequiredResType()).isValid()); } bool nofMiner::StartWorking() { - MapPoint resPt = FindPointWithResource(GetRequiredResType()); - if(!resPt.isValid()) - return false; const GlobalGameSettings& settings = world->GetGGS(); - bool inexhaustibleRes = settings.isEnabled(AddonId::INEXHAUSTIBLE_MINES) - || (workplace->GetBuildingType() == BuildingType::GraniteMine - && settings.isEnabled(AddonId::INEXHAUSTIBLE_GRANITEMINES)); - if(!inexhaustibleRes) - world->ReduceResource(resPt); + const bool canMineWithoutResource = CanMineWithoutResource(); + const bool inexhaustibleRes = settings.isEnabled(AddonId::INEXHAUSTIBLE_MINES) || canMineWithoutResource; + if(!canMineWithoutResource) + { + MapPoint resPt = FindPointWithResource(GetRequiredResType()); + if(!resPt.isValid()) + return false; + if(!inexhaustibleRes) + world->ReduceResource(resPt); + } return nofWorkman::StartWorking(); } +bool nofMiner::CanMineWithoutResource() const +{ + return workplace->GetBuildingType() == BuildingType::GraniteMine + && world->GetGGS().isEnabled(AddonId::INEXHAUSTIBLE_GRANITEMINES); +} + ResourceType nofMiner::GetRequiredResType() const { switch(workplace->GetBuildingType()) diff --git a/libs/s25main/figures/nofMiner.h b/libs/s25main/figures/nofMiner.h index c3fdc810ad..d965a0fdcd 100644 --- a/libs/s25main/figures/nofMiner.h +++ b/libs/s25main/figures/nofMiner.h @@ -23,6 +23,7 @@ class nofMiner : public nofWorkman bool AreWaresAvailable() const override; bool StartWorking() override; ResourceType GetRequiredResType() const; + bool CanMineWithoutResource() const; public: nofMiner(MapPoint pos, unsigned char player, nobUsual* workplace); diff --git a/tests/s25Main/integration/testProduction.cpp b/tests/s25Main/integration/testProduction.cpp index 5ce05b76b4..75ad8525a3 100644 --- a/tests/s25Main/integration/testProduction.cpp +++ b/tests/s25Main/integration/testProduction.cpp @@ -102,4 +102,46 @@ BOOST_FIXTURE_TEST_CASE(MetalWorkerOrders, WorldWithGCExecution1P) RTTR_EXEC_TILL(1300, mw->is_working); } +BOOST_FIXTURE_TEST_CASE(GraniteMineWithoutResourcesNeedsAddon, WorldWithGCExecution1P) +{ + GoodsAndPeopleCounts inv; + inv[GoodType::Fish] = 10; + inv[GoodType::PickAxe] = 1; + inv[Job::Miner] = 1; + world.GetSpecObj(hqPos)->AddToInventory(inv, true); + + MapPoint minePos = hqPos + MapPoint(2, 0); + const auto* mine = static_cast( + BuildingFactory::CreateBuilding(world, BuildingType::GraniteMine, minePos, curPlayer, Nation::Romans)); + this->BuildRoad(world.GetNeighbour(minePos, Direction::SouthEast), false, + std::vector(2, Direction::West)); + + const Inventory& curInventory = world.GetPlayer(curPlayer).GetInventory(); + const unsigned initialStones = curInventory[GoodType::Stones]; + RTTR_EXEC_TILL(500, mine->HasWorker()); + RTTR_SKIP_GFS(2000); + + BOOST_TEST(curInventory[GoodType::Stones] == initialStones); +} + +BOOST_FIXTURE_TEST_CASE(InexhaustibleGraniteMineWorksWithoutResources, WorldWithGCExecution1P) +{ + ggs.setSelection(AddonId::INEXHAUSTIBLE_GRANITEMINES, 1); + + GoodsAndPeopleCounts inv; + inv[GoodType::Fish] = 10; + inv[GoodType::PickAxe] = 1; + inv[Job::Miner] = 1; + world.GetSpecObj(hqPos)->AddToInventory(inv, true); + + MapPoint minePos = hqPos + MapPoint(2, 0); + BuildingFactory::CreateBuilding(world, BuildingType::GraniteMine, minePos, curPlayer, Nation::Romans); + this->BuildRoad(world.GetNeighbour(minePos, Direction::SouthEast), false, + std::vector(2, Direction::West)); + + const Inventory& curInventory = world.GetPlayer(curPlayer).GetInventory(); + const unsigned initialStones = curInventory[GoodType::Stones]; + RTTR_EXEC_TILL(3000, curInventory[GoodType::Stones] > initialStones); +} + BOOST_AUTO_TEST_SUITE_END() From b887aaf018ef588af1033d8229a1bb79a5b580e6 Mon Sep 17 00:00:00 2001 From: Manu Date: Fri, 1 May 2026 13:50:26 +0200 Subject: [PATCH 2/6] Simplify granite mine resource handling --- libs/s25main/figures/nofMiner.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libs/s25main/figures/nofMiner.cpp b/libs/s25main/figures/nofMiner.cpp index 65836a8f4e..dfbb2ca1c4 100644 --- a/libs/s25main/figures/nofMiner.cpp +++ b/libs/s25main/figures/nofMiner.cpp @@ -78,14 +78,12 @@ bool nofMiner::AreWaresAvailable() const bool nofMiner::StartWorking() { const GlobalGameSettings& settings = world->GetGGS(); - const bool canMineWithoutResource = CanMineWithoutResource(); - const bool inexhaustibleRes = settings.isEnabled(AddonId::INEXHAUSTIBLE_MINES) || canMineWithoutResource; - if(!canMineWithoutResource) + if(!CanMineWithoutResource()) { MapPoint resPt = FindPointWithResource(GetRequiredResType()); if(!resPt.isValid()) return false; - if(!inexhaustibleRes) + if(!settings.isEnabled(AddonId::INEXHAUSTIBLE_MINES)) world->ReduceResource(resPt); } return nofWorkman::StartWorking(); From 4e4b9d1f0a7a07fb37e6b67311bb18ee18d9f2ef Mon Sep 17 00:00:00 2001 From: Manu Date: Fri, 1 May 2026 14:17:03 +0200 Subject: [PATCH 3/6] Tighten granite mine production tests --- tests/s25Main/integration/testProduction.cpp | 59 +++++++++----------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/tests/s25Main/integration/testProduction.cpp b/tests/s25Main/integration/testProduction.cpp index 75ad8525a3..b24e4e54ae 100644 --- a/tests/s25Main/integration/testProduction.cpp +++ b/tests/s25Main/integration/testProduction.cpp @@ -23,6 +23,27 @@ static std::ostream& operator<<(std::ostream& os, const PostCategory& cat) BOOST_AUTO_TEST_SUITE(Production) +namespace { +struct GraniteMineWithoutResourcesFixture : WorldWithGCExecution1P +{ + const nobUsual* CreateGraniteMineWithoutResources() + { + GoodsAndPeopleCounts inv; + inv[GoodType::Fish] = 10; + inv[GoodType::PickAxe] = 1; + inv[Job::Miner] = 1; + world.GetSpecObj(hqPos)->AddToInventory(inv, true); + + MapPoint minePos = hqPos + MapPoint(2, 0); + const auto* mine = static_cast( + BuildingFactory::CreateBuilding(world, BuildingType::GraniteMine, minePos, curPlayer, Nation::Romans)); + BuildRoad(world.GetNeighbour(minePos, Direction::SouthEast), false, std::vector(2, Direction::West)); + RTTR_EXEC_TILL(500, mine->HasWorker()); + return mine; + } +}; +} // namespace + BOOST_FIXTURE_TEST_CASE(MetalWorkerStopped, WorldWithGCExecution1P) { addStartResources(); @@ -102,46 +123,18 @@ BOOST_FIXTURE_TEST_CASE(MetalWorkerOrders, WorldWithGCExecution1P) RTTR_EXEC_TILL(1300, mw->is_working); } -BOOST_FIXTURE_TEST_CASE(GraniteMineWithoutResourcesNeedsAddon, WorldWithGCExecution1P) +BOOST_FIXTURE_TEST_CASE(GraniteMineWithoutResourcesNeedsAddon, GraniteMineWithoutResourcesFixture) { - GoodsAndPeopleCounts inv; - inv[GoodType::Fish] = 10; - inv[GoodType::PickAxe] = 1; - inv[Job::Miner] = 1; - world.GetSpecObj(hqPos)->AddToInventory(inv, true); - - MapPoint minePos = hqPos + MapPoint(2, 0); - const auto* mine = static_cast( - BuildingFactory::CreateBuilding(world, BuildingType::GraniteMine, minePos, curPlayer, Nation::Romans)); - this->BuildRoad(world.GetNeighbour(minePos, Direction::SouthEast), false, - std::vector(2, Direction::West)); - + const nobUsual* mine = CreateGraniteMineWithoutResources(); const Inventory& curInventory = world.GetPlayer(curPlayer).GetInventory(); const unsigned initialStones = curInventory[GoodType::Stones]; - RTTR_EXEC_TILL(500, mine->HasWorker()); - RTTR_SKIP_GFS(2000); + RTTR_SKIP_GFS(2000); BOOST_TEST(curInventory[GoodType::Stones] == initialStones); -} -BOOST_FIXTURE_TEST_CASE(InexhaustibleGraniteMineWorksWithoutResources, WorldWithGCExecution1P) -{ ggs.setSelection(AddonId::INEXHAUSTIBLE_GRANITEMINES, 1); - - GoodsAndPeopleCounts inv; - inv[GoodType::Fish] = 10; - inv[GoodType::PickAxe] = 1; - inv[Job::Miner] = 1; - world.GetSpecObj(hqPos)->AddToInventory(inv, true); - - MapPoint minePos = hqPos + MapPoint(2, 0); - BuildingFactory::CreateBuilding(world, BuildingType::GraniteMine, minePos, curPlayer, Nation::Romans); - this->BuildRoad(world.GetNeighbour(minePos, Direction::SouthEast), false, - std::vector(2, Direction::West)); - - const Inventory& curInventory = world.GetPlayer(curPlayer).GetInventory(); - const unsigned initialStones = curInventory[GoodType::Stones]; - RTTR_EXEC_TILL(3000, curInventory[GoodType::Stones] > initialStones); + RTTR_EXEC_TILL(2000, mine->is_working || curInventory[GoodType::Stones] > initialStones); + RTTR_EXEC_TILL(2000, curInventory[GoodType::Stones] > initialStones); } BOOST_AUTO_TEST_SUITE_END() From b636b5dc6a45510645c0ae9723d8ed1f78064e3a Mon Sep 17 00:00:00 2001 From: Manu Date: Fri, 1 May 2026 14:17:18 +0200 Subject: [PATCH 4/6] Clarify inexhaustible granite mines addon description --- libs/s25main/addons/AddonInexhaustibleGraniteMines.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/s25main/addons/AddonInexhaustibleGraniteMines.h b/libs/s25main/addons/AddonInexhaustibleGraniteMines.h index 418d30e791..f12b35a909 100644 --- a/libs/s25main/addons/AddonInexhaustibleGraniteMines.h +++ b/libs/s25main/addons/AddonInexhaustibleGraniteMines.h @@ -8,13 +8,13 @@ #include "mygettext/mygettext.h" /** - * Addon for allowing to have unlimited resources. + * Addon for allowing granite mines to produce independently of stone resources. */ class AddonInexhaustibleGraniteMines : public AddonBool { public: AddonInexhaustibleGraniteMines() : AddonBool(AddonId::INEXHAUSTIBLE_GRANITEMINES, AddonGroup::Economy, _("Inexhaustible Granite Mines"), - _("Granite mines will never be depleted.")) + _("Granite mines can produce stone on any mountain spot and never deplete resources.")) {} }; From 2a4936e3bfc18d5f756c13c7b6a56cad3ceb34f9 Mon Sep 17 00:00:00 2001 From: Manu Date: Fri, 1 May 2026 14:38:45 +0200 Subject: [PATCH 5/6] Avoid runtime addon toggle in granite mine test --- tests/s25Main/integration/testProduction.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/s25Main/integration/testProduction.cpp b/tests/s25Main/integration/testProduction.cpp index b24e4e54ae..9783f82a53 100644 --- a/tests/s25Main/integration/testProduction.cpp +++ b/tests/s25Main/integration/testProduction.cpp @@ -125,15 +125,22 @@ BOOST_FIXTURE_TEST_CASE(MetalWorkerOrders, WorldWithGCExecution1P) BOOST_FIXTURE_TEST_CASE(GraniteMineWithoutResourcesNeedsAddon, GraniteMineWithoutResourcesFixture) { - const nobUsual* mine = CreateGraniteMineWithoutResources(); + CreateGraniteMineWithoutResources(); const Inventory& curInventory = world.GetPlayer(curPlayer).GetInventory(); const unsigned initialStones = curInventory[GoodType::Stones]; RTTR_SKIP_GFS(2000); + BOOST_TEST(curInventory[GoodType::Stones] == initialStones); +} +BOOST_FIXTURE_TEST_CASE(InexhaustibleGraniteMineWorksWithoutResources, GraniteMineWithoutResourcesFixture) +{ ggs.setSelection(AddonId::INEXHAUSTIBLE_GRANITEMINES, 1); - RTTR_EXEC_TILL(2000, mine->is_working || curInventory[GoodType::Stones] > initialStones); + CreateGraniteMineWithoutResources(); + const Inventory& curInventory = world.GetPlayer(curPlayer).GetInventory(); + const unsigned initialStones = curInventory[GoodType::Stones]; + RTTR_EXEC_TILL(2000, curInventory[GoodType::Stones] > initialStones); } From fb40fc9d29b516e7224bb3adce85bc5d64109a32 Mon Sep 17 00:00:00 2001 From: Manu Date: Fri, 1 May 2026 21:11:51 +0200 Subject: [PATCH 6/6] Split granite mine work-everywhere addon --- libs/s25main/GlobalGameSettings.cpp | 1 + .../addons/AddonGraniteMinesWorkEverywhere.h | 21 +++++++ .../addons/AddonInexhaustibleGraniteMines.h | 4 +- libs/s25main/addons/Addons.h | 1 + libs/s25main/addons/const_addons.h | 2 +- libs/s25main/figures/nofMiner.cpp | 60 +++++++++++++++---- libs/s25main/figures/nofMiner.h | 4 +- tests/s25Main/integration/testProduction.cpp | 44 ++++++++++++-- 8 files changed, 116 insertions(+), 21 deletions(-) create mode 100644 libs/s25main/addons/AddonGraniteMinesWorkEverywhere.h diff --git a/libs/s25main/GlobalGameSettings.cpp b/libs/s25main/GlobalGameSettings.cpp index 63f240221e..c855b37418 100644 --- a/libs/s25main/GlobalGameSettings.cpp +++ b/libs/s25main/GlobalGameSettings.cpp @@ -79,6 +79,7 @@ void GlobalGameSettings::registerAllAddons() AddonHalfCostMilEquip, AddonInexhaustibleFish, AddonInexhaustibleGraniteMines, + AddonGraniteMinesWorkEverywhere, AddonInexhaustibleMines, AddonLimitCatapults, AddonManualRoadEnlargement, diff --git a/libs/s25main/addons/AddonGraniteMinesWorkEverywhere.h b/libs/s25main/addons/AddonGraniteMinesWorkEverywhere.h new file mode 100644 index 0000000000..ab5bd5c3b8 --- /dev/null +++ b/libs/s25main/addons/AddonGraniteMinesWorkEverywhere.h @@ -0,0 +1,21 @@ +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) +// +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "AddonBool.h" +#include "const_addons.h" +#include "mygettext/mygettext.h" + +/** + * Addon for creating finite granite resources below granite mines without explicit stone resources. + */ +class AddonGraniteMinesWorkEverywhere : public AddonBool +{ +public: + AddonGraniteMinesWorkEverywhere() + : AddonBool(AddonId::GRANITEMINES_WORK_EVERYWHERE, AddonGroup::Economy, _("Granite Mines Work Everywhere"), + _("Granite mines can create a finite stone resource on otherwise empty mountain spots.")) + {} +}; diff --git a/libs/s25main/addons/AddonInexhaustibleGraniteMines.h b/libs/s25main/addons/AddonInexhaustibleGraniteMines.h index f12b35a909..592895651e 100644 --- a/libs/s25main/addons/AddonInexhaustibleGraniteMines.h +++ b/libs/s25main/addons/AddonInexhaustibleGraniteMines.h @@ -8,13 +8,13 @@ #include "mygettext/mygettext.h" /** - * Addon for allowing granite mines to produce independently of stone resources. + * Addon for allowing granite mines to have unlimited resources. */ class AddonInexhaustibleGraniteMines : public AddonBool { public: AddonInexhaustibleGraniteMines() : AddonBool(AddonId::INEXHAUSTIBLE_GRANITEMINES, AddonGroup::Economy, _("Inexhaustible Granite Mines"), - _("Granite mines can produce stone on any mountain spot and never deplete resources.")) + _("Granite mines will never deplete stone resources.")) {} }; diff --git a/libs/s25main/addons/Addons.h b/libs/s25main/addons/Addons.h index 4b773587d0..ac7f84bf61 100644 --- a/libs/s25main/addons/Addons.h +++ b/libs/s25main/addons/Addons.h @@ -35,6 +35,7 @@ #include "addons/AddonInexhaustibleFish.h" #include "addons/AddonInexhaustibleGraniteMines.h" +#include "addons/AddonGraniteMinesWorkEverywhere.h" #include "addons/AddonMaxRank.h" #include "addons/AddonMilitaryAid.h" #include "addons/AddonSeaAttack.h" diff --git a/libs/s25main/addons/const_addons.h b/libs/s25main/addons/const_addons.h index 4ae5da8c88..c031b36218 100644 --- a/libs/s25main/addons/const_addons.h +++ b/libs/s25main/addons/const_addons.h @@ -55,7 +55,7 @@ ENUM_WITH_STRING(AddonId, LIMIT_CATAPULTS = 0x00000000, INEXHAUSTIBLE_MINES = 0x MILITARY_AID = 0x00700000, - INEXHAUSTIBLE_GRANITEMINES = 0x00800000, + INEXHAUSTIBLE_GRANITEMINES = 0x00800000, GRANITEMINES_WORK_EVERYWHERE = 0x00800001, MAX_RANK = 0x00900000, SEA_ATTACK = 0x00900001, INEXHAUSTIBLE_FISH = 0x00900002, MORE_ANIMALS = 0x00900003, BURN_DURATION = 0x00900004, NO_ALLIED_PUSH = 0x00900005, diff --git a/libs/s25main/figures/nofMiner.cpp b/libs/s25main/figures/nofMiner.cpp index dfbb2ca1c4..1750ca60ad 100644 --- a/libs/s25main/figures/nofMiner.cpp +++ b/libs/s25main/figures/nofMiner.cpp @@ -11,6 +11,9 @@ #include "network/GameClient.h" #include "ogl/glArchivItem_Bitmap_Player.h" #include "world/GameWorld.h" +#include "gameTypes/Resource.h" +#include "gameData/GameConsts.h" +#include "random/Random.h" nofMiner::nofMiner(const MapPoint pos, const unsigned char player, nobUsual* workplace) : nofWorkman(Job::Miner, pos, player, workplace) @@ -69,32 +72,63 @@ helpers::OptionalEnum nofMiner::ProduceWare() } } +MapPoint nofMiner::FindPointWithResourceQuiet(ResourceType type) const +{ + const auto pts = world->GetMatchingPointsInRadius<1>( + pos, MINER_RADIUS, [this, type](const MapPoint pt) { return world->GetNode(pt).resources.has(type); }, true); + return pts.empty() ? MapPoint::Invalid() : pts.front(); +} + +bool nofMiner::CanCreateWorkEverywhereGraniteResource() const +{ + return workplace->GetBuildingType() == BuildingType::GraniteMine + && world->GetGGS().isEnabled(AddonId::GRANITEMINES_WORK_EVERYWHERE) + && world->GetNode(pos).resources.getType() == ResourceType::Nothing; +} + +MapPoint nofMiner::CreateWorkEverywhereGraniteResource() +{ + if(!CanCreateWorkEverywhereGraniteResource()) + return MapPoint::Invalid(); + + world->SetResource(pos, Resource(ResourceType::Granite, static_cast(8 + RANDOM_RAND(8)))); + return pos; +} + bool nofMiner::AreWaresAvailable() const { - return nofWorkman::AreWaresAvailable() - && (CanMineWithoutResource() || FindPointWithResource(GetRequiredResType()).isValid()); + if(!nofWorkman::AreWaresAvailable()) + return false; + + if(FindPointWithResourceQuiet(GetRequiredResType()).isValid() || CanCreateWorkEverywhereGraniteResource()) + return true; + + workplace->OnOutOfResources(); + return false; } bool nofMiner::StartWorking() { const GlobalGameSettings& settings = world->GetGGS(); - if(!CanMineWithoutResource()) + MapPoint resPt = FindPointWithResourceQuiet(GetRequiredResType()); + if(!resPt.isValid()) { - MapPoint resPt = FindPointWithResource(GetRequiredResType()); + resPt = CreateWorkEverywhereGraniteResource(); if(!resPt.isValid()) + { + workplace->OnOutOfResources(); return false; - if(!settings.isEnabled(AddonId::INEXHAUSTIBLE_MINES)) - world->ReduceResource(resPt); + } } - return nofWorkman::StartWorking(); -} -bool nofMiner::CanMineWithoutResource() const -{ - return workplace->GetBuildingType() == BuildingType::GraniteMine - && world->GetGGS().isEnabled(AddonId::INEXHAUSTIBLE_GRANITEMINES); -} + const bool inexhaustibleRes = settings.isEnabled(AddonId::INEXHAUSTIBLE_MINES) + || (workplace->GetBuildingType() == BuildingType::GraniteMine + && settings.isEnabled(AddonId::INEXHAUSTIBLE_GRANITEMINES)); + if(!inexhaustibleRes) + world->ReduceResource(resPt); + return nofWorkman::StartWorking(); +} ResourceType nofMiner::GetRequiredResType() const { switch(workplace->GetBuildingType()) diff --git a/libs/s25main/figures/nofMiner.h b/libs/s25main/figures/nofMiner.h index d965a0fdcd..b45fa765ea 100644 --- a/libs/s25main/figures/nofMiner.h +++ b/libs/s25main/figures/nofMiner.h @@ -23,7 +23,9 @@ class nofMiner : public nofWorkman bool AreWaresAvailable() const override; bool StartWorking() override; ResourceType GetRequiredResType() const; - bool CanMineWithoutResource() const; + MapPoint FindPointWithResourceQuiet(ResourceType type) const; + bool CanCreateWorkEverywhereGraniteResource() const; + MapPoint CreateWorkEverywhereGraniteResource(); public: nofMiner(MapPoint pos, unsigned char player, nobUsual* workplace); diff --git a/tests/s25Main/integration/testProduction.cpp b/tests/s25Main/integration/testProduction.cpp index 9783f82a53..ba54e84ce1 100644 --- a/tests/s25Main/integration/testProduction.cpp +++ b/tests/s25Main/integration/testProduction.cpp @@ -26,10 +26,10 @@ BOOST_AUTO_TEST_SUITE(Production) namespace { struct GraniteMineWithoutResourcesFixture : WorldWithGCExecution1P { - const nobUsual* CreateGraniteMineWithoutResources() + MapPoint CreateGraniteMineWithoutResources() { GoodsAndPeopleCounts inv; - inv[GoodType::Fish] = 10; + inv[GoodType::Fish] = 40; inv[GoodType::PickAxe] = 1; inv[Job::Miner] = 1; world.GetSpecObj(hqPos)->AddToInventory(inv, true); @@ -39,7 +39,7 @@ struct GraniteMineWithoutResourcesFixture : WorldWithGCExecution1P BuildingFactory::CreateBuilding(world, BuildingType::GraniteMine, minePos, curPlayer, Nation::Romans)); BuildRoad(world.GetNeighbour(minePos, Direction::SouthEast), false, std::vector(2, Direction::West)); RTTR_EXEC_TILL(500, mine->HasWorker()); - return mine; + return minePos; } }; } // namespace @@ -134,14 +134,50 @@ BOOST_FIXTURE_TEST_CASE(GraniteMineWithoutResourcesNeedsAddon, GraniteMineWithou BOOST_TEST(curInventory[GoodType::Stones] == initialStones); } -BOOST_FIXTURE_TEST_CASE(InexhaustibleGraniteMineWorksWithoutResources, GraniteMineWithoutResourcesFixture) +BOOST_FIXTURE_TEST_CASE(InexhaustibleGraniteMineStillNeedsResourceSpot, GraniteMineWithoutResourcesFixture) { ggs.setSelection(AddonId::INEXHAUSTIBLE_GRANITEMINES, 1); CreateGraniteMineWithoutResources(); const Inventory& curInventory = world.GetPlayer(curPlayer).GetInventory(); const unsigned initialStones = curInventory[GoodType::Stones]; + RTTR_SKIP_GFS(2000); + + BOOST_TEST(curInventory[GoodType::Stones] == initialStones); +} + +BOOST_FIXTURE_TEST_CASE(GraniteMineWorkEverywhereCreatesDepletableResource, GraniteMineWithoutResourcesFixture) +{ + ggs.setSelection(AddonId::GRANITEMINES_WORK_EVERYWHERE, 1); + const MapPoint minePos = CreateGraniteMineWithoutResources(); + const Inventory& curInventory = world.GetPlayer(curPlayer).GetInventory(); + const unsigned initialStones = curInventory[GoodType::Stones]; + + RTTR_EXEC_TILL(2000, curInventory[GoodType::Stones] > initialStones); + BOOST_TEST(world.GetNode(minePos).resources.has(ResourceType::Granite)); + + RTTR_EXEC_TILL(50000, world.GetNode(minePos).resources.getType() == ResourceType::Granite + && world.GetNode(minePos).resources.getAmount() == 0u); + BOOST_TEST(static_cast(world.GetNode(minePos).resources.getType()) == static_cast(ResourceType::Granite)); + BOOST_TEST(world.GetNode(minePos).resources.getAmount() == 0u); +} + +BOOST_FIXTURE_TEST_CASE(GraniteMineWorkEverywhereResourceIsInexhaustibleWithGraniteAddon, GraniteMineWithoutResourcesFixture) +{ + ggs.setSelection(AddonId::GRANITEMINES_WORK_EVERYWHERE, 1); + ggs.setSelection(AddonId::INEXHAUSTIBLE_GRANITEMINES, 1); + const MapPoint minePos = CreateGraniteMineWithoutResources(); + const Inventory& curInventory = world.GetPlayer(curPlayer).GetInventory(); + const unsigned initialStones = curInventory[GoodType::Stones]; + RTTR_EXEC_TILL(2000, curInventory[GoodType::Stones] > initialStones); + BOOST_TEST(world.GetNode(minePos).resources.has(ResourceType::Granite)); + const unsigned initialResourceAmount = world.GetNode(minePos).resources.getAmount(); + + RTTR_SKIP_GFS(10000); + + BOOST_TEST(world.GetNode(minePos).resources.has(ResourceType::Granite)); + BOOST_TEST(world.GetNode(minePos).resources.getAmount() == initialResourceAmount); } BOOST_AUTO_TEST_SUITE_END()