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

Return-To-The-Roots / s25client / 22518049176

28 Feb 2026 09:27AM UTC coverage: 50.149%. First build
22518049176

Pull #1890

github

web-flow
Merge 0b97c9676 into 8a6c9b6f2
Pull Request #1890: Add support for borderless Windows

155 of 626 new or added lines in 44 files covered. (24.76%)

23038 of 45939 relevant lines covered (50.15%)

42060.08 hits per line

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

0.0
/libs/s25main/ingameWindows/iwMapDebug.cpp
1
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "iwMapDebug.h"
6
#include "GamePlayer.h"
7
#include "Loader.h"
8
#include "PointOutput.h"
9
#include "RttrForeachPt.h"
10
#include "ai/aijh/AIPlayerJH.h"
11
#include "controls/ctrlCheck.h"
12
#include "controls/ctrlComboBox.h"
13
#include "controls/ctrlEdit.h"
14
#include "controls/ctrlTimer.h"
15
#include "driver/KeyEvent.h"
16
#include "helpers/toString.h"
17
#include "notifications/NodeNote.h"
18
#include "ogl/glFont.h"
19
#include "world/GameWorldBase.h"
20
#include "world/GameWorldView.h"
21
#include "world/NodeMapBase.h"
22
#include "world/TerritoryRegion.h"
23
#include "gameTypes/GameTypesOutput.h"
24
#include "gameTypes/TextureColor.h"
25
#include "gameData/const_gui_ids.h"
26
#include "s25util/StringConversion.h"
27
#include <boost/nowide/iostream.hpp>
28
#include <chrono>
29

30
namespace {
31
enum
32
{
33
    ID_cbShowCoordinates,
34
    ID_cbShowWhat,
35
    ID_cbShowForPlayer,
36
    ID_cbCheckEventForPlayer,
37
    ID_tmrCheckEvents,
38
    ID_lblJump,
39
    ID_edtJumpX,
40
    ID_edtJumpY,
41
};
42
}
43

44
class iwMapDebug::DebugPrinter : public IDrawNodeCallback
45
{
46
public:
47
    DebugPrinter(const GameWorldBase& gwb) : showCoords(true), showDataIdx(0), playerIdx(0), gw(gwb), font(NormalFont)
×
48
    {}
×
49

50
    void onDraw(const MapPoint& pt, const DrawPoint& displayPt) override
×
51
    {
52
        std::string data;
×
53
        unsigned coordsColor = 0xFFFFFF00;
×
54
        unsigned dataColor = 0xFFFF0000;
×
55
        const MapNode& node = gw.GetNode(pt);
×
56
        switch(showDataIdx)
×
57
        {
58
            case 1:
×
59
                if(node.reserved)
×
60
                    data = "R";
×
61
                break;
×
62
            case 2: data = helpers::toString(node.altitude); break;
×
63
            case 3: data = helpers::toString(node.resources.getValue()); break;
×
64
            case 4:
×
65
                if(node.seaId)
×
66
                    data = helpers::toString(node.seaId);
×
67
                else if(gw.GetSeaFromCoastalPoint(pt))
×
68
                    data = "C";
×
69
                break;
×
70
            case 5: data = helpers::toString(node.owner); break;
×
71
            case 6:
×
72
            {
73
                bool isAllowed =
74
                  TerritoryRegion::IsPointValid(gw.GetSize(), gw.GetPlayer(playerIdx).GetRestrictedArea(), pt);
×
75
                coordsColor = dataColor = isAllowed ? 0xFF00FF00 : 0xFFFF0000;
×
76
                if(!showCoords)
×
77
                    data = isAllowed ? "y" : "n";
×
78
                break;
×
79
            }
80
        }
81

82
        if(showCoords)
×
83
        {
84
            std::string coord = helpers::toString(pt.x) + ":" + helpers::toString(pt.y);
×
85
            font->Draw(displayPt, coord, FontStyle{}, coordsColor);
×
86
        }
87
        if(!data.empty())
×
88
            font->Draw(displayPt, data, FontStyle{}, dataColor);
×
89
    }
×
90

91
    bool showCoords;
92
    unsigned showDataIdx;
93
    unsigned playerIdx;
94
    const GameWorldBase& gw;
95
    const glFont* font;
96
};
97

