Skip to content

Commit bf934c8

Browse files
authored
Merge pull request #1925 from DevOpsOfChaos/sidequest/ignore-isolated-fish-resources
Ignore isolated fish resources for fisheries
2 parents 64b706b + 70b841f commit bf934c8

12 files changed

Lines changed: 235 additions & 114 deletions

File tree

external/turtle

libs/s25main/CheatCommandTracker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ void CheatCommandTracker::onSpecialKeyEvent(const KeyEvent& ke)
5757

5858
switch(ke.kt)
5959
{
60+
case KeyType::F6: cheats_.toggleHumanAIPlayer(); break;
6061
case KeyType::F7:
6162
{
6263
if(ke.alt)
@@ -65,7 +66,6 @@ void CheatCommandTracker::onSpecialKeyEvent(const KeyEvent& ke)
6566
cheats_.toggleAllVisible();
6667
}
6768
break;
68-
case KeyType::F10: cheats_.toggleHumanAIPlayer(); break;
6969
default: break;
7070
}
7171
}

libs/s25main/Replay.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ uint8_t Replay::GetLatestMinorVersion() const
4040
// 8.1: Portraits support
4141
// 8.2: Set correct initial distributions if replay starts without savegame for leather addon (see GameClient.cpp
4242
// StartReplay function for detailed description)
43-
return 2;
43+
// 8.3 Remove invalid fish for replays started from start (i.e. map instead of savegame)
44+
return 3;
4445
}
4546

4647
uint8_t Replay::GetLatestMajorVersion() const

libs/s25main/figures/nofFisher.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ class nofFisher : public nofFarmhand
2626
/// Abgeleitete Klasse informieren, wenn fertig ist mit Arbeiten
2727
void WorkFinished() override;
2828

29-
/// Returns the quality of this working point or determines if the worker can work here at all
30-
PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const override;
31-
3229
public:
3330
nofFisher(MapPoint pos, unsigned char player, nobUsual* workplace);
3431
nofFisher(SerializedGameData& sgd, unsigned obj_id);
@@ -38,4 +35,7 @@ class nofFisher : public nofFarmhand
3835
void Serialize(SerializedGameData& sgd) const override;
3936

4037
GO_Type GetGOT() const final { return GO_Type::NofFisher; }
38+
39+
/// Returns the quality of this working point or determines if the worker can work here at all
40+
PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const override;
4141
};

libs/s25main/network/GameClient.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,9 @@ void GameClient::StartGame(const unsigned random_init)
337337
OnError(ClientError::InvalidMap);
338338
return;
339339
}
340-
gameWorld.SetupResources();
340+
// TODO (Replay): Always use true
341+
const bool fixFish = !GetReplay() || GetReplay()->GetMinorVersion() >= 3;
342+
MapLoader::SetupResources(gameWorld, fixFish);
341343
}
342344
gameWorld.InitAfterLoad();
343345

@@ -1872,9 +1874,14 @@ void GameClient::ToggleHumanAIPlayer(const AI::Info& aiInfo)
18721874
auto it = helpers::find_if(game->aiPlayers_,
18731875
[id = this->GetPlayerId()](const auto& player) { return player.GetPlayerId() == id; });
18741876
if(it != game->aiPlayers_.end())
1877+
{
18751878
game->aiPlayers_.erase(it);
1876-
else
1879+
SystemChat(_("Disabled AI for current player"));
1880+
} else
1881+
{
18771882
game->AddAIPlayer(CreateAIPlayer(GetPlayerId(), aiInfo));
1883+
SystemChat(_("Enabled AI for current player"));
1884+
}
18781885
}
18791886

18801887
void GameClient::RequestSwapToPlayer(const unsigned char newId)

libs/s25main/world/GameWorld.cpp

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,87 +1294,6 @@ bool GameWorld::IsBorderNode(const MapPoint pt, const unsigned char owner) const
12941294
return (GetNode(pt).owner == owner && !IsPlayerTerritory(pt, owner));
12951295
}
12961296

