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

Return-To-The-Roots / s25client / 21567098038

01 Feb 2026 05:25PM UTC coverage: 50.754% (+0.09%) from 50.661%
21567098038

push

github

web-flow
Merge pull request #1679 from kubaau/cheats2

94 of 174 new or added lines in 12 files covered. (54.02%)

10 existing lines in 5 files now uncovered.

22798 of 44919 relevant lines covered (50.75%)

41544.68 hits per line

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

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

5
#include "world/GameWorldView.h"
6
#include "CatapultStone.h"
7
#include "Cheats.h"
8
#include "FOWObjects.h"
9
#include "GameInterface.h"
10
#include "GamePlayer.h"
11
#include "GlobalGameSettings.h"
12
#include "Loader.h"
13
#include "MapGeometry.h"
14
#include "Settings.h"
15
#include "addons/AddonMaxWaterwayLength.h"
16
#include "buildings/noBuildingSite.h"
17
#include "buildings/nobMilitary.h"
18
#include "buildings/nobUsual.h"
19
#include "drivers/VideoDriverWrapper.h"
20
#include "helpers/EnumArray.h"
21
#include "helpers/Range.h"
22
#include "helpers/containerUtils.h"
23
#include "helpers/toString.h"
24
#include "ogl/FontStyle.h"
25
#include "ogl/glArchivItem_Bitmap.h"
26
#include "ogl/glFont.h"
27
#include "ogl/glSmartBitmap.h"
28
#include "world/GameWorldBase.h"
29
#include "world/GameWorldViewer.h"
30
#include "gameTypes/RoadBuildState.h"
31
#include "gameData/BuildingConsts.h"
32
#include "gameData/GuiConsts.h"
33
#include "gameData/MapConsts.h"
34
#include "s25util/error.h"
35
#include "s25util/warningSuppression.h"
36
#include <glad/glad.h>
37
#include <boost/format.hpp>
38
#include <cmath>
39

40
GameWorldView::GameWorldView(const GameWorldViewer& gwv, const Position& pos, const Extent& size)
10✔
41
    : selPt(0, 0), show_bq(SETTINGS.ingame.showBQ), show_names(SETTINGS.ingame.showNames),
10✔
42
      show_productivity(SETTINGS.ingame.showProductivity), offset(0, 0), lastOffset(0, 0), gwv(gwv), origin_(pos),
20✔
43
      size_(size), zoomFactor_(1.f), targetZoomFactor_(1.f), zoomSpeed_(0.f)
20✔
44
{
45
    updateEffectiveZoomFactor();
10✔
46
    MoveBy({0, 0});
10✔
47
}
10✔
48

49
const GameWorldBase& GameWorldView::GetWorld() const
82✔
50
{
51
    return gwv.GetWorld();
82✔
52
}
53

54
SoundManager& GameWorldView::GetSoundMgr()
×
55
{
56
    return const_cast<GameWorldViewer&>(gwv).GetSoundMgr();
×
57
}
58

59
void GameWorldView::SetNextZoomFactor()
×
60
{
61
    if(zoomFactor_ == targetZoomFactor_) // == with float is ok here, is explicitly set in last step //-V550
×
62
        return;
×
63

64
    float remainingZoomDiff = targetZoomFactor_ - zoomFactor_;
×
65

66
    if(std::abs(remainingZoomDiff) <= 0.5 * zoomSpeed_ * zoomSpeed_ / ZOOM_ACCELERATION)
×
67
    {
68
        // deceleration towards zero zoom speed
69
        if(zoomSpeed_ > 0)
×
70
            zoomSpeed_ -= ZOOM_ACCELERATION;
×
71
        else
72
            zoomSpeed_ += ZOOM_ACCELERATION;
×
73
    } else
74
    {
75
        // acceleration to unlimited speed
76
        if(remainingZoomDiff > 0)
×
77
            zoomSpeed_ += ZOOM_ACCELERATION;
×
78
        else
79
            zoomSpeed_ -= ZOOM_ACCELERATION;
×
80
    }
81

82
    if(std::abs(remainingZoomDiff) < std::abs(zoomSpeed_))
×
83
    {
84
        // last step
85
        zoomFactor_ = targetZoomFactor_;
×
86
        zoomSpeed_ = 0;
×
87
    }
88

89
    zoomFactor_ = zoomFactor_ + zoomSpeed_;
×
90
    updateEffectiveZoomFactor();
×
91
    CalcFxLx();
×
92
}
93

