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

Return-To-The-Roots / s25client / 21211219610

21 Jan 2026 01:23PM UTC coverage: 50.61% (+0.009%) from 50.601%
21211219610

push

github

web-flow
Merge pull request #1738 from kubaau/cheat_enemy_productivity

14 of 26 new or added lines in 5 files covered. (53.85%)

2 existing lines in 1 file now uncovered.

22656 of 44766 relevant lines covered (50.61%)

34851.98 hits per line

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

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

5
#include "world/GameWorldViewer.h"
6
#include "Cheats.h"
7
#include "GameInterface.h"
8
#include "GamePlayer.h"
9
#include "GlobalGameSettings.h"
10
#include "RttrForeachPt.h"
11
#include "buildings/nobMilitary.h"
12
#include "network/GameClient.h"
13
#include "notifications/NodeNote.h"
14
#include "notifications/PlayerNodeNote.h"
15
#include "notifications/RoadNote.h"
16
#include "world/BQCalculator.h"
17
#include "world/GameWorldBase.h"
18
#include "nodeObjs/noShip.h"
19
#include "gameTypes/MapCoordinates.h"
20
#include "gameData/BuildingProperties.h"
21

22
GameWorldViewer::GameWorldViewer(unsigned playerId, GameWorldBase& gwb) : playerId_(playerId), gwb(gwb)
22✔
23
{
24
    InitVisualData();
22✔
25
    maxNodeAltitude_ = 0;
22✔
26
    RTTR_FOREACH_PT(MapPoint, gwb.GetSize())
48,648✔
27
    {
28
        maxNodeAltitude_ = std::max(maxNodeAltitude_, gwb.GetNode(pt).altitude);
47,776✔
29
    }
30
    evAltitudeChanged = gwb.GetNotifications().subscribe<NodeNote>([this](const NodeNote& note) {
44✔
31
        if(note.type == NodeNote::Altitude)
6,756✔
32
        {
33
            maxNodeAltitude_ = std::max(maxNodeAltitude_, this->gwb.GetNode(note.pos).altitude);
1✔
34
        }
35
    });
22✔
36
}
22✔
37

38
void GameWorldViewer::InitVisualData()
22✔
39
{
40
    visualNodes.Resize(gwb.GetSize());
22✔
41
    RTTR_FOREACH_PT(MapPoint, gwb.GetSize())
48,648✔
42
    {
43
        VisualMapNode& vNode = visualNodes[pt];
47,776✔
44
        const MapNode& node = gwb.GetNode(pt);
47,776✔
45
        vNode.bq = node.bq;
47,776✔
46
        // Roads are only overlays. At first we don't have any -> PointRoad::None=use real road
47
        std::fill(vNode.roads.begin(), vNode.roads.end(), PointRoad::None);
47,776✔
48
    }
49
    evRoadConstruction =
50
      gwb.GetNotifications().subscribe<RoadNote>([this](const RoadNote& note) { RoadConstructionEnded(note); });
40✔
51
    evBQChanged = gwb.GetNotifications().subscribe<NodeNote>([this](const NodeNote& note) {
44✔
52
        if(note.type == NodeNote::BQ)
6,756✔
53
            RecalcBQ(note.pos);
2,912✔
54
    });
22✔
55
}
22✔
56

57
void GameWorldViewer::InitTerrainRenderer()
×
58
{
59
    tr.GenerateOpenGL(*this);
×
60
    // Notify renderer about altitude changes
61
    evAltitudeChanged = gwb.GetNotifications().subscribe<NodeNote>([this](const NodeNote& note) {
×
62
        if(note.type == NodeNote::Altitude)
×
63
        {
64
            maxNodeAltitude_ = std::max(maxNodeAltitude_, gwb.GetNode(note.pos).altitude);
×
65
            tr.AltitudeChanged(note.pos, *this);
×
66
        }
67
    });
×
68
    // And visibility changes
69
    evVisibilityChanged = gwb.GetNotifications().subscribe<PlayerNodeNote>([this](const PlayerNodeNote& note) {
×
70
        if(note.type == PlayerNodeNote::Visibility)
×
71
            VisibilityChanged(note.pt, note.player);
×
72
    });
×
73
}
×
74

75
SoundManager& GameWorldViewer::GetSoundMgr()
×
76
{
77
    return gwb.GetSoundMgr();
×
78
}
79

