• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 21283273387

23 Jan 2026 10:39AM UTC coverage: 50.757% (+0.09%) from 50.663%
21283273387

Pull #1679

github

web-flow
Merge c392aa0ea into 12da8bf44
Pull Request #1679: Add all classic S2 cheats and a few more

94 of 155 new or added lines in 12 files covered. (60.65%)

7 existing lines in 5 files now uncovered.

22798 of 44916 relevant lines covered (50.76%)

41539.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.39
/libs/s25main/world/GameWorldBase.cpp
1
// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "world/GameWorldBase.h"
6
#include "BQCalculator.h"
7
#include "GamePlayer.h"
8
#include "GlobalGameSettings.h"
9
#include "MapGeometry.h"
10
#include "RttrForeachPt.h"
11
#include "SoundManager.h"
12
#include "TradePathCache.h"
13
#include "addons/const_addons.h"
14
#include "buildings/nobHarborBuilding.h"
15
#include "buildings/nobMilitary.h"
16
#include "figures/nofPassiveSoldier.h"
17
#include "helpers/EnumRange.h"
18
#include "helpers/containerUtils.h"
19
#include "lua/LuaInterfaceGame.h"
20
#include "notifications/NodeNote.h"
21
#include "notifications/PlayerNodeNote.h"
22
#include "pathfinding/FreePathFinder.h"
23
#include "pathfinding/RoadPathFinder.h"
24
#include "nodeObjs/noFlag.h"
25
#include "gameData/BuildingProperties.h"
26
#include "gameData/GameConsts.h"
27
#include "gameData/TerrainDesc.h"
28
#include <utility>
29

30
GameWorldBase::GameWorldBase(std::vector<GamePlayer> players, const GlobalGameSettings& gameSettings, EventManager& em)
180✔
31
    : roadPathFinder(new RoadPathFinder(*this)), freePathFinder(new FreePathFinder(*this)), players(std::move(players)),
360✔
32
      gameSettings(gameSettings), em(em), soundManager(std::make_unique<SoundManager>()), lua(nullptr), gi(nullptr)
540✔
33
{}
180✔
34

35
GameWorldBase::~GameWorldBase() = default;
180✔
36

37
void GameWorldBase::Init(const MapExtent& mapSize, DescIdx<LandscapeDesc> lt)
169✔
38
{
39
    RTTR_Assert(GetDescription().terrain.size() > 0); // Must have game data initialized
169✔
40
    World::Init(mapSize, lt);
169✔
41
    freePathFinder->Init(mapSize);
169✔
42
}
169✔
43

44
void GameWorldBase::InitAfterLoad()
152✔
45
{
46
    RTTR_FOREACH_PT(MapPoint, GetSize())
275,504✔
47
        RecalcBQ(pt);
270,212✔
48
}
152✔
49

50
GamePlayer& GameWorldBase::GetPlayer(const unsigned id)
16,223✔
51
{
52
    RTTR_Assert(id < GetNumPlayers());
16,223✔
53
    return players[id];
16,223✔
54
}
55

56
const GamePlayer& GameWorldBase::GetPlayer(const unsigned id) const
48,454✔
57
{
58
    RTTR_Assert(id < GetNumPlayers());
48,454✔
59
    return players[id];
48,454✔
60
}
61

62
unsigned GameWorldBase::GetNumPlayers() const
82,838✔
63
{
64
    return players.size();
82,838✔
65
}
66

67
bool GameWorldBase::IsSinglePlayer() const
134✔
68
{
69
    bool foundPlayer = false;
134✔
70
    for(const PlayerInfo& player : players)
305✔
71
    {
72
        if(player.ps == PlayerState::Occupied)
177✔
73
        {
74
            if(foundPlayer)
140✔
75
                return false;
6✔
76
            else
77
                foundPlayer = true;
134✔
78
        }
79
    }
80
    return true;
128✔
81
}
82

