Skip to content

Commit d90bf9a

Browse files
authored
Merge pull request #1917 from Flamefire/fixShipConnections
Fix sea path finding (for ships)
2 parents d70b8ec + ef760c2 commit d90bf9a

23 files changed

Lines changed: 540 additions & 162 deletions

libs/s25main/GamePlayer.cpp

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,15 +1332,12 @@ void GamePlayer::RegulateAllTroops()
13321332
milBld->RegulateTroops();
13331333
}
13341334

1335-
/// Prüft von allen Militärgebäuden die Fahnen neu
13361335
void GamePlayer::RecalcMilitaryFlags()
13371336
{
13381337
for(nobMilitary* milBld : buildings.GetMilitaryBuildings())
13391338
milBld->LookForEnemyBuildings(nullptr);
13401339
}
13411340

1342-
/// Sucht für Soldaten ein neues Militärgebäude, als Argument wird Referenz auf die
1343-
/// entsprechende Soldatenanzahl im Lagerhaus verlangt
13441341
void GamePlayer::NewSoldiersAvailable(const unsigned& soldier_count)
13451342
{
13461343
RTTR_Assert(soldier_count > 0);
@@ -1436,7 +1433,6 @@ void GamePlayer::ChangeMilitarySettings(const MilitarySettings& military_setting
14361433
RefreshDefenderList();
14371434
}
14381435

1439-
/// Setzt neue Werkzeugeinstellungen
14401436
void GamePlayer::ChangeToolsSettings(const ToolSettings& tools_settings,
14411437
const helpers::EnumArray<int8_t, Tool>& orderChanges)
14421438
{
@@ -1459,7 +1455,6 @@ void GamePlayer::ChangeToolsSettings(const ToolSettings& tools_settings,
14591455
}
14601456
}
14611457

1462-
/// Setzt neue Verteilungseinstellungen
14631458
void GamePlayer::ChangeDistribution(const Distributions& distribution_settings)
14641459
{
14651460
unsigned idx = 0;
@@ -1471,7 +1466,6 @@ void GamePlayer::ChangeDistribution(const Distributions& distribution_settings)
14711466
RecalcDistribution();
14721467
}
14731468

1474-
/// Setzt neue Baureihenfolge-Einstellungen
14751469
void GamePlayer::ChangeBuildOrder(bool useCustomBuildOrder, const BuildOrders& order_data)
14761470
{
14771471
this->useCustomBuildOrder_ = useCustomBuildOrder;
@@ -1570,7 +1564,6 @@ void GamePlayer::IncreaseMerchandiseStatistic(GoodType type)
15701564
}
15711565
}
15721566

1573-
/// Calculates current statistics
15741567
void GamePlayer::CalcStatistics()
15751568
{
15761569
// Waren aus der Inventur zählen
@@ -1710,7 +1703,6 @@ void GamePlayer::AcceptPact(const unsigned id, const PactType pt, const unsigned
17101703
}
17111704
}
17121705

1713-
/// Bündnis (real, d.h. spielentscheidend) abschließen
17141706
void GamePlayer::MakePact(const PactType pt, const unsigned char other_player, const unsigned duration)
17151707
{
17161708
pacts[other_player][pt].accepted = true;
@@ -1722,7 +1714,6 @@ void GamePlayer::MakePact(const PactType pt, const unsigned char other_player, c
17221714
std::make_unique<PostMsg>(world.GetEvMgr().GetCurrentGF(), pt, world.GetPlayer(other_player), true));
17231715
}
17241716

1725-
/// Zeigt an, ob ein Pakt besteht
17261717
PactState GamePlayer::GetPactState(const PactType pt, const unsigned char other_player) const
17271718
{
17281719
// Prüfen, ob Bündnis in Kraft ist
@@ -1739,7 +1730,6 @@ PactState GamePlayer::GetPactState(const PactType pt, const unsigned char other_
17391730
return PactState::None;
17401731
}
17411732

1742-
/// all allied players get a letter with the location
17431733
void GamePlayer::NotifyAlliesOfLocation(const MapPoint pt)
17441734
{
17451735
for(unsigned i = 0; i < world.GetNumPlayers(); ++i)
@@ -1751,7 +1741,6 @@ void GamePlayer::NotifyAlliesOfLocation(const MapPoint pt)
17511741
}
17521742
}
17531743