94
float GameWorldView::SetZoomFactor(float zoomFactor, bool smoothTransition /* = true*/)
36✔
95
{
96
    targetZoomFactor_ = zoomFactor;
36✔
97
    if(!smoothTransition)
36✔
98
    {
99
        zoomFactor_ = targetZoomFactor_;
×
100
        updateEffectiveZoomFactor();
×
101
        CalcFxLx();
×
102
    }
103
    return targetZoomFactor_;
36✔
104
}
105

106
float GameWorldView::GetCurrentTargetZoomFactor() const
52✔
107
{
108
    return targetZoomFactor_;
52✔
109
}
110

111
Position GameWorldView::ViewPosToMap(Position pos) const
2✔
112
{
113
    pos -= origin_;
2✔
114
    if(effectiveZoomFactor_ != 1.f)
2✔
115
    {
116
        PointF diff(size_.x - size_.x / effectiveZoomFactor_, size_.y - size_.y / effectiveZoomFactor_);
×
117
        diff /= 2.f;
×
118
        pos = Position(PointF(pos) / effectiveZoomFactor_ + diff);
×
119
    }
120

121
    return pos;
2✔
122
}
123

124
struct ObjectBetweenLines
125
{
126
    noBase& obj;
127
    DrawPoint pos;
UNCOV
128
    ObjectBetweenLines(noBase& obj, const DrawPoint& pos) : obj(obj), pos(pos) {}
×
129
};
130

131
void GameWorldView::Draw(const RoadBuildState& rb, const MapPoint selected, bool drawMouse, unsigned* water)
×
132
{
133
    SetNextZoomFactor();
×
134

135
    const auto windowSize = VIDEODRIVER.GetWindowSize();
×
136
    const auto& guiScale = VIDEODRIVER.getGuiScale();
×
137
    const auto screenOrigin = guiScale.viewToScreen(origin_);
×
138
    const auto screenSize = guiScale.viewToScreen(size_);
×
139
    glScissor(screenOrigin.x, windowSize.height - (screenOrigin.y + screenSize.y), screenSize.x, screenSize.y);
×
140

141
    int shortestDistToMouse = 100000;
×
142
    Position mousePos = VIDEODRIVER.GetMousePos();
×
143
    mousePos -= Position(origin_);
×
144
    if(effectiveZoomFactor_ != 1.f) //-V550
×
145
    {
146
        glMatrixMode(GL_PROJECTION);
×
147
        glPushMatrix();
×
148
        glScalef(effectiveZoomFactor_, effectiveZoomFactor_, 1);
×
149
        // Offset to center view
150
        PointF diff(size_.x - size_.x / effectiveZoomFactor_, size_.y - size_.y / effectiveZoomFactor_);
×
151
        diff = diff / 2.f;
×
152
        glTranslatef(-diff.x, -diff.y, 0.f);
×
153
        // Also adjust mouse
154
        mousePos = Position(PointF(mousePos) / effectiveZoomFactor_ + diff);
×
155
        glMatrixMode(GL_MODELVIEW);
×
156
    }
157

NEW
158
    glPushMatrix();
×
159
    glTranslatef(static_cast<GLfloat>(origin_.x) / effectiveZoomFactor_,
×
160
                 static_cast<GLfloat>(origin_.y) / effectiveZoomFactor_, 0.0f);
×
161

162
    glTranslatef(static_cast<GLfloat>(-offset.x), static_cast<GLfloat>(-offset.y), 0.0f);
×
163
    const TerrainRenderer& terrainRenderer = gwv.GetTerrainRenderer();
×
164
    terrainRenderer.Draw(GetFirstPt(), GetLastPt(), gwv, water);
×
165
    glTranslatef(static_cast<GLfloat>(offset.x), static_cast<GLfloat>(offset.y), 0.0f);
×
166

NEW
167
    const auto& world = GetWorld();
×
NEW
168
    const auto resourceRevealMode = world.GetGameInterface()->GI_GetCheats().getResourceRevealMode();
×
169

170
    // Save figures that are between rows (due to moving) to draw later
NEW
171
    std::vector<ObjectBetweenLines> objsBetweenRows;
×
NEW
172
    for(const int y : helpers::range(firstPt.y, lastPt.y + 1))
×
173
    {
NEW
174
        objsBetweenRows.clear();
×
NEW
175
        for(const int x : helpers::range(firstPt.x, lastPt.x + 1))
×
176
        {
177
            Position curOffset;
×
178
            const MapPoint curPt = terrainRenderer.ConvertCoords(Position(x, y), &curOffset);
×
NEW
179
            const DrawPoint curPos = world.GetNodePos(curPt) - offset + curOffset;
×
180

181
            Position mouseDist = mousePos - curPos;
×
182
            mouseDist *= mouseDist;
×
183
            if(std::abs(mouseDist.x) + std::abs(mouseDist.y) < shortestDistToMouse)
×
184
            {
185
                selPt = curPt;
×
186
                selPtOffset = curOffset;
×
187
                shortestDistToMouse = std::abs(mouseDist.x) + std::abs(mouseDist.y);
×
188
            }
189

NEW
190
            const Visibility visibility = gwv.GetVisibility(curPt);
×
191

192
            DrawBoundaryStone(curPt, curPos, visibility);
×
193

194
            if(visibility == Visibility::Visible)
×
195
            {
196
                DrawObject(curPt, curPos);
×
NEW
197
                DrawMovingFiguresFromBelow(terrainRenderer, Position(x, y), objsBetweenRows);
×
NEW
198
                DrawFigures(curPt, curPos, objsBetweenRows);
×
199

UNCOV
200
                if(show_bq)
×
201
                    DrawConstructionAid(curPt, curPos);
×
NEW
202
                if(resourceRevealMode != Cheats::ResourceRevealMode::Nothing)
×
NEW
203
                    DrawResource(curPt, curPos, resourceRevealMode);
×
UNCOV
204
            } else if(visibility == Visibility::FogOfWar)
×
205
            {
206
                const FOWObject* fowobj = gwv.GetYoungestFOWObject(MapPoint(curPt));
×
207
                if(fowobj)
×
208
                    fowobj->Draw(curPos);
×
209
            }
210

211
            for(IDrawNodeCallback* callback : drawNodeCallbacks)
×
212
                callback->onDraw(curPt, curPos);
×
213
        }
214

215
        // Draw objects that are between rows now
NEW
216
        for(auto& between_line : objsBetweenRows)
×
UNCOV
217
            between_line.obj.Draw(between_line.pos);
×
218
    }
219

220
    if(show_names || show_productivity)
×
221
        DrawNameProductivityOverlay(terrainRenderer);
×
222

223
    DrawGUI(rb, terrainRenderer, selected, drawMouse);
×
224

225
    // Draw catapult stones
NEW
226
    for(auto* catapult_stone : world.catapult_stones)
×
227
    {
228
        if(gwv.GetVisibility(catapult_stone->dest_building) == Visibility::Visible
×
229
           || gwv.GetVisibility(catapult_stone->dest_map) == Visibility::Visible)
×
230
            catapult_stone->Draw(offset);
×
231
    }
232

233
    if(effectiveZoomFactor_ != 1.f) //-V550
×
234
    {
235
        glMatrixMode(GL_PROJECTION);
×
236
        glPopMatrix();
×
237
        glMatrixMode(GL_MODELVIEW);
×
238
    }
NEW
239
    glPopMatrix();
×
240

241
    glScissor(0, 0, windowSize.width, windowSize.height);
×
242
}
×
243