1297-
/**
1298-
* Konvertiert Ressourcen zwischen Typen hin und her oder löscht sie.
1299-
* Für Spiele ohne Gold.
1300-
*/
1301-
void GameWorld::ConvertMineResourceTypes(ResourceType from, ResourceType to)
1302-
{
1303-
// LOG.write(("Convert map resources from %i to %i\n", from, to);
1304-
if(from == to)
1305-
return;
1306-
1307-
RTTR_FOREACH_PT(MapPoint, GetSize())
1308-
{
1309-
Resource resources = GetNode(pt).resources;
1310-
// Gibt es Ressourcen dieses Typs?
1311-
// Wenn ja, dann umwandeln bzw löschen
1312-
if(resources.getType() == from)
1313-
{
1314-
resources.setType(to);
1315-
SetResource(pt, resources);
1316-
}
1317-
}
1318-
}
1319-
1320-
void GameWorld::SetupResources()
1321-
{
1322-
ResourceType target;
1323-
switch(GetGGS().getSelection(AddonId::CHANGE_GOLD_DEPOSITS))
1324-
{
1325-
case 0:
1326-
default: target = ResourceType::Gold; break;
1327-
case 1: target = ResourceType::Nothing; break;
1328-
case 2: target = ResourceType::Iron; break;
1329-
case 3: target = ResourceType::Coal; break;
1330-
case 4: target = ResourceType::Granite; break;
1331-
}
1332-
ConvertMineResourceTypes(ResourceType::Gold, target);
1333-
PlaceAndFixWater();
1334-
}
1335-
1336-
/**
1337-
* Fills water depending on terrain and Addon setting
1338-
*/
1339-
void GameWorld::PlaceAndFixWater()
1340-
{
1341-
bool waterEverywhere = GetGGS().getSelection(AddonId::EXHAUSTIBLE_WATER) == 1;
1342-
1343-
RTTR_FOREACH_PT(MapPoint, GetSize())
1344-
{
1345-
Resource curNodeResource = GetNode(pt).resources;
1346-
1347-
if(curNodeResource.getType() == ResourceType::Nothing)
1348-
{
1349-
if(!waterEverywhere)
1350-
continue;
1351-
} else if(curNodeResource.getType() != ResourceType::Water)
1352-
{
1353-
// do not override maps resource.
1354-
continue;
1355-
}
1356-
1357-
uint8_t minHumidity = 100;
1358-
for(const DescIdx<TerrainDesc> tIdx : GetTerrainsAround(pt))
1359-
{
1360-
const uint8_t curHumidity = GetDescription().get(tIdx).humidity;
1361-
if(curHumidity < minHumidity)
1362-
{
1363-
minHumidity = curHumidity;
1364-
if(minHumidity == 0)
1365-
break;
1366-
}
1367-
}
1368-
if(minHumidity)
1369-
curNodeResource =
1370-
Resource(ResourceType::Water, waterEverywhere ? 7 : helpers::iround<uint8_t>(minHumidity * 7. / 100.));
1371-
else
1372-
curNodeResource = Resource(ResourceType::Nothing, 0);
1373-
1374-
SetResource(pt, curNodeResource);
1375-
}
1376-
}
1377-
13781297
/// Gründet vom Schiff aus eine neue Kolonie
13791298
bool GameWorld::FoundColony(const HarborId harbor, const unsigned char player, const SeaId seaId)
13801299
{

libs/s25main/world/GameWorld.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,6 @@ class GameWorld : public GameWorldBase
145145
/// Return whether this is a border node (node belongs to player, but not all others around)
146146
bool IsBorderNode(MapPoint pt, unsigned char owner) const;
147147

148-
// Konvertiert Ressourcen zwischen Typen hin und her oder löscht sie.
149-
// Für Spiele ohne Gold.
150-
void ConvertMineResourceTypes(ResourceType from, ResourceType to);
151-
// Setup resources like gold and water after loading a new map
152-
void SetupResources();
153-
154-
// Fills water depending on terrain and Addon setting
155-
void PlaceAndFixWater();
156-
157148
/// Gründet vom Schiff aus eine neue Kolonie, gibt true zurück bei Erfolg
158149
bool FoundColony(HarborId harbor, unsigned char player, SeaId seaId);
159150
/// Registriert eine Baustelle eines Hafens, die vom Schiff aus gesetzt worden ist

libs/s25main/world/MapLoader.cpp

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
#include "GlobalGameSettings.h"
1010
#include "PointOutput.h"
1111
#include "RttrForeachPt.h"
12+
#include "addons/const_addons.h"
1213
#include "buildings/nobHQ.h"
1314
#include "factories/BuildingFactory.h"
1415
#include "helpers/IdRange.h"
16+
#include "helpers/Range.h"
17+
#include "helpers/containerUtils.h"
18+
#include "helpers/mathFuncs.h"
1519
#include "lua/GameDataLoader.h"
1620
#include "pathfinding/PathConditionShip.h"
1721
#include "random/Random.h"
@@ -106,6 +110,105 @@ bool MapLoader::PlaceHQs(bool addStartWares)
106110
return PlaceHQs(world_, hqPositions, addStartWares);
107111
}
108112