1754-
/// Gibt die verbleibende Dauer zurück, die ein Bündnis noch laufen wird (DURATION_INFINITE = für immer)
17551744
unsigned GamePlayer::GetRemainingPactTime(const PactType pt, const unsigned char other_player) const
17561745
{
17571746
if(pacts[other_player][pt].duration)
@@ -1769,8 +1758,6 @@ unsigned GamePlayer::GetRemainingPactTime(const PactType pt, const unsigned char
17691758
return 0;
17701759
}
17711760

1772-
/// Gibt Einverständnis, dass dieser Spieler den Pakt auflösen will
1773-
/// Falls dieser Spieler einen Bündnisvorschlag gemacht hat, wird dieser dagegen zurückgenommen
17741761
void GamePlayer::CancelPact(const PactType pt, const unsigned char otherPlayerIdx)
17751762
{
17761763
// Don't try to cancel pact with self
@@ -1888,11 +1875,9 @@ void GamePlayer::DecreaseInventoryWare(const GoodType ware, const unsigned count
18881875
global_inventory.Remove(ConvertShields(ware), count);
18891876
}
18901877

1891-
/// Registriert ein Schiff beim Einwohnermeldeamt
18921878
void GamePlayer::RegisterShip(noShip& ship)
18931879
{
18941880
ships.push_back(&ship);
1895-
// Evtl bekommt das Schiffchen gleich was zu tun?
18961881
GetJobForShip(ship);
18971882
}
18981883

@@ -1909,7 +1894,6 @@ struct ShipForHarbor
19091894
}
19101895
};
19111896

1912-
/// Schiff für Hafen bestellen
19131897
bool GamePlayer::OrderShip(nobHarborBuilding& hb)
19141898
{
19151899
std::vector<ShipForHarbor> sfh;
@@ -1981,18 +1965,15 @@ bool GamePlayer::OrderShip(nobHarborBuilding& hb)
19811965
return false;
19821966
}
19831967

1984-
/// Meldet das Schiff wieder ab
19851968
void GamePlayer::RemoveShip(noShip* ship)
19861969
{
19871970
auto it = helpers::find(ships, ship);
19881971
RTTR_Assert(it != ships.end());
19891972
ships.erase(it);
19901973
}
19911974

1992-
/// Versucht, für ein untätiges Schiff eine Arbeit zu suchen
19931975
void GamePlayer::GetJobForShip(noShip& ship)
19941976
{
1995-
// Evtl. steht irgendwo eine Expedition an und das Schiff kann diese übernehmen
19961977
nobHarborBuilding* best = nullptr;
19971978
int best_points = 0;
19981979
std::vector<Direction> best_route;
@@ -2040,7 +2021,6 @@ void GamePlayer::GetJobForShip(noShip& ship)
20402021

20412022
// Einen Hafen gefunden?
20422023
if(best)
2043-
// Dann bekommt das gleich der Hafen
20442024
ship.GoToHarbor(*best, best_route);
20452025
}
20462026

@@ -2049,7 +2029,6 @@ unsigned GamePlayer::GetShipID(const noShip& ship) const
20492029
return static_cast<unsigned>(helpers::indexOf(ships, &ship));
20502030
}
20512031

2052-
/// Gibt ein Schiff anhand der ID zurück bzw. nullptr, wenn keines mit der ID existiert
20532032
noShip* GamePlayer::GetShipByID(const unsigned ship_id) const
20542033
{
20552034
if(ship_id >= ships.size())
@@ -2058,19 +2037,6 @@ noShip* GamePlayer::GetShipByID(const unsigned ship_id) const
20582037
return ships[ship_id];
20592038
}
20602039

2061-
void GamePlayer::AddHarborsAtSea(std::vector<nobHarborBuilding*>& harborBuildings, const SeaId seaId) const
2062-
{
2063-
for(nobHarborBuilding* harbor : buildings.GetHarbors())
2064-
{
2065-
if(helpers::contains(harborBuildings, harbor))
2066-
continue;
2067-
2068-
if(world.IsHarborAtSea(harbor->GetHarborPosID(), seaId))
2069-
harborBuildings.push_back(harbor);
2070-
}
2071-
}
2072-
2073-
/// Gibt die Anzahl der Schiffe, die einen bestimmten Hafen ansteuern, zurück
20742040
unsigned GamePlayer::GetShipsToHarbor(const nobHarborBuilding& hb) const
20752041
{
20762042
unsigned count = 0;
@@ -2083,8 +2049,6 @@ unsigned GamePlayer::GetShipsToHarbor(const nobHarborBuilding& hb) const
20832049
return count;
20842050
}
20852051