244
void GameWorldView::DrawGUI(const RoadBuildState& rb, const TerrainRenderer& terrainRenderer,
×
245
                            const MapPoint& selectedPt, bool drawMouse)
246
{
247
    // Falls im Straßenbaumodus: Punkte um den aktuellen Straßenbaupunkt herum ermitteln
248
    helpers::EnumArray<MapPoint, Direction> road_points;
×
249

250
    unsigned maxWaterWayLen = 0;
×
251
    if(rb.mode != RoadBuildMode::Disabled)
×
252
    {
253
        for(const auto dir : helpers::EnumRange<Direction>{})
×
254
            road_points[dir] = GetWorld().GetNeighbour(rb.point, dir);
×
255

256
        const unsigned index = GetWorld().GetGGS().getSelection(AddonId::MAX_WATERWAY_LENGTH);
×
257
        RTTR_Assert(index < waterwayLengths.size());
×
258
        maxWaterWayLen = waterwayLengths[index];
×
259
    }
260

261
    for(int x = firstPt.x; x <= lastPt.x; ++x)
×
262
    {
263
        for(int y = firstPt.y; y <= lastPt.y; ++y)
×
264
        {
265
            // Coordinates transform
266
            Position curOffset;
×
267
            MapPoint curPt = terrainRenderer.ConvertCoords(Position(x, y), &curOffset);
×
268
            Position curPos = GetWorld().GetNodePos(curPt) - offset + curOffset;
×
269

270
            /// Current point indicated by Mouse
271
            if(drawMouse && selPt == curPt)
×
272
            {
273
                // Mauszeiger am boden
274
                unsigned mid = 22;
×
275
                if(rb.mode == RoadBuildMode::Disabled)
×
276
                {
277
                    switch(gwv.GetBQ(curPt))
×
278
                    {
279
                        case BuildingQuality::Flag: mid = 40; break;
×
280
                        case BuildingQuality::Mine: mid = 41; break;
×
281
                        case BuildingQuality::Hut: mid = 42; break;
×
282
                        case BuildingQuality::House: mid = 43; break;
×
283
                        case BuildingQuality::Castle: mid = 44; break;
×
284
                        case BuildingQuality::Harbor: mid = 45; break;
×
285
                        default: break;
×
286
                    }
287
                }
288
                LOADER.GetMapTexture(mid)->DrawFull(curPos);
×
289
            }
290

291
            // Currently selected point
292
            if(selectedPt == curPt)
×
293
                LOADER.GetMapTexture(20)->DrawFull(curPos);
×
294

295
            // not building roads, no further action needed
296
            if(rb.mode == RoadBuildMode::Disabled)
×
297
                continue;
×
298

299
            // we dont own curPt, no need for any rendering...
300
            if(!gwv.IsPlayerTerritory(curPt))
×
301
                continue;
×
302

303
            // we are in road build mode
304
            // highlight current route pt
305
            if(rb.point == curPt)
×
306
            {
307
                LOADER.GetMapTexture(21)->DrawFull(curPos);
×
308
                continue;
×
309
            }
310

311
            // ensure that curPt is a neighbour of rb.point
312
            if(!helpers::contains(road_points, curPt))
×
313
            {
314
                continue;
×
315
            }
316

317
            // test on maximal water way length
318
            if(rb.mode == RoadBuildMode::Boat && maxWaterWayLen != 0 && rb.route.size() >= maxWaterWayLen)
×
319
                continue;
×
320

321
            // render special icon for route revert
322
            if(!rb.route.empty() && road_points[rb.route.back() + 3u] == curPt)
×
323
            {
324
                LOADER.GetMapTexture(67)->DrawFull(curPos);
×
325
                continue;
×
326
            }
327

328
            // is a flag but not the route start flag, as it would revert the route.
329
            const bool targetFlag = GetWorld().GetNO(curPt)->GetType() == NodalObjectType::Flag && curPt != rb.start;
×
330
            int altitude = GetWorld().GetNode(rb.point).altitude;
×
331
            if(targetFlag || gwv.IsRoadAvailable(rb.mode == RoadBuildMode::Boat, curPt))
×
332
            {
333
                unsigned id;
334
                switch(int(GetWorld().GetNode(curPt).altitude) - altitude)
×
335
                {
336
                    case 1: id = 61; break;
×
337
                    case 2:
×
338
                    case 3: id = 62; break;
×
339
                    case 4:
×
340
                    case 5: id = 63; break;
×
341
                    case -1: id = 64; break;
×
342
                    case -2:
×
343
                    case -3: id = 65; break;
×
344
                    case -4:
×
345
                    case -5: id = 66; break;
×
346
                    default: id = 60; break;
×
347
                }
348
                if(!targetFlag)
×
349
                    LOADER.GetMapTexture(id)->DrawFull(curPos);
×
350
                else
351
                {
352
                    DrawPoint lastPos = GetWorld().GetNodePos(rb.point) - offset + curOffset;
×
353
                    DrawPoint halfWayPos = (curPos + lastPos) / 2;
×
354
                    LOADER.GetMapTexture(id)->DrawFull(halfWayPos);
×
355
                    LOADER.GetMapTexture(20)->DrawFull(curPos);
×
356
                }
357
            }
358
        }
359
    }
360
}
×
361

