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

Return-To-The-Roots / s25client / 21207834092

21 Jan 2026 11:26AM UTC coverage: 50.595% (-0.006%) from 50.601%
21207834092

Pull #1738

github

web-flow
Merge b3b290695 into b87e768f3
Pull Request #1738: "Show enemy productivity overlay" cheat

7 of 12 new or added lines in 4 files covered. (58.33%)

4 existing lines in 1 file now uncovered.

22646 of 44759 relevant lines covered (50.6%)

35984.22 hits per line

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

13.02
/libs/s25main/world/GameWorldView.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/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/containerUtils.h"
22
#include "helpers/toString.h"
23
#include "ogl/FontStyle.h"
24
#include "ogl/glArchivItem_Bitmap.h"
25
#include "ogl/glFont.h"
26
#include "ogl/glSmartBitmap.h"
27
#include "world/GameWorldBase.h"
28
#include "world/GameWorldViewer.h"
29
#include "gameTypes/RoadBuildState.h"
30
#include "gameData/BuildingConsts.h"
31
#include "gameData/GuiConsts.h"
32
#include "gameData/MapConsts.h"
33
#include "s25util/error.h"
34
#include "s25util/warningSuppression.h"
35
#include <glad/glad.h>
36
#include <boost/format.hpp>
37
#include <cmath>
38

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

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

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

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

63
    float remainingZoomDiff = targetZoomFactor_ - zoomFactor_;
×
64

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

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

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

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

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

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

120
    return pos;
2✔
121
}
122

123
struct ObjectBetweenLines
124
{
125
    noBase& obj;
126
    DrawPoint pos; // Zeichenposition
127

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

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

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

166
    for(int y = firstPt.y; y <= lastPt.y; ++y)
×
167
    {
168
        // Figuren speichern, die in dieser Zeile gemalt werden müssen
169
        // und sich zwischen zwei Zeilen befinden, da sie dazwischen laufen
170
        std::vector<ObjectBetweenLines> between_lines;
×
171

172
        for(int x = firstPt.x; x <= lastPt.x; ++x)
×
173
        {
174
            Position curOffset;
×
175
            const MapPoint curPt = terrainRenderer.ConvertCoords(Position(x, y), &curOffset);
×
176
            DrawPoint curPos = GetWorld().GetNodePos(curPt) - offset + curOffset;
×
177

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

187
            Visibility visibility = gwv.GetVisibility(curPt);
×
188

189
            DrawBoundaryStone(curPt, curPos, visibility);
×
190

191
            if(visibility == Visibility::Visible)
×
192
            {
193
                DrawObject(curPt, curPos);
×
194
                DrawMovingFiguresFromBelow(terrainRenderer, Position(x, y), between_lines);
×
195
                DrawFigures(curPt, curPos, between_lines);
×
196

197
                // Construction aid mode
198
                if(show_bq)
×
199
                    DrawConstructionAid(curPt, curPos);
×
200
            } else if(visibility == Visibility::FogOfWar)
×
201
            {
202
                const FOWObject* fowobj = gwv.GetYoungestFOWObject(MapPoint(curPt));
×
203
                if(fowobj)
×
204
                    fowobj->Draw(curPos);
×
205
            }
206

207
            for(IDrawNodeCallback* callback : drawNodeCallbacks)
×
208
                callback->onDraw(curPt, curPos);
×
209
        }
210

211
        // Figuren zwischen den Zeilen zeichnen
212
        for(auto& between_line : between_lines)
×
213
            between_line.obj.Draw(between_line.pos);
×
214
    }
215

216
    if(show_names || show_productivity)
×
217
        DrawNameProductivityOverlay(terrainRenderer);
×
218

219
    DrawGUI(rb, terrainRenderer, selected, drawMouse);
×
220

221
    // Umherfliegende Katapultsteine zeichnen
222
    for(auto* catapult_stone : GetWorld().catapult_stones)
×
223
    {
224
        if(gwv.GetVisibility(catapult_stone->dest_building) == Visibility::Visible
×
225
           || gwv.GetVisibility(catapult_stone->dest_map) == Visibility::Visible)
×
226
            catapult_stone->Draw(offset);
×
227
    }
228

229
    if(effectiveZoomFactor_ != 1.f) //-V550
×
230
    {
231
        glMatrixMode(GL_PROJECTION);
×
232
        glPopMatrix();
×
233
        glMatrixMode(GL_MODELVIEW);
×
234
    }
235
    glTranslatef(-static_cast<GLfloat>(origin_.x) / effectiveZoomFactor_,
×
236
                 -static_cast<GLfloat>(origin_.y) / effectiveZoomFactor_, 0.0f);
×
237

238
    glScissor(0, 0, windowSize.width, windowSize.height);
×
239
}
×
240

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

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

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

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

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