83
bool GameWorldBase::IsRoadAvailable(const bool boat_road, const MapPoint pt) const
19,061✔
84
{
85
    // Hindernisse
86
    if(GetNode(pt).obj)
19,061✔
87
    {
88
        BlockingManner bm = GetNode(pt).obj->GetBM();
2,721✔
89
        if(bm != BlockingManner::None)
2,721✔
90
            return false;
2,629✔
91
    }
92

93
    // dont build on the border
94
    if(GetNode(pt).boundary_stones[BorderStonePos::OnPoint])
16,432✔
95
        return false;
6✔
96

97
    for(const auto dir : helpers::EnumRange<Direction>{})
256,864✔
98
    {
99
        // Roads around charburner piles are not possible
100
        if(GetNO(GetNeighbour(pt, dir))->GetBM() == BlockingManner::NothingAround)
96,160✔
101
            return false;
580✔
102

103
        // Other roads at this point?
104
        if(GetPointRoad(pt, dir) != PointRoad::None)
96,160✔
105
            return false;
580✔
106
    }
107

108
    // Terrain (unterscheiden, ob Wasser und Landweg)
109
    if(!boat_road)
15,846✔
110
    {
111
        bool flagPossible = false;
15,846✔
112

113
        for(const DescIdx<TerrainDesc> tIdx : GetTerrainsAround(pt))
109,578✔
114
        {
115
            const TerrainBQ bq = GetDescription().get(tIdx).GetBQ();
94,116✔
116
            if(bq == TerrainBQ::Danger)
94,116✔
117
                return false;
384✔
118
            else if(bq != TerrainBQ::Nothing)
93,732✔
119
                flagPossible = true;
93,364✔
120
        }
121

122
        return flagPossible;
15,462✔
123
    } else
124
    {
125
        // Beim Wasserweg muss um den Punkt herum Wasser sein
126
        if(!IsWaterPoint(pt))
×
127
            return false;
×
128
    }
129

130
    return true;
×
131
}
132

133
bool GameWorldBase::RoadAlreadyBuilt(const bool /*boat_road*/, const MapPoint start,
386✔
134
                                     const std::vector<Direction>& route)
135
{
136
    MapPoint tmp(start);
386✔
137
    for(unsigned i = 0; i < route.size() - 1; ++i)
386✔
138
    {
139
        // Richtiger Weg auf diesem Punkt?
140
        if(GetPointRoad(tmp, route[i]) == PointRoad::None)
386✔
141
            return false;
386✔
142

143
        tmp = GetNeighbour(tmp, route[i]);
×
144
    }
145
    return true;
×
146
}
147

148
bool GameWorldBase::IsOnRoad(const MapPoint& pt) const
1,544,874✔
149
{
150
    // This must be fast for BQ calculation so don't use GetVisiblePointRoad
151
    for(const auto roadDir : helpers::EnumRange<RoadDir>{})
15,442,092✔
152
        if(GetRoad(pt, roadDir) != PointRoad::None)
4,632,587✔
153
            return true;
1,289✔
154
    for(const auto roadDir : helpers::EnumRange<RoadDir>{})
15,434,938✔
155
    {
156
        const Direction oppositeDir = getOppositeDir(roadDir);
4,630,466✔
157
        if(GetRoad(GetNeighbour(pt, oppositeDir), roadDir) != PointRoad::None)
4,630,466✔
158
            return true;
167✔
159
    }
160
    return false;
1,543,418✔
161
}
162

163
bool GameWorldBase::IsFlagAround(const MapPoint& pt) const
1,012✔
164
{
165
    for(const MapPoint nb : GetNeighbours(pt))
7,052✔
166
    {
167
        if(GetNO(nb)->GetBM() == BlockingManner::Flag)
6,047✔
168
            return true;
7✔
169
    }
170
    return false;
1,005✔
171
}
172

173
bool GameWorldBase::IsAnyNeighborOwned(const MapPoint& pt) const
7✔
174
{
175
    for(const MapPoint& nb : GetNeighbours(pt))
49✔
176
        if(GetNode(nb).owner)
42✔
NEW
177
            return true;
×
178
    return false;
7✔
179
}
180

181
void GameWorldBase::RecalcBQForRoad(const MapPoint pt)
1,037✔
182
{
183
    RecalcBQ(pt);
1,037✔
184

185
    for(const Direction dir : {Direction::East, Direction::SouthEast, Direction::SouthWest})
4,148✔
186
        RecalcBQ(GetNeighbour(pt, dir));
3,111✔
187
}
1,037✔
188

189
namespace {
190
bool IsMilBldOfOwner(const GameWorldBase& gwb, MapPoint pt, unsigned char owner)
1,189✔
191
{
192
    return gwb.IsMilitaryBuildingOnNode(pt, false) && (gwb.GetNode(pt).owner == owner);
1,189✔
193
}
194
} // namespace
195