362
void GameWorldView::DrawNameProductivityOverlay(const TerrainRenderer& terrainRenderer)
×
363
{
364
    const bool showEnemyProductivity =
365
      GetWorld().GetGameInterface()->GI_GetCheats().shouldShowEnemyProductivityOverlay();
×
366
    auto* attackAidImage =
367
      (GetWorld().GetGGS().getSelection(AddonId::MILITARY_AID) == 2) ? LOADER.GetImageN("map_new", 20000) : nullptr;
×
368
    const bool isAllVisible = gwv.IsAllVisible();
×
369
    for(int x = firstPt.x; x <= lastPt.x; ++x)
×
370
    {
371
        for(int y = firstPt.y; y <= lastPt.y; ++y)
×
372
        {
373
            // Coordinate transform
374
            Position curOffset;
×
375
            MapPoint pt = terrainRenderer.ConvertCoords(Position(x, y), &curOffset);
×
376

377
            const auto* no = GetWorld().GetSpecObj<noBaseBuilding>(pt);
×
378
            if(!no)
×
379
                continue;
×
380

381
            Position curPos = GetWorld().GetNodePos(pt) - offset + curOffset;
×
382
            curPos.y -= 22;
×
383

384
            // Is object not belonging to local player?
385
            if(no->GetPlayer() != gwv.GetPlayerId())
×
386
            {
387
                if(!isAllVisible && gwv.GetVisibility(pt, false) != Visibility::Visible)
×
388
                    continue;
×
389
                if(attackAidImage && gwv.GetNumSoldiersForAttack(pt) > 0)
×
390
                    attackAidImage->DrawFull(curPos - DrawPoint(0, attackAidImage->getHeight()));
×
391
                // Do not draw enemy productivity overlay unless the related cheat is on
392
                if(!showEnemyProductivity)
×
393
                    continue;
×
394
            }
395

396
            // Draw object name
397
            if(show_names)
×
398
            {
399
                unsigned color = (no->GetGOT() == GO_Type::Buildingsite) ? COLOR_GREY : COLOR_YELLOW;
×
400
                SmallFont->Draw(curPos, _(BUILDING_NAMES[no->GetBuildingType()]),
×
401
                                FontStyle::CENTER | FontStyle::VCENTER, color);
402
                curPos.y += SmallFont->getHeight();
×
403
            }
404

405
            // Draw productivity/soldiers
406
            if(show_productivity)
×
407
                DrawProductivity(*no, curPos);
×
408
        }
409
    }
410
}
×
411