288
            // Currently selected point
289
            if(selectedPt == curPt)
×
290
                LOADER.GetMapTexture(20)->DrawFull(curPos);
×
291

292
            // not building roads, no further action needed
293
            if(rb.mode == RoadBuildMode::Disabled)
×
294
                continue;
×
295

296
            // we dont own curPt, no need for any rendering...
297
            if(!gwv.IsPlayerTerritory(curPt))
×
298
                continue;
×
299

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

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

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

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

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

359
void GameWorldView::DrawNameProductivityOverlay(const TerrainRenderer& terrainRenderer)
×
360
{
361
    for(int x = firstPt.x; x <= lastPt.x; ++x)
×
362
    {
363
        for(int y = firstPt.y; y <= lastPt.y; ++y)
×
364
        {
365
            // Coordinate transform
366
            Position curOffset;
×
367
            MapPoint pt = terrainRenderer.ConvertCoords(Position(x, y), &curOffset);
×
368

369
            const auto* no = GetWorld().GetSpecObj<noBaseBuilding>(pt);
×
370
            if(!no)
×
371
                continue;
×
372

373
            Position curPos = GetWorld().GetNodePos(pt) - offset + curOffset;
×
374
            curPos.y -= 22;
×
375

376
            // Is object not belonging to local player?
377
            if(no->GetPlayer() != gwv.GetPlayerId())
×
378
            {
379
                if(GetWorld().GetGGS().getSelection(AddonId::MILITARY_AID) == 2 && gwv.GetNumSoldiersForAttack(pt) > 0)
×
380
                {
381
                    auto* attackAidImage = LOADER.GetImageN("map_new", 20000);
×
382
                    attackAidImage->DrawFull(curPos - DrawPoint(0, attackAidImage->getHeight()));
×
383
                }
384
                // Do not draw enemy productivity overlay unless the object is visible AND the related cheat is on
NEW
385
                if(!(gwv.GetVisibility(pt) == Visibility::Visible
×
NEW
386
                     && GetWorld().GetGameInterface()->GI_GetCheats().shouldShowEnemyProductivityOverlay()))
×
NEW
387
                    continue;
×
388
            }
389

390
            // Draw object name
391
            if(show_names)
×
392
            {
393
                unsigned color = (no->GetGOT() == GO_Type::Buildingsite) ? COLOR_GREY : COLOR_YELLOW;
×
394
                SmallFont->Draw(curPos, _(BUILDING_NAMES[no->GetBuildingType()]),
×
395
                                FontStyle::CENTER | FontStyle::VCENTER, color);
396
                curPos.y += SmallFont->getHeight();
×
397
            }
398

399
            // Draw productivity/soldiers
400
            if(show_productivity)
×
401
                DrawProductivity(*no, curPos);
×
402
        }
403
    }
404
}
×
405