196
bool GameWorldBase::IsMilitaryBuildingNearNode(const MapPoint nPt, const unsigned char player) const
20✔
197
{
198
    // Im Umkreis von 4 Punkten ein Militärgebäude suchen
199
    return CheckPointsInRadius(
40✔
200
      nPt, 4, [this, player](auto pt, auto) { return IsMilBldOfOwner(*this, pt, player + 1); }, false);
1,229✔
201
}
202

203
bool GameWorldBase::IsMilitaryBuildingOnNode(const MapPoint pt, bool attackBldsOnly) const
1,227✔
204
{
205
    const noBase* obj = GetNO(pt);
1,227✔
206
    if(obj->GetType() == NodalObjectType::Building || obj->GetType() == NodalObjectType::Buildingsite)
1,227✔
207
    {
208
        BuildingType buildingType = static_cast<const noBaseBuilding*>(obj)->GetBuildingType();
52✔
209
        if(BuildingProperties::IsMilitary(buildingType))
52✔
210
            return true;
×
211
        if(!attackBldsOnly
52✔
212
           && (buildingType == BuildingType::Headquarters || buildingType == BuildingType::HarborBuilding))
16✔
213
            return true;
1✔
214
    }
215

216
    return false;
1,226✔
217
}
218

219
sortedMilitaryBlds GameWorldBase::LookForMilitaryBuildings(const MapPoint pt, unsigned short radius) const
44,038✔
220
{
221
    return militarySquares.GetBuildingsInRange(pt, radius);
44,038✔
222
}
223

224
noFlag* GameWorldBase::GetRoadFlag(MapPoint pt, Direction& dir, const helpers::OptionalEnum<Direction> prevDir)
117,473✔
225
{
226
    // Getting a flag is const
227
    const noFlag* flag = const_cast<const GameWorldBase*>(this)->GetRoadFlag(pt, dir, prevDir);
117,473✔
228
    // However we self are not const, so we allow returning a non-const flag pointer
229
    return const_cast<noFlag*>(flag);
117,473✔
230
}
231

232
const noFlag* GameWorldBase::GetRoadFlag(MapPoint pt, Direction& dir, helpers::OptionalEnum<Direction> prevDir) const
117,507✔
233
{
234
    while(true)
235
    {
236
        // suchen, wo der Weg weitergeht
237
        helpers::OptionalEnum<Direction> nextDir;
117,507✔
238
        for(const auto i : helpers::EnumRange<Direction>{})
1,878,332✔
239
        {
240
            if(i != prevDir && GetPointRoad(pt, i) != PointRoad::None)
704,535✔
241
            {
242
                nextDir = i;
383✔
243
                break;
383✔
244
            }
245
        }
246

247
        if(!nextDir)
117,507✔
248
            return nullptr;
117,473✔
249

250
        pt = GetNeighbour(pt, *nextDir);
383✔
251

252
        // endlich am Ende des Weges und an einer Flagge angekommen?
253
        if(GetNO(pt)->GetType() == NodalObjectType::Flag)
383✔
254
        {
255
            dir = *nextDir + 3u;
349✔
256
            return GetSpecObj<noFlag>(pt);
349✔
257
        }
258
        prevDir = *nextDir + 3u;
34✔
259
    }
34✔
260
}
261

262
Position GameWorldBase::GetNodePos(const MapPoint pt) const
6✔
263
{
264
    return ::GetNodePos(pt, GetNode(pt).altitude);
6✔
265
}
266

267
void GameWorldBase::VisibilityChanged(const MapPoint pt, unsigned player, Visibility /*oldVis*/, Visibility /*newVis*/)
164,680✔
268
{
269
    GetNotifications().publish(PlayerNodeNote(PlayerNodeNote::Visibility, pt, player));
164,680✔
270
}
164,680✔
271

272
/// Verändert die Höhe eines Punktes und die damit verbundenen Schatten
273
void GameWorldBase::AltitudeChanged(const MapPoint pt)
397✔
274
{
275
    RecalcBQAroundPointBig(pt);
397✔
276
    GetNotifications().publish(NodeNote(NodeNote::Altitude, pt));
397✔
277
}
397✔
278