412
void GameWorldView::DrawProductivity(const noBaseBuilding& no, const DrawPoint& curPos)
×
413
{
414
    const GO_Type got = no.GetGOT();
×
415
    if(got == GO_Type::Buildingsite)
×
416
    {
417
        unsigned color = COLOR_GREY;
×
418

419
        unsigned short p = static_cast<const noBuildingSite&>(no).GetBuildProgress();
×
420
        SmallFont->Draw(curPos, (boost::format("(%1% %%)") % p).str(), FontStyle::CENTER | FontStyle::VCENTER, color);
×
421
    } else if(got == GO_Type::NobUsual || got == GO_Type::NobShipyard || got == GO_Type::NobTemple)
×
422
    {
423
        const auto& n = static_cast<const nobUsual&>(no);
×
424
        std::string text;
×
425
        unsigned color = COLOR_0_PERCENT;
×
426

427
        if(!n.HasWorker())
×
428
            text = _("(House unoccupied)");
×
429
        else if(n.IsProductionDisabledVirtual())
×
430
            text = _("(stopped)");
×
431
        else
432
        {
433
            // Catapult and Lookout tower doesn't have productivity!
434
            if(n.GetBuildingType() == BuildingType::Catapult || n.GetBuildingType() == BuildingType::LookoutTower)
×
435
                return;
×
436

437
            unsigned short p = n.GetProductivity();
×
438
            text = helpers::toString(p) + " %";
×
439
            if(p >= 60)
×
440
                color = COLOR_60_PERCENT;
×
441
            else if(p >= 30)
×
442
                color = COLOR_30_PERCENT;
×
443
            else if(p >= 20)
×
444
                color = COLOR_20_PERCENT;
×
445
        }
446
        SmallFont->Draw(curPos, text, FontStyle::CENTER | FontStyle::VCENTER, color);
×
447
    } else if(got == GO_Type::NobMilitary)
×
448
    {
449
        // Display amount of soldiers
450
        unsigned soldiers_count = static_cast<const nobMilitary&>(no).GetNumTroops();
×
451
        std::string sSoldiers;
×
452
        if(soldiers_count == 1)
×
453
            sSoldiers = _("(1 soldier)");
×
454
        else
455
            sSoldiers = boost::str(boost::format(_("(%d soldiers)")) % soldiers_count);
×
456

457
        SmallFont->Draw(curPos, sSoldiers, FontStyle::CENTER | FontStyle::VCENTER,
×
458
                        (soldiers_count > 0) ? COLOR_YELLOW : COLOR_RED);
×
459
    }
460
}
461

462
void GameWorldView::DrawFigures(const MapPoint& pt, const DrawPoint& curPos,
×
463
                                std::vector<ObjectBetweenLines>& objsBetweenRows) const