113+
void MapLoader::SetupResources(GameWorldBase& world, const bool fixFish)
114+
{
115+
ResourceType target;
116+
switch(world.GetGGS().getSelection(AddonId::CHANGE_GOLD_DEPOSITS))
117+
{
118+
case 0:
119+
default: target = ResourceType::Gold; break;
120+
case 1: target = ResourceType::Nothing; break;
121+
case 2: target = ResourceType::Iron; break;
122+
case 3: target = ResourceType::Coal; break;
123+
case 4: target = ResourceType::Granite; break;
124+
}
125+
ConvertMineResourceTypes(world, ResourceType::Gold, target);
126+
PlaceAndFixWater(world);
127+
if(fixFish)
128+
RemoveUnusableFishResources(world);
129+
}
130+
131+
void MapLoader::ConvertMineResourceTypes(GameWorldBase& world, ResourceType from, ResourceType to)
132+
{
133+
// LOG.write(("Convert map resources from %i to %i\n", from, to);
134+
if(from == to)
135+
return;
136+
137+
RTTR_FOREACH_PT(MapPoint, world.GetSize())
138+
{
139+
Resource resources = world.GetNode(pt).resources;
140+
// Gibt es Ressourcen dieses Typs?
141+
// Wenn ja, dann umwandeln bzw löschen
142+
if(resources.getType() == from)
143+
{
144+
resources.setType(to);
145+
world.SetResource(pt, resources);
146+
}
147+
}
148+
}
149+
150+
void MapLoader::PlaceAndFixWater(GameWorldBase& world)
151+
{
152+
const bool waterEverywhere = world.GetGGS().getSelection(AddonId::EXHAUSTIBLE_WATER) == 1;
153+
154+
RTTR_FOREACH_PT(MapPoint, world.GetSize())
155+
{
156+
Resource curNodeResource = world.GetNode(pt).resources;
157+
158+
if(curNodeResource.getType() == ResourceType::Nothing)
159+
{
160+
if(!waterEverywhere)
161+
continue;
162+
} else if(curNodeResource.getType() != ResourceType::Water)
163+
continue; // do not override maps resource.
164+
165+
uint8_t minHumidity = 100;
166+
for(const DescIdx<TerrainDesc> tIdx : world.GetTerrainsAround(pt))
167+
{
168+
const uint8_t curHumidity = world.GetDescription().get(tIdx).humidity;
169+
if(curHumidity < minHumidity)
170+
{
171+
minHumidity = curHumidity;
172+
if(minHumidity == 0)
173+
break;
174+
}
175+
}
176+
if(minHumidity)
177+
{
178+
curNodeResource =
179+
Resource(ResourceType::Water, waterEverywhere ? 7 : helpers::iround<uint8_t>(minHumidity * 7. / 100.));
180+
} else
181+
curNodeResource = Resource(ResourceType::Nothing, 0);
182+
183+
world.SetResource(pt, curNodeResource);
184+
}
185+
}
186+
187+
void MapLoader::RemoveUnusableFishResources(GameWorldBase& world)
188+
{
189+
const auto isWaterPoint = [&world](const MapPoint nb) { return world.IsWaterPoint(nb); };
190+
for(const MapCoord y : helpers::range(world.GetHeight()))
191+
{
192+
// Optimization: When there was fish on the previous node (in the same row)
193+
// we do not need to check for isolated water points, as there is at least that water point
194+
bool previousHasFish = false;
195+
for(const MapCoord x : helpers::range(world.GetWidth()))
196+
{
197+
const MapPoint pt(x, y);
198+
bool hasFish = false;
199+
200+
if(world.GetNode(pt).resources.has(ResourceType::Fish))
201+
{
202+
if(isWaterPoint(pt) && (previousHasFish || helpers::contains_if(world.GetNeighbours(pt), isWaterPoint)))
203+
hasFish = true;
204+
else
205+
world.SetResource(pt, Resource(ResourceType::Nothing, 0));
206+
}
207+
previousHasFish = hasFish;
208+
}
209+
}
210+
}
211+
109212
void MapLoader::InitShadows(World& world)
110213
{
111214
RTTR_FOREACH_PT(MapPoint, world.GetSize())
@@ -117,7 +220,7 @@ void MapLoader::SetMapExplored(World& world)
117220
RTTR_FOREACH_PT(MapPoint, world.GetSize())
118221
{
119222
// For every player
120-
for(unsigned i = 0; i < MAX_PLAYERS; ++i)
223+
for(const auto i : helpers::range(MAX_PLAYERS))
121224
{
122225
// If we have FoW here, save it
123226
if(world.GetNode(pt).fow[i].visibility == Visibility::FogOfWar)

libs/s25main/world/MapLoader.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "gameTypes/GameSettingTypes.h"
88
#include "gameTypes/MapCoordinates.h"
99
#include "gameTypes/MapTypes.h"
10+
#include "gameTypes/Resource.h"
1011
#include "gameData/DescIdx.h"
1112
#include <boost/filesystem/path.hpp>
1213
#include <vector>
@@ -50,6 +51,9 @@ class MapLoader
5051
/// Optionally add the starting wares to the HQs.
5152
/// Return false if there was an error (e.g. invalid start position)
5253
bool PlaceHQs(bool addStartWares = true);
54+
/// Setup resources like gold and water after loading a new map.
55+
/// TODO(Replay): Remove fixFish (always set to true)
56+
static void SetupResources(GameWorldBase& world, bool fixFish = true);
5357

5458
/// Return the (original/unshuffled) position of the players HQ (only valid after successful load)
5559
MapPoint GetOriginalHQPos(unsigned player) const { return hqPositions_[player]; }
@@ -61,4 +65,12 @@ class MapLoader
6165
/// Place the HQs on a loaded map and add starting wares if desired.
6266
/// Return false if there was an error.
6367
static bool PlaceHQs(GameWorldBase& world, const std::vector<MapPoint>& hqPositions, bool addStartWares = true);
68+
69+
private:
70+
/// Converts map resources between types or deletes them. Used for games without gold.
71+
static void ConvertMineResourceTypes(GameWorldBase& world, ResourceType from, ResourceType to);
72+
/// Fills water depending on terrain and Addon setting.
73+
static void PlaceAndFixWater(GameWorldBase& world);
74+
/// Removes fish resources that cannot be reached by fisheries.
75+
static void RemoveUnusableFishResources(GameWorldBase& world);
6476
};

0 commit comments

Comments
 (0)