279
void GameWorldBase::RecalcBQAroundPoint(const MapPoint pt)
2,434✔
280
{
281
    RecalcBQ(pt);
2,434✔
282
    for(const MapPoint nb : GetNeighbours(pt))
17,038✔
283
        RecalcBQ(nb);
14,604✔
284
}
2,434✔
285

286
void GameWorldBase::RecalcBQAroundPointBig(const MapPoint pt)
2,357✔
287
{
288
    // Point and radius 1
289
    RecalcBQAroundPoint(pt);
2,357✔
290
    // And radius 2
291
    for(unsigned i = 0; i < 12; ++i)
30,641✔
292
        RecalcBQ(GetNeighbour2(pt, i));
28,284✔
293
}
2,357✔
294

295
Visibility GameWorldBase::CalcVisiblityWithAllies(const MapPoint pt, const unsigned char player) const
14,382✔
296
{
297
    const MapNode& node = GetNode(pt);
14,382✔
298
    Visibility best_visibility = node.fow[player].visibility;
14,382✔
299

300
    if(best_visibility == Visibility::Visible)
14,382✔
301
        return best_visibility;
11,190✔
302

303
    /// Teamsicht aktiviert?
304
    if(GetGGS().teamView)
3,192✔
305
    {
306
        const GamePlayer& curPlayer = GetPlayer(player);
3,192✔
307
        // Dann prüfen, ob Teammitglieder evtl. eine bessere Sicht auf diesen Punkt haben
308
        for(unsigned i = 0; i < GetNumPlayers(); ++i)
11,103✔
309
        {
310
            if(i != player && curPlayer.IsAlly(i))
7,911✔
311
            {
312
                if(node.fow[i].visibility > best_visibility)
2,841✔
313
                    best_visibility = node.fow[i].visibility;
100✔
314
            }
315
        }
316
    }
317

318
    return best_visibility;
3,192✔
319
}
320

321
bool GameWorldBase::IsCoastalPointToSeaWithHarbor(const MapPoint pt) const
217✔
322
{
323
    unsigned short sea = GetSeaFromCoastalPoint(pt);
217✔
324
    if(sea)
217✔
325
    {
326
        const unsigned numHarborPts = GetNumHarborPoints();
62✔
327
        for(unsigned i = 1; i <= numHarborPts; i++)
62✔
328
        {
329
            if(IsHarborAtSea(i, sea))
62✔
330
                return true;
62✔
331
        }
332
    }
333
    return false;
155✔
334
}
335

336
template<typename T_IsHarborOk>
337
unsigned GameWorldBase::GetHarborInDir(const MapPoint pt, const unsigned origin_harborId, const ShipDirection& dir,
3✔
338
                                       T_IsHarborOk isHarborOk) const
339
{
340
    RTTR_Assert(origin_harborId);
3✔
341

342
    // Herausfinden, in welcher Richtung sich dieser Punkt vom Ausgangspunkt unterscheidet
343
    helpers::OptionalEnum<Direction> coastal_point_dir;
3✔
344
    const MapPoint hbPt = GetHarborPoint(origin_harborId);
3✔
345

346
    for(const auto dir : helpers::EnumRange<Direction>{})
18✔
347
    {
348
        if(GetNeighbour(hbPt, dir) == pt)
9✔
349
        {
350
            coastal_point_dir = dir;
3✔
351
            break;
3✔
352
        }
353
    }
354

355
    RTTR_Assert(coastal_point_dir);
3✔
356

357
    unsigned short seaId = GetSeaId(origin_harborId, *coastal_point_dir);
3✔
358
    const std::vector<HarborPos::Neighbor>& neighbors = GetHarborNeighbors(origin_harborId, dir);
3✔
359

360
    for(auto neighbor : neighbors)
4✔
361
    {
362
        if(IsHarborAtSea(neighbor.id, seaId) && isHarborOk(neighbor.id))
3✔
363
            return neighbor.id;
2✔
364
    }
365

366
    // Nichts gefunden
367
    return 0;
1✔
368
}
369

370
/// Functor that returns true, when the owner of a point is set and different than the player
371
struct IsPointOwnerDifferent
372
{
373
    const GameWorldBase& gwb;
374
    // Owner to compare. Note that owner=0 --> No owner => owner=player+1
375
    const unsigned char cmpOwner;
376

377
    IsPointOwnerDifferent(const GameWorldBase& gwb, const unsigned char player) : gwb(gwb), cmpOwner(player + 1) {}
4✔
378

379
    bool operator()(const MapPoint pt, unsigned /*distance*/) const
181✔
380
    {
381
        const unsigned char owner = gwb.GetNode(pt).owner;
181✔
382
        return owner != 0 && owner != cmpOwner;
181✔
383
    }
384
};
385