464
{
465
    for(noBase& figure : GetWorld().GetFigures(pt))
×
466
    {
467
        if(figure.IsMoving())
×
468
        {
469
            // Drawn from above
470
            Direction curMoveDir = static_cast<noMovable&>(figure).GetCurMoveDir();
×
471
            if(curMoveDir == Direction::NorthEast || curMoveDir == Direction::NorthWest)
×
472
                continue;
×
473
            // Draw later
NEW
474
            objsBetweenRows.push_back(ObjectBetweenLines(figure, curPos));
×
475
        } else if(figure.GetGOT() == GO_Type::Ship)
×
NEW
476
            objsBetweenRows.push_back(ObjectBetweenLines(figure, curPos)); // TODO: Why special handling for ships?
×
477
        else
NEW
478
            figure.Draw(curPos); // Draw normally
×
479
    }
480
}
×
481

482
void GameWorldView::DrawMovingFiguresFromBelow(const TerrainRenderer& terrainRenderer, const DrawPoint& curPos,
×
483
                                               std::vector<ObjectBetweenLines>& objsBetweenRows)
484
{
485
    // First draw figures moving towards this point from below
NEW
486
    for(Direction dir : {Direction::NorthEast, Direction::NorthWest})
×
487
    {
488
        // Get figures opposite the current dir and check if they are moving in this dir
489
        // Coordinates transform
490
        Position curOffset;
×
491
        MapPoint curPt = terrainRenderer.ConvertCoords(GetNeighbour(curPos, dir + 3u), &curOffset);
×
492
        Position figPos = GetWorld().GetNodePos(curPt) - offset + curOffset;
×
493

494
        for(noBase& figure : GetWorld().GetFigures(curPt))
×
495
        {
496
            if(figure.IsMoving() && static_cast<noMovable&>(figure).GetCurMoveDir() == dir)
×
NEW
497
                objsBetweenRows.push_back(ObjectBetweenLines(figure, figPos));
×
498
        }
499
    }
500
}
×
501

502
constexpr auto getBqImgs()
503
{
504
    helpers::EnumArray<unsigned, BuildingQuality> imgs{};
505
    imgs[BuildingQuality::Flag] = 50;
506
    imgs[BuildingQuality::Hut] = 51;
507
    imgs[BuildingQuality::House] = 52;
508
    imgs[BuildingQuality::Castle] = 53;
509
    imgs[BuildingQuality::Mine] = 54;
510
    imgs[BuildingQuality::Harbor] = 55;
511
    return imgs;
512
}
513

514
void GameWorldView::DrawConstructionAid(const MapPoint& pt, const DrawPoint& curPos)
×
515
{
516
    BuildingQuality bq = gwv.GetBQ(pt);
×
517
    if(bq != BuildingQuality::Nothing)
×
518
    {
519
        constexpr auto bqImgs = getBqImgs();
×
520
        auto* bm = LOADER.GetMapTexture(bqImgs[bq]);
×
521
        // Draw building quality icon
522
        bm->DrawFull(curPos);
×
523
        // Show ability to construct military buildings
524
        if(GetWorld().GetGGS().isEnabled(AddonId::MILITARY_AID))
×
525
        {
526
            if(!GetWorld().IsMilitaryBuildingNearNode(pt, gwv.GetPlayerId())
×
527
               && (bq == BuildingQuality::Hut || bq == BuildingQuality::House || bq == BuildingQuality::Castle
×
528
                   || bq == BuildingQuality::Harbor))
×
529
                LOADER.GetImageN("map_new", 20000)->DrawFull(curPos - DrawPoint(-1, bm->GetSize().y + 5));
×
530
        }
531
    }
532
}
×
533

534
void GameWorldView::DrawObject(const MapPoint& pt, const DrawPoint& curPos) const
×
535
{
536
    noBase* obj = GetWorld().GetNode(pt).obj;
×
537
    if(!obj)
×
538
        return;
×
539

540
    obj->Draw(curPos);
×
541
}
542