406
void GameWorldView::DrawProductivity(const noBaseBuilding& no, const DrawPoint& curPos)
×
407
{
408
    const GO_Type got = no.GetGOT();
×
409
    if(got == GO_Type::Buildingsite)
×
410
    {
411
        unsigned color = COLOR_GREY;
×
412

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

421
        if(!n.HasWorker())
×
422
            text = _("(House unoccupied)");
×
423
        else if(n.IsProductionDisabledVirtual())
×
424
            text = _("(stopped)");
×
425
        else
426
        {
427
            // Catapult and Lookout tower doesn't have productivity!
428
            if(n.GetBuildingType() == BuildingType::Catapult || n.GetBuildingType() == BuildingType::LookoutTower)
×
429
                return;
×
430

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

451
        SmallFont->Draw(curPos, sSoldiers, FontStyle::CENTER | FontStyle::VCENTER,
×
452
                        (soldiers_count > 0) ? COLOR_YELLOW : COLOR_RED);
×
453
    }
454
}
455

456
void GameWorldView::DrawFigures(const MapPoint& pt, const DrawPoint& curPos,
×
457
                                std::vector<ObjectBetweenLines>& between_lines) const
458
{
459
    for(noBase& figure : GetWorld().GetFigures(pt))
×
460
    {
461
        if(figure.IsMoving())
×
462
        {
463
            // Drawn from above
464
            Direction curMoveDir = static_cast<noMovable&>(figure).GetCurMoveDir();
×
465
            if(curMoveDir == Direction::NorthEast || curMoveDir == Direction::NorthWest)
×
466
                continue;
×
467
            // Draw later
468
            between_lines.push_back(ObjectBetweenLines(figure, curPos));
×
469
        } else if(figure.GetGOT() == GO_Type::Ship)
×
470
            between_lines.push_back(ObjectBetweenLines(figure, curPos)); // TODO: Why special handling for ships?
×
471
        else
472
            // Ansonsten jetzt schon zeichnen
473
            figure.Draw(curPos);
×
474
    }
475
}
×
476

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

490
        for(noBase& figure : GetWorld().GetFigures(curPt))
×
491
        {
492
            if(figure.IsMoving() && static_cast<noMovable&>(figure).GetCurMoveDir() == dir)
×
493
                between_lines.push_back(ObjectBetweenLines(figure, figPos));
×
494
        }
495
    }
496
}
×
497

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

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

530
void GameWorldView::DrawObject(const MapPoint& pt, const DrawPoint& curPos) const
×
531
{
532
    noBase* obj = GetWorld().GetNode(pt).obj;
×
533
    if(!obj)
×
534
        return;
×
535

536
    obj->Draw(curPos);
×
537
}
538