386
/// Ist es an dieser Stelle für einen Spieler möglich einen Hafen zu bauen
387
bool GameWorldBase::IsHarborPointFree(const unsigned harborId, const unsigned char player) const
5✔
388
{
389
    MapPoint hbPos(GetHarborPoint(harborId));
5✔
390

391
    // Überprüfen, ob das Gebiet in einem bestimmten Radius entweder vom Spieler oder gar nicht besetzt ist außer wenn
392
    // der Hafen und die Flagge im Spielergebiet liegen
393
    MapPoint flagPos = GetNeighbour(hbPos, Direction::SouthEast);
5✔
394
    if(GetNode(hbPos).owner != player + 1 || GetNode(flagPos).owner != player + 1)
5✔
395
    {
396
        if(CheckPointsInRadius(hbPos, 4, IsPointOwnerDifferent(*this, player), false))
4✔
397
            return false;
1✔
398
    }
399

400
    return GetNode(hbPos).bq == BuildingQuality::Harbor;
4✔
401
}
402

403
/// Sucht freie Hafenpunkte, also wo noch ein Hafen gebaut werden kann
404
unsigned GameWorldBase::GetNextFreeHarborPoint(const MapPoint pt, const unsigned origin_harborId,
3✔
405
                                               const ShipDirection& dir, const unsigned char player) const
406
{
407
    return GetHarborInDir(pt, origin_harborId, dir,
3✔
408
                          [this, player](auto harborId) { return this->IsHarborPointFree(harborId, player); });
6✔
409
}
410

411
/// Bestimmt für einen beliebigen Punkt auf der Karte die Entfernung zum nächsten Hafenpunkt
412
unsigned GameWorldBase::CalcDistanceToNearestHarbor(const MapPoint pos) const
5✔
413
{
414
    unsigned min_distance = 0xffffffff;
5✔
415
    for(unsigned i = 1; i <= GetNumHarborPoints(); ++i)
45✔
416
        min_distance = std::min(min_distance, this->CalcDistance(pos, GetHarborPoint(i)));
40✔
417

418
    return min_distance;
5✔
419
}
420

421
/// returns true when a harborpoint is in SEAATTACK_DISTANCE for figures!
422
bool GameWorldBase::IsAHarborInSeaAttackDistance(const MapPoint pos) const
×
423
{
424
    for(unsigned i = 1; i <= GetNumHarborPoints(); ++i)
×
425
    {
426
        if(CalcDistance(pos, GetHarborPoint(i)) < SEAATTACK_DISTANCE)
×
427
        {
428
            if(FindHumanPath(pos, GetHarborPoint(i), SEAATTACK_DISTANCE))
×
429
                return true;
×
430
        }
431
    }
432
    return false;
×
433
}
434

435
std::vector<unsigned> GameWorldBase::GetUsableTargetHarborsForAttack(const MapPoint targetPt,
33✔
436
                                                                     std::vector<bool>& use_seas,
437
                                                                     const unsigned char player_attacker) const