2086-
/// Sucht einen Hafen in der Nähe, wo dieses Schiff seine Waren abladen kann
2087-
/// gibt true zurück, falls erfolgreich
20882052
bool GamePlayer::FindHarborForUnloading(noShip* ship, const MapPoint start, HarborId* goalHarborId,
20892053
std::vector<Direction>* route, nobHarborBuilding* exception)
20902054
{
@@ -2169,7 +2133,6 @@ void GamePlayer::TestForEmergencyProgramm()
21692133
}
21702134
}
21712135

2172-
/// Testet die Bündnisse, ob sie nicht schon abgelaufen sind
21732136
void GamePlayer::TestPacts()
21742137
{
21752138
for(unsigned i = 0; i < world.GetNumPlayers(); ++i)
@@ -2224,9 +2187,6 @@ bool GamePlayer::CanBuildCatapult() const
22242187
return bc.buildings[BuildingType::Catapult] + bc.buildingSites[BuildingType::Catapult] < max;
22252188
}
22262189

2227-
/// A ship has discovered new hostile territory --> determines if this is new
2228-
/// i.e. there is a sufficient distance to older locations
2229-
/// Returns true if yes and false if not
22302190
bool GamePlayer::ShipDiscoveredHostileTerritory(const MapPoint location)
22312191
{
22322192
// Prüfen, ob Abstand zu bisherigen Punkten nicht zu klein
@@ -2242,7 +2202,6 @@ bool GamePlayer::ShipDiscoveredHostileTerritory(const MapPoint location)
22422202
return true;
22432203
}
22442204

2245-
/// For debug only
22462205
bool GamePlayer::IsDependentFigure(const noFigure& fig)
22472206
{
22482207
for(const nobBaseWarehouse* wh : buildings.GetStorehouses())
@@ -2293,7 +2252,6 @@ struct WarehouseDistanceComparator
22932252
}
22942253
};
22952254

2296-
/// Send wares to warehouse wh
22972255
void GamePlayer::Trade(nobBaseWarehouse* goalWh, const boost_variant2<GoodType, Job>& what, unsigned count) const
22982256
{
22992257
if(!world.GetGGS().isEnabled(AddonId::TRADE))

libs/s25main/GamePlayer.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,6 @@ class GamePlayer : public GamePlayerInfo
253253
unsigned GetNumShips() const { return ships.size(); }
254254
/// Gibt liste der Schiffe zurück
255255
const std::vector<noShip*>& GetShips() const { return ships; }
256-
/// Add all unique harbor buildings at the given sea to the given vector
257-
void AddHarborsAtSea(std::vector<nobHarborBuilding*>& harborBuildings, SeaId seaId) const;
258256
/// Gibt die Anzahl der Schiffe, die einen bestimmten Hafen ansteuern, zurück
259257
unsigned GetShipsToHarbor(const nobHarborBuilding& hb) const;
260258
/// Sucht einen Hafen in der Nähe, wo dieses Schiff seine Waren abladen kann

libs/s25main/Pathfinding.cpp

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44

55
#include "GamePlayer.h"
66
#include "helpers/EnumRange.h"
7+
#include "helpers/IdRange.h"
78
#include "pathfinding/FreePathFinder.h"
89
#include "pathfinding/FreePathFinderImpl.h"
910
#include "pathfinding/PathConditionHuman.h"
1011
#include "pathfinding/PathConditionShip.h"
1112
#include "pathfinding/PathConditionTrade.h"
1213
#include "pathfinding/RoadPathFinder.h"
14+
#include "pathfinding/ShipPathData.h"
1315
#include "world/GameWorld.h"
1416
#include "gameTypes/ShipDirection.h"
1517
#include "gameData/GameConsts.h"
@@ -51,11 +53,10 @@ RoadPathDirection GameWorld::FindPathForWareOnRoads(const noRoadNode& start, con
5153
}
5254

5355
bool GameWorldBase::FindShipPathToHarbor(const MapPoint start, HarborId harborId, SeaId seaId,
54-
std::vector<Direction>* route, unsigned* length)
56+
std::vector<Direction>* route, unsigned* length) const
5557
{
56-
// Find the distance to the furthest harbor from the target harbor and take that as maximum
57-
unsigned maxDistance = 0;
5858
const MapPoint coastalPoint = GetCoastalPoint(harborId, seaId);
59+
RTTR_Assert(coastalPoint.isValid());
5960

6061
// already arrived?
6162
if(start == coastalPoint)
@@ -66,31 +67,60 @@ bool GameWorldBase::FindShipPathToHarbor(const MapPoint start, HarborId harborId
6667
route->clear();
6768
return true;
6869
}
70+
auto& shipPathData = GetShipPathData();
71+
// If we start from a harbor we can get the route directly w/o the costly pathfinding
72+
for(const auto startHbId : helpers::idRange<HarborId>(GetNumHarborPoints()))
73+
{
74+
if(GetCoastalPoint(startHbId, seaId) == start)
75+
{
76+
auto hbRoute = shipPathData.getHarborConnection(startHbId, harborId, seaId);
77+
if(length)
78+
*length = hbRoute.size();
79+
if(route)
80+
*route = std::move(hbRoute);
81+
}
82+
}
6983

70-
for(const auto dir : helpers::EnumRange<ShipDirection>{})
84+
// Try cache first
85+
bool reversed = false;
86+
if(const auto* cachedPath = shipPathData.findCachedPath(start, coastalPoint, reversed))
7187
{
72-
const std::vector<HarborPos::Neighbor>& neighbors = GetHarborNeighbors(harborId, dir);
73-
for(const HarborPos::Neighbor& neighbor : neighbors)
88+
if(route)
7489
{
75-
if(IsHarborAtSea(neighbor.id, seaId) && neighbor.distance > maxDistance)
76-
maxDistance = neighbor.distance;
90+
*route = *cachedPath;
91+
if(reversed)
92+
{
93+
std::reverse(route->begin(), route->end());
94+
for(auto& d : *route)
95+
d = d + 3u; // reverse direction
96+
}
7797
}
98+
if(length)
99+
*length = cachedPath->size();
100+
return true;
78101
}
79-
// Add a few fields reserve
80-
maxDistance += 6;
81-
return FindShipPath(start, coastalPoint, maxDistance, route, length);
102+
103+
// Not in cache -> compute full path and add
104+
std::vector<Direction> newRoute;
105+
if(!FindShipPath(start, coastalPoint, std::numeric_limits<unsigned>::max(), &newRoute, length))
106+
return false;
107+
shipPathData.addToCache(start, coastalPoint, newRoute);
108+
if(route)
109+
*route = std::move(newRoute);
110+
111+
return true;
82112
}
83113

84114
bool GameWorldBase::FindShipPath(const MapPoint start, const MapPoint dest, unsigned maxDistance,
85-
std::vector<Direction>* route, unsigned* length)
115+
std::vector<Direction>* route, unsigned* length) const
86116
{
87117
return GetFreePathFinder().FindPath(start, dest, true, maxDistance, route, length, nullptr,
88118
PathConditionShip(*this));
89119
}
90120

91121
/// Prüft, ob eine Schiffsroute noch Gültigkeit hat
92122
bool GameWorld::CheckShipRoute(const MapPoint start, const std::vector<Direction>& route, const unsigned pos,
93-
MapPoint* dest)
123+
MapPoint* dest) const
94124
{
95125
return GetFreePathFinder().CheckRoute(start, route, pos, PathConditionShip(*this), dest);
96126
}

libs/s25main/SerializedGameData.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@
108108
/// 12: leatheraddon added, three new building types and three new goods
109109
/// 13: SeaId & HarborId: World::harborData w/o dummy entry at 0
110110
/// 14: Remove "age" field in nobBaseMilitary
111-
static const unsigned currentGameDataVersion = 14;
111+
/// 15: Add sea to HarborPos::Neighbor
112+
static const unsigned currentGameDataVersion = 15;
112113
// clang-format on
113114

114115
std::unique_ptr<GameObject> SerializedGameData::Create_GameObject(const GO_Type got, const unsigned obj_id)

libs/s25main/TradePathCache.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org)
1+
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
22
//
33
// SPDX-License-Identifier: GPL-2.0-or-later
44

@@ -8,7 +8,7 @@
88
#include "world/GameWorld.h"
99
#include "gameData/GameConsts.h"
1010

11-
bool TradePathCache::pathExists(const MapPoint start, const MapPoint goal, const unsigned char player)
11+
bool TradePathCache::pathExists(const MapPoint start, const MapPoint goal, const PlayerIdx player)
1212
{
1313
RTTR_Assert(start != goal);
1414

@@ -57,7 +57,7 @@ int TradePathCache::findEntry(const MapPoint start, const MapPoint goal, const P
5757
return -1;
5858
}
5959

60-
void TradePathCache::addEntry(TradePath path, const unsigned char player)
60+
void TradePathCache::addEntry(TradePath path, const PlayerIdx player)
6161
{
6262
Entry entry{player, world.GetEvMgr().GetCurrentGF(), std::move(path)};
6363

0 commit comments

Comments
 (0)