Skip to content

Commit 5d7afa2

Browse files
authored
Merge branch 'master' into fixShipConnections
2 parents bbb8cab + 57b0829 commit 5d7afa2

12 files changed

Lines changed: 223 additions & 66 deletions

File tree

libs/s25main/GlobalGameSettings.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ void GlobalGameSettings::registerAllAddons()
104104
AddonWine,
105105
AddonLeather,
106106
AddonNoArmorDefault,
107-
AddonArmorCapturedBld
107+
AddonArmorCapturedBld,
108+
AddonForesterFarmFieldAvoidance
108109
>;
109110
// clang-format on
110111
using namespace boost::mp11;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
2+
//
3+
// SPDX-License-Identifier: GPL-2.0-or-later
4+
5+
#pragma once
6+
7+
#include "AddonBool.h"
8+
#include "mygettext/mygettext.h"
9+
10+
class AddonForesterFarmFieldAvoidance : public AddonBool
11+
{
12+
public:
13+
AddonForesterFarmFieldAvoidance()
14+
: AddonBool(AddonId::FORESTER_FARM_FIELD_AVOIDANCE, AddonGroup::GamePlay | AddonGroup::Economy,
15+
_("Foresters avoid farm field spots"),
16+
_("Prevents foresters from planting trees on spots that own farms could use for new fields."))
17+
{}
18+
};

libs/s25main/addons/Addons.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include "addons/AddonAutoFlags.h"
6363

6464
#include "addons/AddonArmorCapturedBld.h"
65+
#include "addons/AddonForesterFarmFieldAvoidance.h"
6566
#include "addons/AddonLeather.h"
6667
#include "addons/AddonNoArmorDefault.h"
6768
#include "addons/AddonWine.h"

libs/s25main/addons/const_addons.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
// 00E Jonathan
2727
// 00F Jarno
2828
// 010 aztimh
29+
// 011 DevOpsOfChaos
2930

3031
// Do not forget to add your Addon to GlobalGameSettings::registerAllAddons @ GlobalGameSettings.cpp!
3132
// Never use a number twice!
@@ -75,7 +76,9 @@ ENUM_WITH_STRING(AddonId, LIMIT_CATAPULTS = 0x00000000, INEXHAUSTIBLE_MINES = 0x
7576
AUTOFLAGS = 0x00F00000,
7677

7778
WINE = 0x01000000, LEATHER = 0x01000001, NO_ARMOR_DEFAULT = 0x01000002,
78-
ARMOR_CAPTURED_BLD = 0x01000003)
79+
ARMOR_CAPTURED_BLD = 0x01000003,
80+
81+
FORESTER_FARM_FIELD_AVOIDANCE = 0x01100000)
7982
//-V:AddonId:801
8083

8184
enum class AddonGroup : unsigned

libs/s25main/buildings/nobMilitary.cpp

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,20 @@ void nobMilitary::RegulateTroops()
508508

509509
is_regulating_troops = true;
510510