438
{
439
    // Walk to the flag of the bld/harbor. Important to check because in some locations where the coast is north of the
440
    // harbor this might be blocked
441
    const MapPoint flagPt = GetNeighbour(targetPt, Direction::SouthEast);
33✔
442
    std::vector<unsigned> harbor_points;
33✔
443
    // Check each possible harbor
444
    for(unsigned curHbId = 1; curHbId <= GetNumHarborPoints(); ++curHbId)
297✔
445
    {
446
        const MapPoint harborPt = GetHarborPoint(curHbId);
264✔
447

448
        if(CalcDistance(harborPt, targetPt) > SEAATTACK_DISTANCE)
264✔
449
            continue;
202✔
450

451
        // Not attacking this harbor and harbors block?
452
        if(targetPt != harborPt && GetGGS().getSelection(AddonId::SEA_ATTACK) == 1)
70✔
453
        {
454
            // Does an enemy harbor exist at current harbor spot? -> Can't attack through this harbor spot
455
            const auto* hb = GetSpecObj<nobHarborBuilding>(harborPt);
32✔
456
            if(hb && GetPlayer(player_attacker).IsAttackable(hb->GetPlayer()))
32✔
457
                continue;
8✔
458
        }
459

460
        // add seaIds from which we can actually attack the harbor
461
        bool harborinlist = false;
62✔
462
        for(const auto dir : helpers::enumRange<Direction>())
992✔
463
        {
464
            const unsigned short seaId = GetSeaId(curHbId, dir);
372✔
465
            if(!seaId)
372✔
466
                continue;
310✔
467
            // checks previously tested sea ids to skip pathfinding
468
            bool previouslytested = false;
62✔
469
            for(unsigned k = 0; k < rttr::enum_cast(dir); k++)
205✔
470
            {
471
                if(seaId == GetSeaId(curHbId, Direction(k)))
143✔
472
                {
473
                    previouslytested = true;
×
474
                    break;
×
475
                }
476
            }
477
            if(previouslytested)
62✔
478
                continue;
×
479

480
            // Can figures reach flag from coast
481
            const MapPoint coastalPt = GetCoastalPoint(curHbId, seaId);
62✔
482
            if((flagPt == coastalPt) || FindHumanPath(flagPt, coastalPt, SEAATTACK_DISTANCE))
62✔
483
            {
484
                use_seas.at(seaId - 1) = true;
56✔
485
                if(!harborinlist)
56✔
486
                {
487
                    harbor_points.push_back(curHbId);
56✔
488
                    harborinlist = true;
56✔
489
                }
490
            }
491
        }
492
    }
493
    return harbor_points;
66✔
494
}
495

496
std::vector<unsigned short> GameWorldBase::GetFilteredSeaIDsForAttack(const MapPoint targetPt,
×
497
                                                                      const std::vector<unsigned short>& usableSeas,
498
                                                                      const unsigned char player_attacker) const
499
{
500
    // Walk to the flag of the bld/harbor. Important to check because in some locations where the coast is north of the
501
    // harbor this might be blocked
502
    const MapPoint flagPt = GetNeighbour(targetPt, Direction::SouthEast);
×
503
    std::vector<unsigned short> confirmedSeaIds;
×
504
    // Check each possible harbor
505
    for(unsigned curHbId = 1; curHbId <= GetNumHarborPoints(); ++curHbId)
×
506
    {
507
        const MapPoint harborPt = GetHarborPoint(curHbId);
×
508

509
        if(CalcDistance(harborPt, targetPt) > SEAATTACK_DISTANCE)
×
510
            continue;
×
511

512
        // Not attacking this harbor and harbors block?
513
        if(targetPt != harborPt && GetGGS().getSelection(AddonId::SEA_ATTACK) == 1)
×
514
        {
515
            // Does an enemy harbor exist at current harbor spot? -> Can't attack through this harbor spot
516
            const auto* hb = GetSpecObj<nobHarborBuilding>(harborPt);
×
517
            if(hb && GetPlayer(player_attacker).IsAttackable(hb->GetPlayer()))
×
518
                continue;
×
519
        }
520

521
        for(const auto dir : helpers::enumRange<Direction>())
×
522
        {
523
            const unsigned short seaId = GetSeaId(curHbId, dir);
×
524
            if(!seaId)
×
525
                continue;
×
526
            // sea id is not in compare list or already confirmed? -> skip rest
527
            if(!helpers::contains(usableSeas, seaId) || helpers::contains(confirmedSeaIds, seaId))
×
528
                continue;
×
529

530
            // checks previously tested sea ids to skip pathfinding
531
            bool previouslytested = false;
×
532
            for(unsigned k = 0; k < rttr::enum_cast(dir); k++)
×
533
            {
534
                if(seaId == GetSeaId(curHbId, Direction(k)))
×
535
                {
536
                    previouslytested = true;
×
537
                    break;
×
538
                }
539
            }
540
            if(previouslytested)
×
541
                continue;
×
542

543
            // Can figures reach flag from coast
544
            MapPoint coastalPt = GetCoastalPoint(curHbId, seaId);
×
545
            if((flagPt == coastalPt) || FindHumanPath(flagPt, coastalPt, SEAATTACK_DISTANCE))
×
546
            {
547
                confirmedSeaIds.push_back(seaId);
×
548
                // all sea ids confirmed? return without changes
549
                if(confirmedSeaIds.size() == usableSeas.size())
×
550
                    return confirmedSeaIds;
×
551
            }
552
        }
553
    }
554
    return confirmedSeaIds;
×
555
}
556