98
class iwMapDebug::EventChecker
99
{
100
public:
101
    EventChecker(const GameWorldBase& gw) : gw(gw)
×
102
    {
103
        bqMap.Resize(gw.GetSize());
×
104
        for(unsigned i = 0; i < gw.GetNumPlayers(); i++)
×
105
        {
106
            if(gw.GetPlayer(i).isUsed())
×
107
                usedPlayerIdxs.push_back(i);
×
108
        }
109
        RTTR_FOREACH_PT(MapPoint, bqMap.GetSize())
×
110
        {
111
            for(const unsigned playerIdx : usedPlayerIdxs)
×
112
                bqMap[pt][playerIdx] = gw.GetBQ(pt, playerIdx);
×
113
        }
114
        nodeSub = AIJH::recordBQsToUpdate(this->gw, this->pointsToUpdate);
×
115
    }
×
116
    void check()
×
117
    {
118
        for(const MapPoint pt : pointsToUpdate)
×
119
            updateBq(pt);
×
120
        pointsToUpdate.clear();
×
121
        RTTR_FOREACH_PT(MapPoint, bqMap.GetSize())
×
122
        {
123
            for(const unsigned playerIdx : usedPlayerIdxs)
×
124
            {
125
                if(bqMap[pt][playerIdx] != gw.GetBQ(pt, playerIdx))
×
126
                {
127
                    boost::nowide::cerr << "BQs mismatch at " << pt << " for player " << playerIdx << ": "
×
128
                                        << bqMap[pt][playerIdx] << "!=" << gw.GetBQ(pt, playerIdx) << std::endl;
×
129
                }
130
            }
131
        }
132
    }
×
133

134
private:
135
    void updateBq(MapPoint pt)
×
136
    {
137
        for(const unsigned playerIdx : usedPlayerIdxs)
×
138
            bqMap[pt][playerIdx] = gw.GetBQ(pt, playerIdx);
×
139
    }
×
140
    const GameWorldBase& gw;
141
    std::vector<unsigned> usedPlayerIdxs;
142
    using BqNode = std::array<BuildingQuality, MAX_PLAYERS>;
143
    NodeMapBase<BqNode> bqMap;
144
    std::vector<MapPoint> pointsToUpdate;
145
    Subscription nodeSub;
146
};
147

148
using namespace std::chrono_literals;
149
static const std::array<std::chrono::milliseconds, 6> BQ_CHECK_INTERVALS = {10s, 1s, 500ms, 250ms, 100ms, 50ms};
150

151
iwMapDebug::iwMapDebug(GameWorldView& gwv, bool allowCheating)
×
152
    : IngameWindow(CGI_MAP_DEBUG, IngameWindow::posLastOrCenter, Extent(230, 160), _("Map Debug"),
×
153
                   LOADER.GetImageN("resource", 41)),
×
154
      gwv(gwv), printer(std::make_unique<DebugPrinter>(gwv.GetWorld()))