511+
GamePlayer& owner = world->GetPlayer(player);
512+
std::array<int, NUM_SOLDIER_RANKS> hasReturnWarehouseByRank;
513+
hasReturnWarehouseByRank.fill(-1);
514+
auto canReturnHome = [&](const nofPassiveSoldier& soldier) {
515+
const unsigned rank = soldier.GetRank();
516+
int& hasReturnWarehouse = hasReturnWarehouseByRank[rank];
517+
if(hasReturnWarehouse < 0)
518+
{
519+
hasReturnWarehouse =
520+
owner.FindWarehouse(*this, FW::AcceptsFigure(soldier.GetJobType()), true, false) ? 1 : 0;
521+
}
522+
return hasReturnWarehouse != 0;
523+
};
524+
511525
// This only has an effect if the military control addon is in use and the troop limits have been lowered.
512526
std::array<unsigned, NUM_SOLDIER_RANKS> counts = GetTotalSoldiersByRank();
513527
std::array<unsigned, NUM_SOLDIER_RANKS> lack;
@@ -521,7 +535,7 @@ void nobMilitary::RegulateTroops()
521535
std::vector<nofPassiveSoldier*> notNeededSoldiers;
522536
for(auto it = ordered_troops.begin(); excess && it != ordered_troops.end();)
523537
{
524-
if((*it)->GetRank() == rank)
538+
if((*it)->GetRank() == rank && canReturnHome(**it))
525539
{
526540
notNeededSoldiers.push_back(*it);
527541
it = ordered_troops.erase(it);
@@ -538,12 +552,11 @@ void nobMilitary::RegulateTroops()
538552
// This bit is for ordering troops later
539553
lack[rank] = troop_limits[rank] - counts[rank];
540554
}
541-
if(excess > 0
542-
&& world->GetPlayer(player).FindWarehouse(*this, FW::AcceptsFigure(SOLDIER_JOBS[rank]), true, false))
555+
if(excess > 0)
543556
{
544557
for(auto it = troops.begin(); excess && it != troops.end() && troops.size() > 1;)
545558
{
546-
if((*it)->GetRank() == rank)
559+
if((*it)->GetRank() == rank && canReturnHome(**it))
547560
{
548561
(*it)->LeaveBuilding();
549562
AddLeavingFigure(std::move(*it));
@@ -565,22 +578,35 @@ void nobMilitary::RegulateTroops()
565578
// Zuerst die bestellten Soldaten wegschicken
566579
// Weak ones first
567580
std::vector<nofPassiveSoldier*> notNeededSoldiers;
568-
GamePlayer& owner = world->GetPlayer(player);
569581
if(owner.GetMilitarySetting(1) > MILITARY_SETTINGS_SCALE[1] / 2)
570582
{
571-
for(auto it = ordered_troops.begin(); diff && !ordered_troops.empty(); ++diff)
583+
for(auto it = ordered_troops.begin(); diff && it != ordered_troops.end();)
572584
{
573-
notNeededSoldiers.push_back(*it);
574-
it = ordered_troops.erase(it);
585+
if(canReturnHome(**it))
586+
{
587+
notNeededSoldiers.push_back(*it);
588+
it = ordered_troops.erase(it);
589+
++diff;
590+
} else
591+
{
592+
++it;
593+
}
575594
}
576595
}
577596
// Strong ones first
578597
else
579598
{
580-
for(auto it = ordered_troops.rbegin(); diff && !ordered_troops.empty(); ++diff)
599+
for(auto it = ordered_troops.rbegin(); diff && it != ordered_troops.rend();)
581600
{
582-
notNeededSoldiers.push_back(*it);
583-
it = helpers::erase_reverse(ordered_troops, it);
601+
if(canReturnHome(**it))
602+
{
603+
notNeededSoldiers.push_back(*it);
604+
it = helpers::erase_reverse(ordered_troops, it);
605+
++diff;
606+
} else
607+
{
608+
++it;
609+
}
584610
}
585611
}
586612

@@ -590,32 +616,41 @@ void nobMilitary::RegulateTroops()
590616
notNeededSoldier->NotNeeded();
591617
}
592618

593-
// Nur rausschicken, wenn es einen Weg zu einem Lagerhaus gibt!
594-
if(owner.FindWarehouse(*this, FW::NoCondition(), true, false))
619+
// Dann den Rest (einer muss immer noch drinbleiben!)
620+
// erst die schwachen Soldaten raus
621+
if(owner.GetMilitarySetting(1) > MILITARY_SETTINGS_SCALE[1] / 2)
595622
{
596-
// Dann den Rest (einer muss immer noch drinbleiben!)
597-
// erst die schwachen Soldaten raus
598-
if(owner.GetMilitarySetting(1) > MILITARY_SETTINGS_SCALE[1] / 2)
623+
for(auto it = troops.begin(); diff && it != troops.end() && troops.size() > 1;)
599624
{
600-
for(auto it = troops.begin(); diff && troops.size() > 1; ++diff)
625+
if(canReturnHome(**it))
601626
{
602627
(*it)->LeaveBuilding();
603628
AddLeavingFigure(std::move(*it));
604629
it = troops.erase(it);
630+
++diff;
631+
} else
632+
{
633+
++it;
605634
}
606635
}
607-
// erst die starken Soldaten raus
608-
else
636+
}
637+
// erst die starken Soldaten raus
638+
else
639+
{
640+
for(auto it = troops.rbegin(); diff && it != troops.rend() && troops.size() > 1;)
609641
{
610-
for(auto it = troops.rbegin(); diff && troops.size() > 1; ++diff)
642+
if(canReturnHome(**it))
611643
{
612644
(*it)->LeaveBuilding();
613645
AddLeavingFigure(std::move(*it));
614646
it = helpers::erase_reverse(troops, it);
647+
++diff;
648+
} else
649+
{
650+
++it;
615651
}
616652
}
617653
}
618-
619654
} else if(diff > 0)
620655
{
621656
// Zu wenig Truppen

libs/s25main/figures/nofFarmer.cpp

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,36 @@ unsigned short nofFarmer::GetCarryID() const
5757
return 71;
5858
}
5959

60+
nofFarmhand::PointQuality nofFarmer::GetNewFieldPointQuality(const GameWorld& world, const MapPoint pt)
61+
{
62+
// Nicht auf Straßen bauen!
63+
for(const auto dir : helpers::EnumRange<Direction>{})
64+
{
65+
if(world.GetPointRoad(pt, dir) != PointRoad::None)
66+
return PointQuality::NotPossible;
67+
}
68+
69+
// Terrain untersuchen
70+
if(!world.IsOfTerrain(pt, [](const auto& desc) { return desc.IsVital(); }))
71+
return PointQuality::NotPossible;
72+
73+
// Ist Platz frei?
74+
NodalObjectType noType = world.GetNO(pt)->GetType();
75+
if(noType != NodalObjectType::Environment && noType != NodalObjectType::Nothing)
76+
return PointQuality::NotPossible;
77+
78+
for(const MapPoint nb : world.GetNeighbours(pt))
79+
{
80+
// Nicht direkt neben andere Getreidefelder und Gebäude setzen!
81+
noType = world.GetNO(nb)->GetType();
82+
if(noType == NodalObjectType::Grainfield || noType == NodalObjectType::Grapefield
83+
|| noType == NodalObjectType::Building || noType == NodalObjectType::Buildingsite)
84+
return PointQuality::NotPossible;
85+
}
86+
87+
return PointQuality::Class2;
88+
}
89+
6090
/// Abgeleitete Klasse informieren, wenn sie anfängt zu arbeiten (Vorbereitungen)
6191
void nofFarmer::WorkStarted()
6292
{
@@ -123,34 +153,7 @@ nofFarmhand::PointQuality nofFarmer::GetPointQuality(const MapPoint pt, bool /*
123153
}
124154
// oder einen freien Platz, wo wir ein neues sähen können
125155
else
126-
{
127-
// Nicht auf Straßen bauen!
128-
for(const auto dir : helpers::EnumRange<Direction>{})
129-
{
130-
if(world->GetPointRoad(pt, dir) != PointRoad::None)
131-
return PointQuality::NotPossible;
132-
}
133-
134-
// Terrain untersuchen
135-
if(!world->IsOfTerrain(pt, [](const auto& desc) { return desc.IsVital(); }))
136-
return PointQuality::NotPossible;
137-
138-
// Ist Platz frei?
139-
NodalObjectType noType = world->GetNO(pt)->GetType();
140-
if(noType != NodalObjectType::Environment && noType != NodalObjectType::Nothing)
141-
return PointQuality::NotPossible;
142-
143-
for(const MapPoint nb : world->GetNeighbours(pt))
144-
{
145-
// Nicht direkt neben andere Getreidefelder und Gebäude setzen!
146-
noType = world->GetNO(nb)->GetType();
147-
if(noType == NodalObjectType::Grainfield || noType == NodalObjectType::Grapefield
148-
|| noType == NodalObjectType::Building || noType == NodalObjectType::Buildingsite)
149-
return PointQuality::NotPossible;
150-
}
151-
152-
return PointQuality::Class2;
153-
}
156+
return GetNewFieldPointQuality(*world, pt);
154157
}
155158

156159
void nofFarmer::WorkAborted()

libs/s25main/figures/nofFarmer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#pragma once
66

77
#include "nofFarmhand.h"
8+
class GameWorld;
89
class SerializedGameData;
910
class nobUsual;
1011

@@ -34,6 +35,8 @@ class nofFarmer : public nofFarmhand
3435
nofFarmer(MapPoint pos, unsigned char player, nobUsual* workplace);
3536
nofFarmer(SerializedGameData& sgd, unsigned obj_id);
3637

38+
static nofFarmhand::PointQuality GetNewFieldPointQuality(const GameWorld& world, MapPoint pt);
39+
3740
void Serialize(SerializedGameData& sgd) const override;
3841

3942
GO_Type GetGOT() const final { return GO_Type::NofFarmer; }

libs/s25main/figures/nofFarmhand.cpp

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,23 @@ nofFarmhand::nofFarmhand(SerializedGameData& sgd, const unsigned obj_id)
2727
: nofBuildingWorker(sgd, obj_id), dest(sgd.PopMapPoint())
2828
{}
2929

30+
unsigned nofFarmhand::GetWorkRadius(const Job job)
31+
{
32+
switch(job)
33+
{
34+
case Job::Carpenter: return 0;
35+
case Job::Hunter:
36+
case Job::Farmer:
37+
case Job::Winegrower: return 2;
38+
case Job::CharBurner: return 3;
39+
case Job::Woodcutter:
40+
case Job::Forester: return 6;
41+
case Job::Fisher: return 7;
42+
case Job::Stonemason: return 8;
43+
default: throw std::logic_error("Invalid job");
44+
}
45+
}
46+
3047
void nofFarmhand::WalkedDerived()
3148
{
3249
switch(state)
@@ -59,21 +76,7 @@ void nofFarmhand::HandleDerivedEvent(const unsigned /*id*/)
5976
{
6077
// Start working after the initial wait period
6178
// Work radius
62-
const unsigned max_radius = [](Job job) {
63-
switch(job)
64-
{
65-
case Job::Carpenter: return 0;
66-
case Job::Hunter:
67-
case Job::Farmer:
68-
case Job::Winegrower: return 2;
69-
case Job::CharBurner: return 3;
70-
case Job::Woodcutter:
71-
case Job::Forester: return 6;
72-
case Job::Fisher: return 7;
73-
case Job::Stonemason: return 8;
74-
default: throw std::logic_error("Invalid job");
75-
}
76-
}(job_);
79+
const unsigned max_radius = GetWorkRadius(job_);
7780
// Number of additional radii in which points should be found
7881
// I.e. 0 => Don't search for points further away than ones already found
7982
const unsigned additionalRadiiToFind = [](Job job) {

libs/s25main/figures/nofFarmhand.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class nofFarmhand : public nofBuildingWorker
5454
nofFarmhand(Job job, MapPoint pos, unsigned char player, nobUsual* workplace);
5555
nofFarmhand(SerializedGameData& sgd, unsigned obj_id);
5656

57+
static unsigned GetWorkRadius(Job job);
58+
5759
void Serialize(SerializedGameData& sgd) const override;
5860

5961
void HandleDerivedEvent(unsigned id) override;

libs/s25main/figures/nofForester.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,38 @@
66

77
#include "GameInterface.h"
88
#include "GamePlayer.h"
9+
#include "GlobalGameSettings.h"
910
#include "Loader.h"
1011
#include "SoundManager.h"
12+
#include "addons/const_addons.h"
13+
#include "buildings/noBaseBuilding.h"
1114
#include "network/GameClient.h"
15+
#include "nofFarmer.h"
1216
#include "ogl/glArchivItem_Bitmap_Player.h"
1317
#include "random/Random.h"
1418
#include "world/GameWorld.h"
1519
#include "nodeObjs/noTree.h"
1620
#include <boost/container/static_vector.hpp>
1721

22+
namespace {
23+
bool IsPotentialNewFieldForOwnFarm(GameWorld& world, const MapPoint pt, const unsigned char player)
24+
{
25+
if(nofFarmer::GetNewFieldPointQuality(world, pt) == nofFarmhand::PointQuality::NotPossible)
26+
return false;
27+
28+
return world.CheckPointsInRadius(
29+
pt, nofFarmhand::GetWorkRadius(Job::Farmer),
30+
[&world, player](const MapPoint farmPt, unsigned) {
31+
if(world.GetNO(farmPt)->GetType() != NodalObjectType::Building)
32+
return false;
33+
34+
const auto* building = world.GetSpecObj<noBaseBuilding>(farmPt);
35+
return building && building->GetPlayer() == player && building->GetBuildingType() == BuildingType::Farm;
36+
},
37+
false);
38+
}
39+
} // namespace
40+
1841
nofForester::nofForester(const MapPoint pos, const unsigned char player, nobUsual* workplace)
1942
: nofFarmhand(Job::Forester, pos, player, workplace)
2043
{}
@@ -110,6 +133,10 @@ nofFarmhand::PointQuality nofForester::GetPointQuality(const MapPoint pt, bool /
110133
return PointQuality::NotPossible;
111134
}
112135

136+
if(world->GetGGS().isEnabled(AddonId::FORESTER_FARM_FIELD_AVOIDANCE)
137+
&& IsPotentialNewFieldForOwnFarm(*world, pt, player))
138+
return PointQuality::NotPossible;
139+
113140
// Terrain untersuchen
114141
if(world->IsOfTerrain(pt, [](const auto& desc) { return desc.IsVital(); }))
115142
return PointQuality::Class1;

0 commit comments

Comments
 (0)