80
const GamePlayer& GameWorldViewer::GetPlayer() const
1,795✔
81
{
82
    return GetWorld().GetPlayer(playerId_);
1,795✔
83
}
84

85
unsigned GameWorldViewer::GetNumPlayers() const
×
86
{
87
    return GetWorld().GetNumPlayers();
×
88
}
89

90
unsigned GameWorldViewer::GetNumSoldiersForAttack(const MapPoint pt) const
53✔
91
{
92
    const auto* attacked_building = GetWorld().GetSpecObj<nobBaseMilitary>(pt);
53✔
93
    // Can we actually attack this bld?
94
    if(!attacked_building || !attacked_building->IsAttackable(playerId_))
53✔
95
        return 0;
26✔
96

97
    // Militärgebäude in der Nähe finden
98
    unsigned total_count = 0;
27✔
99

100
    sortedMilitaryBlds buildings = GetWorld().LookForMilitaryBuildings(pt, 3);
27✔
101
    for(auto& building : buildings)
457✔
102
    {
103
        // Muss ein Gebäude von uns sein und darf nur ein "normales Militärgebäude" sein (kein HQ etc.)
104
        if(building->GetPlayer() == playerId_ && BuildingProperties::IsMilitary(building->GetBuildingType()))
188✔
105
            total_count += static_cast<nobMilitary*>(building)->GetNumSoldiersForAttack(pt);
25✔
106
    }
107

108
    return total_count;
27✔
109
}
110

111
BuildingQuality GameWorldViewer::GetBQ(const MapPoint& pt) const
452✔
112
{
113
    return GetWorld().AdjustBQ(pt, playerId_, visualNodes[pt].bq);
452✔
114
}
115

116
Visibility GameWorldViewer::GetVisibility(const MapPoint pt, const bool checkAllVisible) const
1,781✔
117
{
118
    if(checkAllVisible && IsAllVisible())
1,781✔
119
        return Visibility::Visible;
2✔
120
    return GetWorld().CalcVisiblityWithAllies(pt, playerId_);
1,779✔
121
}
122

123
bool GameWorldViewer::IsAllVisible() const
1,781✔
124
{
125
    const auto* const gi = GetWorld().GetGameInterface();
1,781✔
126
    if(gi && gi->GI_GetCheats().isAllVisible())
1,781✔
127
        return true;
2✔
128

129
    /// Replaymodus und FoW aus? Dann alles sichtbar
130
    if(GAMECLIENT.IsReplayModeOn() && GAMECLIENT.IsReplayFOWDisabled())
1,779✔
NEW
131
        return true;
×
132

133
    // Spieler schon tot? Dann auch alles sichtbar?
134
    if(GetPlayer().IsDefeated())
1,779✔
NEW
135
        return true;
×
136
    return false;
1,779✔
137
}
138

139
bool GameWorldViewer::IsOwner(const MapPoint& pt) const
1✔
140
{
141
    return GetWorld().GetNode(pt).owner == playerId_ + 1;
1✔
142
}
143

144
bool GameWorldViewer::IsPlayerTerritory(const MapPoint& pt) const
79✔
145
{
146
    return GetWorld().IsPlayerTerritory(pt, playerId_ + 1);
79✔
147
}
148

149
const MapNode& GameWorldViewer::GetNode(const MapPoint& pt) const
5,298✔
150
{
151
    return GetWorld().GetNode(pt);
5,298✔
152
}
153

154
MapPoint GameWorldViewer::GetNeighbour(const MapPoint pt, const Direction dir) const
3,343✔
155
{
156
    return GetWorld().GetNeighbour(pt, dir);
3,343✔
157
}
158

159
void GameWorldViewer::RecalcAllColors()
×
160
{
161
    tr.UpdateAllColors(*this);
×
162
}
×
163

164
/// liefert sichtbare Straße, im FoW entsprechend die FoW-Straße
165
PointRoad GameWorldViewer::GetVisibleRoad(const MapPoint pt, RoadDir roadDir, const Visibility visibility) const
5,286✔
166
{
167
    if(visibility == Visibility::Visible)
5,286✔
168
        return GetVisibleRoad(pt, roadDir);
5,286✔
169
    else if(visibility == Visibility::FogOfWar)
×
170
        return GetYoungestFOWNode(pt).roads[roadDir];
×
171
    else
172
        return PointRoad::None;
×
173
}
174