557
/// Liefert Hafenpunkte im Umkreis von einem bestimmten Militärgebäude
558
std::vector<unsigned> GameWorldBase::GetHarborPointsAroundMilitaryBuilding(const MapPoint pt) const
10✔
559
{
560
    std::vector<unsigned> harbor_points;
10✔
561
    // Nach Hafenpunkten in der Nähe des angegriffenen Gebäudes suchen
562
    // Alle unsere Häfen durchgehen
563
    for(unsigned i = 1; i <= GetNumHarborPoints(); ++i)
90✔
564
    {
565
        const MapPoint harborPt = GetHarborPoint(i);
80✔
566

567
        if(CalcDistance(harborPt, pt) <= SEAATTACK_DISTANCE)
80✔
568
        {
569
            // Wird ein Weg vom Militärgebäude zum Hafen gefunden bzw. Ziel = Hafen?
570
            if(pt == harborPt || FindHumanPath(pt, harborPt, SEAATTACK_DISTANCE))
20✔
571
                harbor_points.push_back(i);
20✔
572
        }
573
    }
574
    return harbor_points;
10✔
575
}
576

577
/// Gibt Anzahl oder geschätzte Stärke(rang summe + anzahl) der verfügbaren Soldaten die zu einem Schiffsangriff starten
578
/// können von einer bestimmten sea id aus
579
unsigned GameWorldBase::GetNumSoldiersForSeaAttackAtSea(const unsigned char player_attacker, unsigned short seaid,
×
580
                                                        bool returnCount) const
581
{
582
    // Liste alle Militärgebäude des Angreifers, die Soldaten liefern
583
    std::vector<nobHarborBuilding::SeaAttackerBuilding> buildings;
×
584
    unsigned attackercount = 0;
×
585
    // Angrenzende Häfen des Angreifers an den entsprechenden Meeren herausfinden
586
    const std::list<nobHarborBuilding*>& harbors = GetPlayer(player_attacker).GetBuildingRegister().GetHarbors();
×
587
    for(auto* harbor : harbors)
×
588
    {
589
        // Bestimmen, ob Hafen an einem der Meere liegt, über die sich auch die gegnerischen
590
        // Hafenpunkte erreichen lassen
591
        if(!IsHarborAtSea(harbor->GetHarborPosID(), seaid))
×
592
            continue;
×
593

594
        std::vector<nobHarborBuilding::SeaAttackerBuilding> tmp = harbor->GetAttackerBuildingsForSeaIdAttack();
×
595
        buildings.insert(buildings.begin(), tmp.begin(), tmp.end());
×
596
    }
597

598
    // Die Soldaten aus allen Militärgebäuden sammeln
599
    for(auto& building : buildings)
×
600
    {
601
        // Soldaten holen
602
        std::vector<nofPassiveSoldier*> tmp_soldiers =
603
          building.building->GetSoldiersForAttack(building.harbor->GetPos());
×
604

605
        // Überhaupt welche gefunden?
606
        if(tmp_soldiers.empty())
×
607
            continue;
×
608

609
        // Soldaten hinzufügen
610
        for(auto& tmp_soldier : tmp_soldiers)
×
611
        {
612
            if(returnCount)
×
613
                attackercount++;
×
614
            else
615
                attackercount += (tmp_soldier->GetRank() + 1); // private is rank 0 -> increase by 1-5
×
616
        }
617
    }
618
    return attackercount;
×
619
}
620