539
void GameWorldView::DrawBoundaryStone(const MapPoint& pt, const DrawPoint pos, Visibility vis)
×
540
{
541
    if(vis == Visibility::Invisible)
×
542
        return;
×
543

544
    const bool isFoW = vis == Visibility::FogOfWar;
×
545

546
    const BoundaryStones& boundary_stones =
547
      isFoW ? gwv.GetYoungestFOWNode(pt).boundary_stones : GetWorld().GetNode(pt).boundary_stones;
×
548
    const unsigned char owner = boundary_stones[BorderStonePos::OnPoint];
×
549

550
    if(!owner)
×
551
        return;
×
552

553
    const Nation nation = GetWorld().GetPlayer(owner - 1).nation;
×
554
    unsigned player_color = GetWorld().GetPlayer(owner - 1).color;
×
555
    if(isFoW)
×
556
        player_color = CalcPlayerFOWDrawColor(player_color);
×
557

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

574
void GameWorldView::ToggleShowBQ()
×
575
{
576
    show_bq = !show_bq;
×
577
    SaveIngameSettingsValues();
×
578
    onHudSettingsChanged();
×
579
}
×
580

581
void GameWorldView::ToggleShowNames()
×
582
{
583
    show_names = !show_names;
×
584
    SaveIngameSettingsValues();
×
585
    onHudSettingsChanged();
×
586
}
×
587

588
void GameWorldView::ToggleShowProductivity()
×
589
{
590
    show_productivity = !show_productivity;
×
591
    SaveIngameSettingsValues();
×
592
    onHudSettingsChanged();
×
593
}
×
594

595
void GameWorldView::ToggleShowNamesAndProductivity()
×
596
{
597
    if(show_productivity && show_names)
×
598
        show_productivity = show_names = false;
×
599
    else
600
        show_productivity = show_names = true;
×
601
    SaveIngameSettingsValues();
×
602
    onHudSettingsChanged();
×
603
}
×
604

605
void GameWorldView::CopyHudSettingsTo(GameWorldView& other, bool copyBQ) const
×
606
{
607
    other.show_bq = (copyBQ ? show_bq : false);
×
608
    other.show_names = show_names;
×
609
    other.show_productivity = show_productivity;
×
610
}
×
611

612
void GameWorldView::MoveBy(const DrawPoint& numPixels)
23✔
613
{
614
    MoveTo(offset + numPixels);
23✔
615
}
23✔
616

617
void GameWorldView::MoveTo(const DrawPoint& newPos)
33✔
618
{
619
    offset = newPos;
33✔
620
    DrawPoint size(GetWorld().GetWidth() * TR_W, GetWorld().GetHeight() * TR_H);
33✔
621
    if(size.x && size.y)
33✔
622
    {
623
        offset.x %= size.x;
33✔
624
        offset.y %= size.y;
33✔
625

626
        if(offset.x < 0)
33✔
627
            offset.x += size.x;
5✔
628
        if(offset.y < 0)
33✔
629
            offset.y += size.y;
6✔
630
    }
631

632
    CalcFxLx();
33✔
633
}
33✔
634

635
void GameWorldView::MoveToMapPt(const MapPoint pt)
6✔
636
{
637
    if(!pt.isValid())
6✔
638
        return;
×
639

640
    lastOffset = offset;
6✔
641
    Position nodePos = GetWorld().GetNodePos(pt);
6✔
642

643
    MoveTo(nodePos - GetSize() / 2u);
6✔
644
}
645

646
void GameWorldView::MoveToLastPosition()
×
647
{
648
    Position newLastOffset = offset;
×
649

650
    MoveTo(lastOffset);
×
651

652
    lastOffset = newLastOffset;
×
653
}
×
654

655
void GameWorldView::AddDrawNodeCallback(IDrawNodeCallback* newCallback)
×
656
{
657
    RTTR_Assert(newCallback);
×
658
    drawNodeCallbacks.push_back(newCallback);
×
659
}
×
660

661
void GameWorldView::RemoveDrawNodeCallback(IDrawNodeCallback* callbackToRemove)
×
662
{
663
    auto itPos = helpers::find(drawNodeCallbacks, callbackToRemove);
×
664
    RTTR_Assert(itPos != drawNodeCallbacks.end());
×
665
    drawNodeCallbacks.erase(itPos);
×
666
}
×
667

668
void GameWorldView::CalcFxLx()
33✔
669
{
670
    // Calc first and last point in map units (with 1 extra for incomplete triangles)
671
    firstPt.x = offset.x / TR_W - 1;
33✔
672
    firstPt.y = offset.y / TR_H - 1;
33✔
673
    lastPt.x = (offset.x + size_.x) / TR_W + 1;
33✔
674
    const auto maxAltitude = gwv.getMaxNodeAltitude();
33✔
675
    lastPt.y = (offset.y + size_.y + maxAltitude * HEIGHT_FACTOR) / TR_H + 1;
33✔
676

677
    if(effectiveZoomFactor_ != 1.f) //-V550
33✔
678
    {
679
        // Calc pixels we can remove from sides, as they are not drawn due to zoom
680
        PointF diff(size_.x - size_.x / effectiveZoomFactor_, size_.y - size_.y / effectiveZoomFactor_);
×
681
        // Stay centered by removing half the pixels from opposite sites
682
        diff = diff / 2.f;
×
683
        // Convert to map points
684
        diff.x /= TR_W;
×
685
        diff.y /= TR_H;
×
686
        // Don't remove to much
687
        diff.x = std::floor(diff.x);
×
688
        diff.y = std::floor(diff.y);
×
689
        firstPt = Position(PointF(firstPt) + diff);
×
690
        lastPt = Position(PointF(lastPt) - diff);
×
691
    }
692
}
33✔
693

694
void GameWorldView::Resize(const Extent& newSize)
×
695
{
696
    size_ = newSize;
×
697
    CalcFxLx();
×
698
}
×
699

700
void GameWorldView::SaveIngameSettingsValues() const
×
701
{
702
    auto& ingameSettings = SETTINGS.ingame;
×
703
    ingameSettings.showBQ = show_bq;
×
704
    ingameSettings.showNames = show_names;
×
705
    ingameSettings.showProductivity = show_productivity;
×
706
}
×
707

708
void GameWorldView::updateEffectiveZoomFactor()
9✔
709
{
710
    // partially "undo" GUI scale depending on DPI scale
711
    // => zoom levels should look the same on screens with different DPI regardless of GUI scale
712
    effectiveZoomFactor_ = VIDEODRIVER.getGuiScale().screenToView(zoomFactor_ * VIDEODRIVER.getDpiScale());
9✔
713
}
9✔
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