175
PointRoad GameWorldViewer::GetVisibleRoad(const MapPoint pt, RoadDir roadDir) const
11,752✔
176
{
177
    const PointRoad visualResult = visualNodes[pt].roads[roadDir];
11,752✔
178
    if(visualResult != PointRoad::None)
11,752✔
179
        return visualResult;
150✔
180
    else
181
        return GetWorld().GetRoad(pt, roadDir);
11,602✔
182
}
183

184
PointRoad GameWorldViewer::GetVisiblePointRoad(MapPoint pt, Direction dir) const
6✔
185
{
186
    const RoadDir rDir = GetWorld().toRoadDir(pt, dir);
6✔
187
    return GetVisibleRoad(pt, rDir);
6✔
188
}
189

190
void GameWorldViewer::SetVisiblePointRoad(MapPoint pt, Direction dir, PointRoad type)
133✔
191
{
192
    const RoadDir rDir = GetWorld().toRoadDir(pt, dir);
133✔
193
    visualNodes[pt].roads[rDir] = type;
133✔
194
}
133✔
195

196
bool GameWorldViewer::IsOnRoad(const MapPoint& pt) const
1,524✔
197
{
198
    // This must be fast for BQ calculation so don't use GetVisiblePointRoad
199
    for(const auto roadDir : helpers::EnumRange<RoadDir>{})
12,216✔
200
        if(GetVisibleRoad(pt, roadDir) != PointRoad::None)
3,583✔
201
            return true;
523✔
202
    for(const auto roadDir : helpers::EnumRange<RoadDir>{})
9,610✔
203
    {
204
        const Direction oppositeDir = getOppositeDir(roadDir);
2,877✔
205
        if(GetVisibleRoad(GetNeighbour(pt, oppositeDir), roadDir) != PointRoad::None)
2,877✔
206
            return true;
74✔
207
    }
208
    return false;
927✔
209
}
210

211
/// Return a ship at this position owned by the given player. Prefers ships that need instructions.
212
const noShip* GameWorldViewer::GetShip(const MapPoint pt) const
1✔
213
{
214
    const noShip* resultShip = nullptr;
1✔
215

216
    // Check if we want this ship and set resultShip. Return true if we found a ship which is waiting for instructions
217
    auto checkShip = [pt, &resultShip, playerId = playerId_](const noShip& ship) {
1✔
218
        if(ship.GetPlayerId() == playerId && (ship.GetPos() == pt || ship.GetDestinationForCurrentMove() == pt))
×
219
        {
220
            resultShip = &ship;
×
221
            if(ship.IsWaitingForExpeditionInstructions())
×
222
                return true;
×
223
        }
224
        return false;
×
225
    };
1✔
226
    const auto& world = GetWorld();
1✔
227
    auto checkPointForShips = [&world, checkShip](const MapPoint curPt, auto /*radius*/) {
7✔
228
        for(const auto& figure : world.GetFigures(curPt))
28✔
229
        {
230
            if(figure.GetGOT() == GO_Type::Ship && checkShip(static_cast<const noShip&>(figure)))
×
231
                return true;
×
232
        }
233
        return false;
7✔
234
    };
1✔
235
    world.CheckPointsInRadius(pt, 1u, checkPointForShips, true);
1✔
236

237
    return resultShip;
1✔
238
}
239

240
/// Gibt die verfügbar Anzahl der Angreifer für einen Seeangriff zurück
241
unsigned GameWorldViewer::GetNumSoldiersForSeaAttack(const MapPoint pt) const
34✔
242
{
243
    return unsigned(GetWorld().GetSoldiersForSeaAttack(playerId_, pt).size());
34✔
244
}
245

246
void GameWorldViewer::ChangePlayer(unsigned player, bool updateVisualData /* = true*/)
47✔
247
{
248
    if(player == playerId_)
47✔
249
        return;
14✔
250
    playerId_ = player;
33✔
251
    if(updateVisualData)
33✔
252
    {
253
        RecalcAllColors();
×
254
        InitVisualData();
×
255
    }
256
}
257