621
/// Sucht verfügbare Soldaten, um dieses Militärgebäude mit einem Seeangriff anzugreifen
622
std::vector<GameWorldBase::PotentialSeaAttacker>
623
GameWorldBase::GetSoldiersForSeaAttack(const unsigned char player_attacker, const MapPoint pt) const
69✔
624
{
625
    std::vector<GameWorldBase::PotentialSeaAttacker> attackers;
69✔
626
    // sea attack abgeschaltet per addon?
627
    if(!GetGGS().isEnabled(AddonId::SEA_ATTACK))
69✔
628
        return attackers;
10✔
629
    // Do we have an attackble military building?
630
    const auto* milBld = GetSpecObj<nobBaseMilitary>(pt);
59✔
631
    if(!milBld || !milBld->IsAttackable(player_attacker))
59✔
632
        return attackers;
26✔
633
    std::vector<bool> use_seas(GetNumSeas());
66✔
634

635
    // Mögliche Hafenpunkte in der Nähe des Gebäudes
636
    std::vector<unsigned> defender_harbors = GetUsableTargetHarborsForAttack(pt, use_seas, player_attacker);
66✔
637

638
    // Liste alle Militärgebäude des Angreifers, die Soldaten liefern
639
    std::vector<nobHarborBuilding::SeaAttackerBuilding> buildings;
66✔
640

641
    // Angrenzende Häfen des Angreifers an den entsprechenden Meeren herausfinden
642
    const std::list<nobHarborBuilding*>& harbors = GetPlayer(player_attacker).GetBuildingRegister().GetHarbors();
33✔
643
    for(auto* harbor : harbors)
66✔
644
    {
645
        // Bestimmen, ob Hafen an einem der Meere liegt, über die sich auch die gegnerischen
646
        // Hafenpunkte erreichen lassen
647
        bool is_at_sea = false;
33✔
648
        for(const auto dir : helpers::EnumRange<Direction>{})
390✔
649
        {
650
            const unsigned short seaId = GetSeaId(harbor->GetHarborPosID(), dir);
152✔
651
            if(seaId && use_seas[seaId - 1])
152✔
652
            {
653
                is_at_sea = true;
23✔
654
                break;
23✔
655
            }
656
        }
657

658
        if(!is_at_sea)
33✔
659
            continue;
10✔
660

661
        std::vector<nobHarborBuilding::SeaAttackerBuilding> tmp =
662
          harbor->GetAttackerBuildingsForSeaAttack(defender_harbors);
46✔
663
        for(auto& itBld : tmp)
46✔
664
        {
665
            // Check if the building was already inserted
666
            auto oldBldIt =
667
              helpers::find_if(buildings, nobHarborBuilding::SeaAttackerBuilding::CmpBuilding(itBld.building));
23✔
668
            if(oldBldIt == buildings.end())
23✔
669
            {
670
                // Not found -> Add
671
                buildings.push_back(itBld);
23✔
672
            } else if(oldBldIt->distance > itBld.distance
×
673
                      || (oldBldIt->distance == itBld.distance
×
674
                          && oldBldIt->harbor->GetObjId() > itBld.harbor->GetObjId()))
×
675
            {
676
                // New distance is smaller (with tie breaker for async prevention) -> update
677
                *oldBldIt = itBld;
×
678
            }
679
        }
680
    }
681

682
    // Die Soldaten aus allen Militärgebäuden sammeln
683
    for(const auto& bld : buildings)
56✔
684
    {
685
        // Soldaten holen
686
        std::vector<nofPassiveSoldier*> tmp_soldiers = bld.building->GetSoldiersForAttack(bld.harbor->GetPos());
46✔
687

688
        // Soldaten hinzufügen
689
        for(nofPassiveSoldier* soldier : tmp_soldiers)
116✔
690
        {
691
            RTTR_Assert(!helpers::contains_if(attackers, PotentialSeaAttacker::CmpSoldier(soldier)));
93✔
692
            attackers.push_back(PotentialSeaAttacker(soldier, bld.harbor, bld.distance));
93✔
693
        }
694
    }
695

696
    return attackers;
33✔
697
}
698

699
void GameWorldBase::RecalcBQ(const MapPoint pt)
531,034✔
700
{
701
    BQCalculator calcBQ(*this);
531,034✔
702
    if(SetBQ(pt, calcBQ(pt, [this](auto pt) { return this->IsOnRoad(pt); })))
2,071,707✔
703
    {
704
        GetNotifications().publish(NodeNote(NodeNote::BQ, pt));
231,686✔
705
    }
706
}
531,034✔
707

708
void GameWorldBase::SetComputerBarrier(const MapPoint& pt, unsigned radius)
2✔
709
{
710
    for(const auto& pt : GetPointsInRadiusWithCenter(pt, radius))
544✔
711
        ptsInsideComputerBarriers.insert(pt);
542✔
712
}
2✔
713

714
bool GameWorldBase::IsInsideComputerBarrier(const MapPoint& pt) const
652✔
715
{
716
    return helpers::contains(ptsInsideComputerBarriers, pt);
652✔
717
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc