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

Return-To-The-Roots / s25client / 13471749703

22 Feb 2025 09:40AM UTC coverage: 50.279% (-0.05%) from 50.326%
13471749703

Pull #1737

github

web-flow
Merge d4bfe402b into 541bc230f
Pull Request #1737: Fix testCheats when running standalone

22347 of 44446 relevant lines covered (50.28%)

34667.71 hits per line

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

67.82
/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)
21✔
23
{
24
    InitVisualData();
21✔
25
    maxNodeAltitude_ = 0;
21✔
26
    RTTR_FOREACH_PT(MapPoint, gwb.GetSize())
48,517✔
27
    {
28
        maxNodeAltitude_ = std::max(maxNodeAltitude_, gwb.GetNode(pt).altitude);
47,656✔
29
    }
30
    evAltitudeChanged = gwb.GetNotifications().subscribe<NodeNote>([this](const NodeNote& note) {
42✔
31
        if(note.type == NodeNote::Altitude)
6,756✔
32
        {
33
            maxNodeAltitude_ = std::max(maxNodeAltitude_, this->gwb.GetNode(note.pos).altitude);
1✔
34
        }
35
    });
21✔
36
}
21✔
37

38
void GameWorldViewer::InitVisualData()
21✔
39
{
40
    visualNodes.Resize(gwb.GetSize());
21✔
41
    RTTR_FOREACH_PT(MapPoint, gwb.GetSize())
48,517✔
42
    {
43
        VisualMapNode& vNode = visualNodes[pt];
47,656✔
44
        const MapNode& node = gwb.GetNode(pt);
47,656✔
45
        vNode.bq = node.bq;
47,656✔
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,656✔
48
    }
49
    evRoadConstruction =
50
      gwb.GetNotifications().subscribe<RoadNote>([this](const RoadNote& note) { RoadConstructionEnded(note); });
39✔
51
    evBQChanged = gwb.GetNotifications().subscribe<NodeNote>([this](const NodeNote& note) {
42✔
52
        if(note.type == NodeNote::BQ)
6,756✔
53
            RecalcBQ(note.pos);
2,912✔
54
    });
21✔
55
}
21✔
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,553✔
81
{
82
    return GetWorld().GetPlayer(playerId_);
1,553✔
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
1,541✔
117
{
118
    const auto* const gi = GetWorld().GetGameInterface();
1,541✔
119
    if(gi && gi->GI_GetCheats().isAllVisible())
1,541✔
120
        return Visibility::Visible;
2✔
121

122
    /// Replaymodus und FoW aus? Dann alles sichtbar
123
    if(GAMECLIENT.IsReplayModeOn() && GAMECLIENT.IsReplayFOWDisabled())
1,539✔
124
        return Visibility::Visible;
×
125

126
    // Spieler schon tot? Dann auch alles sichtbar?
127
    if(GetPlayer().IsDefeated())
1,539✔
128
        return Visibility::Visible;
×
129

130
    return GetWorld().CalcVisiblityWithAllies(pt, playerId_);
1,539✔
131
}
132

133
bool GameWorldViewer::IsOwner(const MapPoint& pt) const
1✔
134
{
135
    return GetWorld().GetNode(pt).owner == playerId_ + 1;
1✔
136
}
137

138
bool GameWorldViewer::IsPlayerTerritory(const MapPoint& pt) const
79✔
139
{
140
    return GetWorld().IsPlayerTerritory(pt, playerId_ + 1);
79✔
141
}
142

143
const MapNode& GameWorldViewer::GetNode(const MapPoint& pt) const
4,582✔
144
{
145
    return GetWorld().GetNode(pt);
4,582✔
146
}
147

148
MapPoint GameWorldViewer::GetNeighbour(const MapPoint pt, const Direction dir) const
3,343✔
149
{
150
    return GetWorld().GetNeighbour(pt, dir);
3,343✔
151
}
152

153
void GameWorldViewer::RecalcAllColors()
×
154
{
155
    tr.UpdateAllColors(*this);
×
156
}
×
157

158
/// liefert sichtbare Straße, im FoW entsprechend die FoW-Straße
159
PointRoad GameWorldViewer::GetVisibleRoad(const MapPoint pt, RoadDir roadDir, const Visibility visibility) const
4,572✔
160
{
161
    if(visibility == Visibility::Visible)
4,572✔
162
        return GetVisibleRoad(pt, roadDir);
4,572✔
163
    else if(visibility == Visibility::FogOfWar)
×
164
        return GetYoungestFOWNode(pt).roads[roadDir];
×
165
    else
166
        return PointRoad::None;
×
167
}
168

169
PointRoad GameWorldViewer::GetVisibleRoad(const MapPoint pt, RoadDir roadDir) const
11,038✔
170
{
171
    const PointRoad visualResult = visualNodes[pt].roads[roadDir];
11,038✔
172
    if(visualResult != PointRoad::None)
11,038✔
173
        return visualResult;
150✔
174
    else
175
        return GetWorld().GetRoad(pt, roadDir);
10,888✔
176
}
177

178
PointRoad GameWorldViewer::GetVisiblePointRoad(MapPoint pt, Direction dir) const
6✔
179
{
180
    const RoadDir rDir = GetWorld().toRoadDir(pt, dir);
6✔
181
    return GetVisibleRoad(pt, rDir);
6✔
182
}
183

184
void GameWorldViewer::SetVisiblePointRoad(MapPoint pt, Direction dir, PointRoad type)
133✔
185
{
186
    const RoadDir rDir = GetWorld().toRoadDir(pt, dir);
133✔
187
    visualNodes[pt].roads[rDir] = type;
133✔
188
}
133✔
189

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

205
/// Return a ship at this position owned by the given player. Prefers ships that need instructions.
206
const noShip* GameWorldViewer::GetShip(const MapPoint pt) const
1✔
207
{
208
    const noShip* resultShip = nullptr;
1✔
209

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

231
    return resultShip;
1✔
232
}
233

234
/// Gibt die verfügbar Anzahl der Angreifer für einen Seeangriff zurück
235
unsigned GameWorldViewer::GetNumSoldiersForSeaAttack(const MapPoint pt) const
34✔
236
{
237
    return unsigned(GetWorld().GetSoldiersForSeaAttack(playerId_, pt).size());
34✔
238
}
239

240
void GameWorldViewer::ChangePlayer(unsigned player, bool updateVisualData /* = true*/)
47✔
241
{
242
    if(player == playerId_)
47✔
243
        return;
14✔
244
    playerId_ = player;
33✔
245
    if(updateVisualData)
33✔
246
    {
247
        RecalcAllColors();
×
248
        InitVisualData();
×
249
    }
250
}
251

252
helpers::EnumArray<MapPoint, Direction> GameWorldViewer::GetNeighbours(const MapPoint pt) const
×
253
{
254
    return GetWorld().GetNeighbours(pt);
×
255
}
256

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

264
void GameWorldViewer::RoadConstructionEnded(const RoadNote& note)
18✔
265
{
266
    if(note.player != playerId_
18✔
267
       || (note.type != RoadNote::Constructed && note.type != RoadNote::ConstructionFailed)) //-V560
17✔
268
        return;
1✔
269
    // Road construction command ended -> Remove visual overlay
270
    RemoveVisualRoad(note.pos, note.route);
17✔
271
}
272

273
void GameWorldViewer::RecalcBQ(const MapPoint& pt)
3,532✔
274
{
275
    BQCalculator calcBQ(GetWorld());
3,532✔
276
    visualNodes[pt].bq = calcBQ(pt, [this](const MapPoint& pos) { return IsOnRoad(pos); });
4,947✔
277
}
3,532✔
278

279
void GameWorldViewer::RecalcBQForRoad(const MapPoint& pt)
155✔
280
{
281
    RecalcBQ(pt);
155✔
282

283
    for(const Direction dir : {Direction::East, Direction::SouthEast, Direction::SouthWest})
620✔
284
        RecalcBQ(GetNeighbour(pt, dir));
465✔
285
}
155✔
286

287
void GameWorldViewer::RemoveVisualRoad(const MapPoint& start, const std::vector<Direction>& route)
19✔
288
{
289
    MapPoint curPt = start;
19✔
290
    for(auto i : route)
128✔
291
    {
292
        SetVisiblePointRoad(curPt, i, PointRoad::None);
109✔
293
        RecalcBQForRoad(curPt);
109✔
294
        curPt = GetWorld().GetNeighbour(curPt, i);
109✔
295
    }
296
    RecalcBQForRoad(curPt);
19✔
297
}
19✔
298

299
bool GameWorldViewer::IsRoadAvailable(bool isWaterRoad, const MapPoint& pt) const
79✔
300
{
301
    return !IsOnRoad(pt) && GetWorld().IsRoadAvailable(isWaterRoad, pt);
79✔
302
}
303

304
/// Get the "youngest" FOWObject of all players who share the view with the local player
305
const FOWObject* GameWorldViewer::GetYoungestFOWObject(const MapPoint pos) const
×
306
{
307
    return GetYoungestFOWNode(pos).object.get();
×
308
}
309

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

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

343
    return *bestNode;
×
344
}
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