258
helpers::EnumArray<MapPoint, Direction> GameWorldViewer::GetNeighbours(const MapPoint pt) const
×
259
{
260
    return GetWorld().GetNeighbours(pt);
×
261
}
262

263
void GameWorldViewer::VisibilityChanged(const MapPoint& pt, unsigned player)
×
264
{
265
    // If visibility changed for us, or our team mate if shared view is on -> Update renderer
266
    if(player == playerId_ || (GetWorld().GetGGS().teamView && GetWorld().GetPlayer(playerId_).IsAlly(player)))
×
267
        tr.VisibilityChanged(pt, *this);
×
268
}
×
269

270
void GameWorldViewer::RoadConstructionEnded(const RoadNote& note)
18✔
271
{
272
    if(note.player != playerId_
18✔
273
       || (note.type != RoadNote::Constructed && note.type != RoadNote::ConstructionFailed)) //-V560
17✔
274
        return;
1✔
275
    // Road construction command ended -> Remove visual overlay
276
    RemoveVisualRoad(note.pos, note.route);
17✔
277
}
278

279
void GameWorldViewer::RecalcBQ(const MapPoint& pt)
3,532✔
280
{
281
    BQCalculator calcBQ(GetWorld());
3,532✔
282
    visualNodes[pt].bq = calcBQ(pt, [this](const MapPoint& pos) { return IsOnRoad(pos); });
4,947✔
283
}
3,532✔
284

285
void GameWorldViewer::RecalcBQForRoad(const MapPoint& pt)
155✔
286
{
287
    RecalcBQ(pt);
155✔
288

289
    for(const Direction dir : {Direction::East, Direction::SouthEast, Direction::SouthWest})
620✔
290
        RecalcBQ(GetNeighbour(pt, dir));
465✔
291
}
155✔
292

293
void GameWorldViewer::RemoveVisualRoad(const MapPoint& start, const std::vector<Direction>& route)
19✔
294
{
295
    MapPoint curPt = start;
19✔
296
    for(auto i : route)
128✔
297
    {
298
        SetVisiblePointRoad(curPt, i, PointRoad::None);
109✔
299
        RecalcBQForRoad(curPt);
109✔
300
        curPt = GetWorld().GetNeighbour(curPt, i);
109✔
301
    }
302
    RecalcBQForRoad(curPt);
19✔
303
}
19✔
304

305
bool GameWorldViewer::IsRoadAvailable(bool isWaterRoad, const MapPoint& pt) const
79✔
306
{
307
    return !IsOnRoad(pt) && GetWorld().IsRoadAvailable(isWaterRoad, pt);
79✔
308
}
309

310
/// Get the "youngest" FOWObject of all players who share the view with the local player
311
const FOWObject* GameWorldViewer::GetYoungestFOWObject(const MapPoint pos) const
×
312
{
313
    return GetYoungestFOWNode(pos).object.get();
×
314
}
315

316
/// Gets the youngest fow node of all visible objects of all players who are connected
317
/// with the local player via team view
318
const FoWNode& GameWorldViewer::GetYoungestFOWNode(const MapPoint pos) const
×
319
{
320
    const MapNode& node = GetWorld().GetNode(pos);
×
321
    const FoWNode* bestNode = &node.fow[playerId_];
×
322
    unsigned youngest_time = bestNode->last_update_time;
×
323

324
    // Shared team view enabled?
325
    if(GetWorld().GetGGS().teamView)
×
326
    {
327
        const GamePlayer& player = GetWorld().GetPlayer(playerId_);
×
328
        // Then check if team members have a better (="younger", see our economy) fow object
329
        for(unsigned i = 0; i < GetWorld().GetNumPlayers(); ++i)
×
330
        {
331
            if(!player.IsAlly(i))
×
332
                continue;
×
333
            // Has the player FOW at this point at all?
334
            const FoWNode* curNode = &node.fow[i];
×
335
            if(curNode->visibility == Visibility::FogOfWar)
×
336
            {
337
                // Younger than the youngest or no object at all?
338
                if(curNode->last_update_time > youngest_time)
×
339
                {
340
                    // Then take it
341
                    youngest_time = curNode->last_update_time;
×
342
                    // And remember its owner
343
                    bestNode = curNode;
×
344
                }
345
            }
346
        }
347
    }
348

349
    return *bestNode;
×
350
}
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