543
void GameWorldView::DrawBoundaryStone(const MapPoint& pt, const DrawPoint pos, Visibility vis)
×
544
{
545
    if(vis == Visibility::Invisible)
×
546
        return;
×
547

548
    const bool isFoW = vis == Visibility::FogOfWar;
×
549

550
    const BoundaryStones& boundary_stones =
551
      isFoW ? gwv.GetYoungestFOWNode(pt).boundary_stones : GetWorld().GetNode(pt).boundary_stones;
×
552
    const unsigned char owner = boundary_stones[BorderStonePos::OnPoint];
×
553

554
    if(!owner)
×
555
        return;
×
556

557
    const Nation nation = GetWorld().GetPlayer(owner - 1).nation;
×
558
    unsigned player_color = GetWorld().GetPlayer(owner - 1).color;
×
559
    if(isFoW)
×
560
        player_color = CalcPlayerFOWDrawColor(player_color);
×
561

562
    const auto curVertexPos = gwv.GetTerrainRenderer().GetVertexPos(pt);
×
563
    for(const auto bPos : helpers::EnumRange<BorderStonePos>{})
×
564
    {
565
        DrawPoint curPos;
×
566
        if(bPos == BorderStonePos::OnPoint)
×
567
            curPos = pos;
×
568
        else if(boundary_stones[bPos])
×
569
            curPos = pos
570
                     - DrawPoint((curVertexPos - gwv.GetTerrainRenderer().GetNeighbourVertexPos(pt, toDirection(bPos)))
×
571
                                 / 2.0f);
×
572
        else
573
            continue;
×
574
        LOADER.boundary_stone_cache[nation].draw(curPos, isFoW ? FOW_DRAW_COLOR : COLOR_WHITE, player_color);
×
575
    }
576
}
577

NEW
578
void GameWorldView::DrawResource(const MapPoint& pt, DrawPoint curPos, const Cheats::ResourceRevealMode resRevealMode)
×
579
{
580
    using RRM = Cheats::ResourceRevealMode;
581

NEW
582
    if(resRevealMode == RRM::Nothing)
×
NEW
583
        return;
×
584

NEW
585
    const Resource res = gwv.GetNode(pt).resources;
×
NEW
586
    const auto amount = res.getAmount();
×
587

NEW
588
    if(!amount)
×
NEW
589
        return;
×
590

NEW
591
    GoodType gt = GoodType::Nothing;
×
NEW
592
    RTTR_Assert(resRevealMode >= RRM::Ores);
×
NEW
593
    switch(res.getType())
×
594
    {
NEW
595
        case ResourceType::Iron: gt = GoodType::IronOre; break;
×
NEW
596
        case ResourceType::Gold: gt = GoodType::Gold; break;
×
NEW
597
        case ResourceType::Coal: gt = GoodType::Coal; break;
×
NEW
598
        case ResourceType::Granite: gt = GoodType::Stones; break;
×
NEW
599
        case ResourceType::Water:
×
NEW
600
            if(resRevealMode < RRM::Water)
×
NEW
601
                return;
×
NEW
602
            gt = GoodType::Water;
×
NEW
603
            break;
×
NEW
604
        case ResourceType::Fish:
×
NEW
605
            if(resRevealMode < RRM::Fish)
×
NEW
606
                return;
×
NEW
607
            gt = GoodType::Fish;
×
NEW
608
            break;
×
NEW
609
        case ResourceType::Nothing: return;
×
610
    }
611

NEW
612
    if(auto* bm = LOADER.GetWareTex(gt))
×
613
    {
NEW
614
        for([[maybe_unused]] const auto i : helpers::range(amount))
×
615
        {
NEW
616
            bm->DrawFull(curPos);
×
NEW
617
            curPos.y -= 4;
×
618
        }
619
    }
620
}
621

UNCOV
622
void GameWorldView::ToggleShowBQ()
×
623
{
624
    show_bq = !show_bq;
×
625
    SaveIngameSettingsValues();
×
626
    onHudSettingsChanged();
×
627
}
×
628

629
void GameWorldView::ToggleShowNames()
×
630
{
631
    show_names = !show_names;
×
632
    SaveIngameSettingsValues();
×
633
    onHudSettingsChanged();
×
634
}
×
635

636
void GameWorldView::ToggleShowProductivity()
×
637
{
638
    show_productivity = !show_productivity;
×
639
    SaveIngameSettingsValues();
×
640
    onHudSettingsChanged();
×
641
}
×
642

643
void GameWorldView::ToggleShowNamesAndProductivity()
×
644
{
645
    if(show_productivity && show_names)
×
646
        show_productivity = show_names = false;
×
647
    else
648
        show_productivity = show_names = true;
×
649
    SaveIngameSettingsValues();
×
650
    onHudSettingsChanged();
×
651
}
×
652

653
void GameWorldView::CopyHudSettingsTo(GameWorldView& other, bool copyBQ) const
×
654
{
655
    other.show_bq = (copyBQ ? show_bq : false);
×
656
    other.show_names = show_names;
×
657
    other.show_productivity = show_productivity;
×
658
}
×
659