×
155
{
156
    gwv.AddDrawNodeCallback(printer.get());
×
157

158
    DrawPoint curPos(15, 25);
×
159
    constexpr Extent ctrlSize(200, 20);
×
160
    constexpr auto spaceY = ctrlSize.y + 5;
×
161

162
    ctrlCheck* cbShowCoords =
163
      AddCheckBox(ID_cbShowCoordinates, curPos, ctrlSize, TextureColor::Grey, _("Show coordinates"), NormalFont);
×
164
    curPos.y += spaceY;
×
165
    cbShowCoords->setChecked(true);
×
166

167
    constexpr Extent editSize(40, ctrlSize.y);
×
168
    AddText(ID_lblJump, curPos + DrawPoint(0, 2), _("Jump to X:Y"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
169
    const DrawPoint edtPos = curPos + DrawPoint(ctrlSize.x - 2 * editSize.x - 2, 0);
×
170
    AddEdit(ID_edtJumpX, edtPos, editSize, TextureColor::Grey, NormalFont, 4);
×
171
    AddEdit(ID_edtJumpY, edtPos + DrawPoint(editSize.x + 2, 0), editSize, TextureColor::Grey, NormalFont, 4);
×
172
    curPos.y += spaceY;
×
173

174
    ctrlComboBox* cbCheckEvents =
175
      AddComboBox(ID_cbCheckEventForPlayer, curPos, ctrlSize, TextureColor::Grey, NormalFont, 100);
×
176
    curPos.y += spaceY;
×
NEW
177
    cbCheckEvents->AddItem(_("BQ check disabled"));
×
178
    for(const std::chrono::milliseconds ms : BQ_CHECK_INTERVALS)
×
179
    {
NEW
180
        cbCheckEvents->AddItem((boost::format(_("BQ check every %1%ms")) % ms.count()).str());
×
181
    }
182
    cbCheckEvents->SetSelection(0);
×
183
    using namespace std::chrono_literals;
184
    AddTimer(ID_tmrCheckEvents, 500ms)->Stop();
×
185

186
    if(allowCheating)
×
187
    {
188
        ctrlComboBox* data = AddComboBox(ID_cbShowWhat, curPos, ctrlSize, TextureColor::Grey, NormalFont, 100);
×
189
        curPos.y += spaceY;
×
NEW
190
        data->AddItem(_("Nothing"));
×
NEW
191
        data->AddItem(_("Reserved"));
×
NEW
192
        data->AddItem(_("Altitude"));
×
NEW
193
        data->AddItem(_("Resources"));
×
NEW
194
        data->AddItem(_("Sea Id"));
×
NEW
195
        data->AddItem(_("Owner"));
×
NEW
196
        data->AddItem(_("Restricted area"));
×
197
        data->SetSelection(1);
×
198
        ctrlComboBox* players = AddComboBox(ID_cbShowForPlayer, curPos, ctrlSize, TextureColor::Grey, NormalFont, 100);
×
199
        curPos.y += spaceY;
×
200
        for(unsigned pIdx = 0; pIdx < gwv.GetWorld().GetNumPlayers(); pIdx++)
×
201
        {
202
            const GamePlayer& p = gwv.GetWorld().GetPlayer(pIdx);
×
203
            if(!p.isUsed())
×
NEW
204
                players->AddItem((boost::format(_("Player %1%")) % pIdx).str());
×
205
            else
NEW
206
                players->AddItem(p.name);
×
207
        }
208
        players->SetSelection(0);
×
209
        printer->showDataIdx = data->GetSelection().get();
×
210
        printer->playerIdx = players->GetSelection().get();
×
211
    } else
212
    {
213
        printer->showDataIdx = 0;
×
214
        printer->playerIdx = 0;
×
215
        Extent iwSize = GetIwSize();
×
216
        iwSize.y -= 40 + 10;
×
217
        SetIwSize(iwSize);
×
218
    }
219

220
    printer->showCoords = cbShowCoords->isChecked();
×
221
}
×
222

223
iwMapDebug::~iwMapDebug()
×
224
{
225
    gwv.RemoveDrawNodeCallback(printer.get());
×
226
}
×
227

228
void iwMapDebug::Msg_ComboSelectItem(const unsigned ctrl_id, const unsigned select)
×
229
{
230
    if(ctrl_id == ID_cbShowWhat)
×
231
        printer->showDataIdx = select;
×
232
    else if(ctrl_id == ID_cbShowForPlayer)
×
233
        printer->playerIdx = select;
×
234
    else if(ctrl_id == ID_cbCheckEventForPlayer)
×
235
    {
236
        if(select == 0)
×
237
        {
238
            eventChecker.reset();
×
239
            GetCtrl<ctrlTimer>(ID_tmrCheckEvents)->Stop();
×
240
        } else
241
        {
242
            eventChecker = std::make_unique<EventChecker>(gwv.GetWorld());
×
243
            GetCtrl<ctrlTimer>(ID_tmrCheckEvents)->Start(BQ_CHECK_INTERVALS[select - 1]);
×
244
        }
245
    }
246
}
×
247

248
void iwMapDebug::Msg_CheckboxChange(const unsigned ctrl_id, const bool checked)
×
249
{
250
    if(ctrl_id == ID_cbShowCoordinates)
×
251
        printer->showCoords = checked;
×
252
}
×
253

254
void iwMapDebug::Msg_Timer(unsigned /*ctrl_id*/)
×
255
{
256
    if(eventChecker)
×
257
        eventChecker->check();
×
258
}
×
259

260
void iwMapDebug::Msg_EditEnter(unsigned ctrl_id)
×
261
{
262
    RTTR_Assert(ctrl_id == ID_edtJumpX || ctrl_id == ID_edtJumpY);
×
263
    Position pos;
×
264
    if(s25util::tryFromStringClassic(GetCtrl<ctrlEdit>(ID_edtJumpX)->GetText(), pos.x)
×
265
       && s25util::tryFromStringClassic(GetCtrl<ctrlEdit>(ID_edtJumpY)->GetText(), pos.y))
×
266
    {
267
        gwv.MoveToMapPt(gwv.GetWorld().MakeMapPoint(pos));
×
268
    }
269
}
×
270

271
bool iwMapDebug::Msg_KeyDown(const KeyEvent& ke)
×
272
{
273
    switch(ke.kt)
×
274
    {
275
        case KeyType::Return: Msg_EditEnter(ID_edtJumpY); break;
×
276
        case KeyType::Tab:
×
277
        {
278
            auto* edtNewFocus = GetCtrl<ctrlEdit>(ID_edtJumpX);
×
279
            if(edtNewFocus->HasFocus())
×
280
                edtNewFocus = GetCtrl<ctrlEdit>(ID_edtJumpY);
×
281
            edtNewFocus->SetFocus();
×
282
            edtNewFocus->SetText("");
×
283
        };
284
        break;
×
285
        default: return IngameWindow::Msg_KeyDown(ke);
×
286
    }
287
    return true;
×
288
}
289

290
void iwMapDebug::SetActive(bool activate)
×
291
{
292
    IngameWindow::SetActive(activate);
×
293
    if(activate)
×
294
        GetCtrl<ctrlEdit>(ID_edtJumpX)->SetFocus();
×
295
}
×
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