660
void GameWorldView::MoveBy(const DrawPoint& numPixels)
24✔
661
{
662
    MoveTo(offset + numPixels);
24✔
663
}
24✔
664

665
void GameWorldView::MoveTo(const DrawPoint& newPos)
34✔
666
{
667
    offset = newPos;
34✔
668
    DrawPoint size(GetWorld().GetWidth() * TR_W, GetWorld().GetHeight() * TR_H);
34✔
669
    if(size.x && size.y)
34✔
670
    {
671
        offset.x %= size.x;
34✔
672
        offset.y %= size.y;
34✔
673

674
        if(offset.x < 0)
34✔
675
            offset.x += size.x;
5✔
676
        if(offset.y < 0)
34✔
677
            offset.y += size.y;
6✔
678
    }
679

680
    CalcFxLx();
34✔
681
}
34✔
682

683
void GameWorldView::MoveToMapPt(const MapPoint pt)
6✔
684
{
685
    if(!pt.isValid())
6✔
686
        return;
×
687

688
    lastOffset = offset;
6✔
689
    Position nodePos = GetWorld().GetNodePos(pt);
6✔
690

691
    MoveTo(nodePos - GetSize() / 2u);
6✔
692
}
693

694
void GameWorldView::MoveToLastPosition()
×
695
{
696
    Position newLastOffset = offset;
×
697

698
    MoveTo(lastOffset);
×
699

700
    lastOffset = newLastOffset;
×
701
}
×
702

703
void GameWorldView::AddDrawNodeCallback(IDrawNodeCallback* newCallback)
×
704
{
705
    RTTR_Assert(newCallback);
×
706
    drawNodeCallbacks.push_back(newCallback);
×
707
}
×
708

709
void GameWorldView::RemoveDrawNodeCallback(IDrawNodeCallback* callbackToRemove)
×
710
{
711
    auto itPos = helpers::find(drawNodeCallbacks, callbackToRemove);
×
712
    RTTR_Assert(itPos != drawNodeCallbacks.end());
×
713
    drawNodeCallbacks.erase(itPos);
×
714
}
×
715

716
void GameWorldView::CalcFxLx()
34✔
717
{
718
    // Calc first and last point in map units (with 1 extra for incomplete triangles)
719
    firstPt.x = offset.x / TR_W - 1;
34✔
720
    firstPt.y = offset.y / TR_H - 1;
34✔
721
    lastPt.x = (offset.x + size_.x) / TR_W + 1;
34✔
722
    const auto maxAltitude = gwv.getMaxNodeAltitude();
34✔
723
    lastPt.y = (offset.y + size_.y + maxAltitude * HEIGHT_FACTOR) / TR_H + 1;
34✔
724

725
    if(effectiveZoomFactor_ != 1.f) //-V550
34✔
726
    {
727
        // Calc pixels we can remove from sides, as they are not drawn due to zoom
728
        PointF diff(size_.x - size_.x / effectiveZoomFactor_, size_.y - size_.y / effectiveZoomFactor_);
×
729
        // Stay centered by removing half the pixels from opposite sites
730
        diff = diff / 2.f;
×
731
        // Convert to map points
732
        diff.x /= TR_W;
×
733
        diff.y /= TR_H;
×
734
        // Don't remove to much
735
        diff.x = std::floor(diff.x);
×
736
        diff.y = std::floor(diff.y);
×
737
        firstPt = Position(PointF(firstPt) + diff);
×
738
        lastPt = Position(PointF(lastPt) - diff);
×
739
    }
740
}
34✔
741

742
void GameWorldView::Resize(const Extent& newSize)
×
743
{
744
    size_ = newSize;
×
745
    CalcFxLx();
×
746
}
×
747

748
void GameWorldView::SaveIngameSettingsValues() const
×
749
{
750
    auto& ingameSettings = SETTINGS.ingame;
×
751
    ingameSettings.showBQ = show_bq;
×
752
    ingameSettings.showNames = show_names;
×
753
    ingameSettings.showProductivity = show_productivity;
×
754
}
×
755

756
void GameWorldView::updateEffectiveZoomFactor()
10✔
757
{
758
    // partially "undo" GUI scale depending on DPI scale
759
    // => zoom levels should look the same on screens with different DPI regardless of GUI scale
760
    effectiveZoomFactor_ = VIDEODRIVER.getGuiScale().screenToView(zoomFactor_ * VIDEODRIVER.getDpiScale());
10✔
761
}
10✔
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