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

Return-To-The-Roots / s25client / 12325966962

14 Dec 2024 02:14AM UTC coverage: 50.206% (+0.04%) from 50.166%
12325966962

Pull #1688

github

web-flow
Merge 7e31a47c7 into 77372c5a4
Pull Request #1688: Refactoring of existing cheats (see #1679)

33 of 45 new or added lines in 4 files covered. (73.33%)

255 existing lines in 1 file now uncovered.

22296 of 44409 relevant lines covered (50.21%)

33749.32 hits per line

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

31.22
/libs/s25main/desktops/dskGameInterface.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 "dskGameInterface.h"
6
#include "CollisionDetection.h"
7
#include "EventManager.h"
8
#include "Game.h"
9
#include "GamePlayer.h"
10
#include "Loader.h"
11
#include "NWFInfo.h"
12
#include "Settings.h"
13
#include "SoundManager.h"
14
#include "WindowManager.h"
15
#include "addons/AddonMaxWaterwayLength.h"
16
#include "buildings/noBuildingSite.h"
17
#include "buildings/nobHQ.h"
18
#include "buildings/nobHarborBuilding.h"
19
#include "buildings/nobMilitary.h"
20
#include "buildings/nobStorehouse.h"
21
#include "buildings/nobTemple.h"
22
#include "buildings/nobUsual.h"
23
#include "controls/ctrlImageButton.h"
24
#include "controls/ctrlText.h"
25
#include "driver/MouseCoords.h"
26
#include "drivers/VideoDriverWrapper.h"
27
#include "helpers/format.hpp"
28
#include "helpers/strUtils.h"
29
#include "helpers/toString.h"
30
#include "ingameWindows/iwAIDebug.h"
31
#include "ingameWindows/iwAction.h"
32
#include "ingameWindows/iwBaseWarehouse.h"
33
#include "ingameWindows/iwBuildOrder.h"
34
#include "ingameWindows/iwBuilding.h"
35
#include "ingameWindows/iwBuildingProductivities.h"
36
#include "ingameWindows/iwBuildingSite.h"
37
#include "ingameWindows/iwBuildings.h"
38
#include "ingameWindows/iwDiplomacy.h"
39
#include "ingameWindows/iwDistribution.h"
40
#include "ingameWindows/iwEconomicProgress.h"
41
#include "ingameWindows/iwEndgame.h"
42
#include "ingameWindows/iwHQ.h"
43
#include "ingameWindows/iwHarborBuilding.h"
44
#include "ingameWindows/iwInventory.h"
45
#include "ingameWindows/iwMainMenu.h"
46
#include "ingameWindows/iwMapDebug.h"
47
#include "ingameWindows/iwMerchandiseStatistics.h"
48
#include "ingameWindows/iwMilitary.h"
49
#include "ingameWindows/iwMilitaryBuilding.h"
50
#include "ingameWindows/iwMinimap.h"
51
#include "ingameWindows/iwMusicPlayer.h"
52
#include "ingameWindows/iwOptionsWindow.h"
53
#include "ingameWindows/iwPostWindow.h"
54
#include "ingameWindows/iwRoadWindow.h"
55
#include "ingameWindows/iwSave.h"
56
#include "ingameWindows/iwShip.h"
57
#include "ingameWindows/iwSkipGFs.h"
58
#include "ingameWindows/iwStatistics.h"
59
#include "ingameWindows/iwTempleBuilding.h"
60
#include "ingameWindows/iwTextfile.h"
61
#include "ingameWindows/iwTools.h"
62
#include "ingameWindows/iwTrade.h"
63
#include "ingameWindows/iwTransport.h"
64
#include "ingameWindows/iwVictory.h"
65
#include "lua/GameDataLoader.h"
66
#include "network/GameClient.h"
67
#include "notifications/BuildingNote.h"
68
#include "notifications/NotificationManager.h"
69
#include "ogl/FontStyle.h"
70
#include "ogl/SoundEffectItem.h"
71
#include "ogl/glArchivItem_Bitmap_Player.h"
72
#include "ogl/glFont.h"
73
#include "pathfinding/FindPathForRoad.h"
74
#include "postSystem/PostBox.h"
75
#include "postSystem/PostMsg.h"
76
#include "random/Random.h"
77
#include "world/GameWorldBase.h"
78
#include "world/GameWorldViewer.h"
79
#include "nodeObjs/noFlag.h"
80
#include "nodeObjs/noTree.h"
81
#include "gameData/BuildingProperties.h"
82
#include "gameData/GameConsts.h"
83
#include "gameData/GuiConsts.h"
84
#include "gameData/TerrainDesc.h"
85
#include "gameData/const_gui_ids.h"
86
#include "liblobby/LobbyClient.h"
87
#include <algorithm>
88
#include <cstdio>
89
#include <utility>
90

91
namespace {
92
enum
93
{
94
    ID_btMap,
95
    ID_btOptions,
96
    ID_btConstructionAid,
97
    ID_btPost,
98
    ID_txtNumMsg
99
};
100
}
101

102
dskGameInterface::dskGameInterface(std::shared_ptr<Game> game, std::shared_ptr<const NWFInfo> nwfInfo,
5✔
103
                                   unsigned playerIdx, bool initOGL)
5✔
104
    : Desktop(nullptr), game_(std::move(game)), nwfInfo_(std::move(nwfInfo)),
10✔
105
      worldViewer(playerIdx, const_cast<Game&>(*game_).world_),
5✔
106
      gwv(worldViewer, Position(0, 0), VIDEODRIVER.GetRenderSize()), cbb(*LOADER.GetPaletteN("pal5")),
15✔
107
      actionwindow(nullptr), roadwindow(nullptr), minimap(worldViewer), isScrolling(false), zoomLvl(ZOOM_DEFAULT_INDEX),
5✔
108
      cheats_(const_cast<Game&>(*game_).world_),
5✔
109
      cheatCommandTracker_(game_->world_.IsSinglePlayer() ? &cheats_ : nullptr)
15✔
110
{
111
    road.mode = RoadBuildMode::Disabled;
5✔
112
    road.point = MapPoint(0, 0);
5✔
113
    road.start = MapPoint(0, 0);
5✔
114

115
    SetScale(false);
5✔
116

117
    DrawPoint barPos((GetSize().x - LOADER.GetImageN("resource", 29)->getWidth()) / 2 + 44,
10✔
118
                     GetSize().y - LOADER.GetImageN("resource", 29)->getHeight() + 4);
15✔
119

120
    Extent btSize = Extent(37, 32);
5✔
121
    AddImageButton(ID_btMap, barPos, btSize, TextureColor::Green1, LOADER.GetImageN("io", 50), _("Map"))
5✔
122
      ->SetBorder(false);
10✔
123
    barPos.x += btSize.x;
5✔
124
    AddImageButton(ID_btOptions, barPos, btSize, TextureColor::Green1, LOADER.GetImageN("io", 192), _("Main selection"))
5✔
125
      ->SetBorder(false);
10✔
126
    barPos.x += btSize.x;
5✔
127
    AddImageButton(ID_btConstructionAid, barPos, btSize, TextureColor::Green1, LOADER.GetImageN("io", 83),
5✔
128
                   _("Construction aid mode"))
129
      ->SetBorder(false);
10✔
130
    barPos.x += btSize.x;
5✔
131
    AddImageButton(ID_btPost, barPos, btSize, TextureColor::Green1, LOADER.GetImageN("io", 62), _("Post office"))
5✔
132
      ->SetBorder(false);
10✔
133
    barPos += DrawPoint(18, 24);
5✔
134

135
    AddText(ID_txtNumMsg, barPos, "", COLOR_YELLOW, FontStyle::CENTER | FontStyle::VCENTER, SmallFont);
5✔
136

137
    const_cast<Game&>(*game_).world_.SetGameInterface(this);
5✔
138

139
    std::fill(borders.begin(), borders.end(), (glArchivItem_Bitmap*)(nullptr));
5✔
140
    cbb.loadEdges(LOADER.GetArchive("resource"));
10✔
141
    cbb.buildBorder(VIDEODRIVER.GetRenderSize(), borders);
5✔
142

143
    InitPlayer();
5✔
144
    if(initOGL)
5✔
UNCOV
145
        worldViewer.InitTerrainRenderer();
×
146

147
    VIDEODRIVER.setTargetFramerate(SETTINGS.video.framerate); // Use requested setting for ingame
5✔
148
}
5✔
149

150
void dskGameInterface::InitPlayer()
5✔
151
{
152
    // Jump to players HQ if it exists
153
    if(worldViewer.GetPlayer().GetHQPos().isValid())
5✔
154
        gwv.MoveToMapPt(worldViewer.GetPlayer().GetHQPos());
5✔
155

156
    evBld = worldViewer.GetWorld().GetNotifications().subscribe<BuildingNote>([this](const auto& note) {
10✔
157
        if(note.player == worldViewer.GetPlayerId())
×
UNCOV
158
            this->OnBuildingNote(note);
×
159
    });
5✔
160
    PostBox& postBox = GetPostBox();
5✔
161
    postBox.ObserveNewMsg([this](const auto& msg, auto msgCt) { this->NewPostMessage(msg, msgCt); });
5✔
162
    postBox.ObserveDeletedMsg([this](auto msgCt) { this->PostMessageDeleted(msgCt); });
5✔
163
    UpdatePostIcon(postBox.GetNumMsgs(), true);
5✔
164
}
5✔
165

166
PostBox& dskGameInterface::GetPostBox()
5✔
167
{
168
    PostBox* postBox = worldViewer.GetWorld().GetPostMgr().GetPostBox(worldViewer.GetPlayerId());
5✔
169
    if(!postBox)
5✔
170
        postBox = &worldViewer.GetWorldNonConst().GetPostMgr().AddPostBox(worldViewer.GetPlayerId());
5✔
171
    RTTR_Assert(postBox != nullptr);
5✔
172
    return *postBox;
5✔
173
}
174

175
dskGameInterface::~dskGameInterface()
5✔
176
{
177
    for(auto& border : borders)
25✔
178
        deletePtr(border);
20✔
179
    GAMECLIENT.RemoveInterface(this);
5✔
180
    LOBBYCLIENT.RemoveListener(this);
5✔
181
}
5✔
182

183
void dskGameInterface::SetActive(bool activate)
17✔
184
{
185
    if(activate == IsActive())
17✔
186
        return;
9✔
187
    if(!activate && isScrolling)
8✔
188
    {
189
        // Stay active if scrolling and no modal window is open
190
        const IngameWindow* wnd = WINDOWMANAGER.GetTopMostWindow();
1✔
191
        if(wnd && wnd->IsModal())
1✔
UNCOV
192
            StopScrolling();
×
193
        else
194
            return;
1✔
195
    }
196
    Desktop::SetActive(activate);
7✔
197
    // Do this here to allow previous screen to keep control
198
    if(activate)
7✔
199
    {
200
        GAMECLIENT.SetInterface(this);
5✔
201
        LOBBYCLIENT.AddListener(this);
5✔
202
        if(!game_->IsStarted())
5✔
203
        {
204
            GAMECLIENT.OnGameStart();
5✔
205

206
            ShowPersistentWindowsAfterSwitch();
5✔
207
        }
208
    }
209
}
210

211
void dskGameInterface::StopScrolling()
6✔
212
{
213
    isScrolling = false;
6✔
214
    WINDOWMANAGER.SetCursor(road.mode == RoadBuildMode::Disabled ? Cursor::Hand : Cursor::Remove);
6✔
215
}
6✔
216

217
void dskGameInterface::StartScrolling(const Position& mousePos)
11✔
218
{
219
    startScrollPt = mousePos;
11✔
220
    isScrolling = true;
11✔
221
    WINDOWMANAGER.SetCursor(Cursor::Scroll);
11✔
222
}
11✔
223

UNCOV
224
void dskGameInterface::ToggleFoW()
×
225
{
226
    DisableFoW(!GAMECLIENT.IsReplayFOWDisabled());
×
UNCOV
227
}
×
228

UNCOV
229
void dskGameInterface::DisableFoW(const bool hideFOW)
×
230
{
UNCOV
231
    GAMECLIENT.SetReplayFOW(hideFOW);
×
232
    // Notify viewer and minimap to recalculate the visibility
233
    worldViewer.RecalcAllColors();
×
234
    minimap.UpdateAll();
×
UNCOV
235
}
×
236

237
void dskGameInterface::ShowPersistentWindowsAfterSwitch()
5✔
238
{
239
    auto& windows = SETTINGS.windows.persistentSettings;
5✔
240

241
    if(windows[CGI_CHAT].isOpen)
5✔
UNCOV
242
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwChat>(this));
×
243
    if(windows[CGI_POSTOFFICE].isOpen)
5✔
UNCOV
244
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwPostWindow>(gwv, GetPostBox()));
×
245
    if(windows[CGI_DISTRIBUTION].isOpen)
5✔
UNCOV
246
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwDistribution>(gwv.GetViewer(), GAMECLIENT));
×
247
    if(windows[CGI_BUILDORDER].isOpen && gwv.GetWorld().GetGGS().isEnabled(AddonId::CUSTOM_BUILD_SEQUENCE))
5✔
UNCOV
248
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwBuildOrder>(gwv.GetViewer()));
×
249
    if(windows[CGI_TRANSPORT].isOpen)
5✔
UNCOV
250
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwTransport>(gwv.GetViewer(), GAMECLIENT));
×
251
    if(windows[CGI_MILITARY].isOpen)
5✔
UNCOV
252
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwMilitary>(gwv.GetViewer(), GAMECLIENT));
×
253
    if(windows[CGI_TOOLS].isOpen)
5✔
UNCOV
254
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwTools>(gwv.GetViewer(), GAMECLIENT));
×
255
    if(windows[CGI_INVENTORY].isOpen)
5✔
UNCOV
256
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwInventory>(gwv.GetViewer().GetPlayer()));
×
257
    if(windows[CGI_MINIMAP].isOpen)
5✔
UNCOV
258
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwMinimap>(minimap, gwv));
×
259
    if(windows[CGI_BUILDINGS].isOpen)
5✔
UNCOV
260
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwBuildings>(gwv, GAMECLIENT));
×
261
    if(windows[CGI_BUILDINGSPRODUCTIVITY].isOpen)
5✔
UNCOV
262
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwBuildingProductivities>(gwv.GetViewer().GetPlayer()));
×
263
    if(windows[CGI_MUSICPLAYER].isOpen)
5✔
UNCOV
264
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwMusicPlayer>());
×
265
    if(windows[CGI_STATISTICS].isOpen)
5✔
UNCOV
266
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwStatistics>(gwv.GetViewer()));
×
267
    if(windows[CGI_ECONOMICPROGRESS].isOpen && gwv.GetWorld().getEconHandler())
5✔
UNCOV
268
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwEconomicProgress>(gwv.GetViewer()));
×
269
    if(windows[CGI_DIPLOMACY].isOpen)
5✔
UNCOV
270
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwDiplomacy>(gwv.GetViewer(), GAMECLIENT));
×
271
    if(windows[CGI_SHIP].isOpen)
5✔
272
        WINDOWMANAGER.ShowAfterSwitch(
×
UNCOV
273
          std::make_unique<iwShip>(gwv, GAMECLIENT, gwv.GetViewer().GetPlayer().GetShipByID(0)));
×
274
    if(windows[CGI_MERCHANDISE_STATISTICS].isOpen)
5✔
UNCOV
275
        WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwMerchandiseStatistics>(gwv.GetViewer().GetPlayer()));
×
276
}
5✔
277

UNCOV
278
void dskGameInterface::Resize(const Extent& newSize)
×
279
{
UNCOV
280
    Window::Resize(newSize);
×
281

282
    // recreate borders
283
    for(auto& border : borders)
×
284
        deletePtr(border);
×
UNCOV
285
    cbb.buildBorder(newSize, borders);
×
286

287
    // move buttons
288
    DrawPoint barPos((newSize.x - LOADER.GetImageN("resource", 29)->getWidth()) / 2 + 44,
×
UNCOV
289
                     newSize.y - LOADER.GetImageN("resource", 29)->getHeight() + 4);
×
290

291
    auto* button = GetCtrl<ctrlButton>(ID_btMap);
×
UNCOV
292
    button->SetPos(barPos);
×
293

294
    barPos.x += button->GetSize().x;
×
295
    button = GetCtrl<ctrlButton>(ID_btOptions);
×
UNCOV
296
    button->SetPos(barPos);
×
297

298
    barPos.x += button->GetSize().x;
×
299
    button = GetCtrl<ctrlButton>(ID_btConstructionAid);
×
UNCOV
300
    button->SetPos(barPos);
×
301

302
    barPos.x += button->GetSize().x;
×
303
    button = GetCtrl<ctrlButton>(ID_btPost);
×
UNCOV
304
    button->SetPos(barPos);
×
305

306
    barPos += DrawPoint(18, 24);
×
307
    auto* text = GetCtrl<ctrlText>(ID_txtNumMsg);
×
UNCOV
308
    text->SetPos(barPos);
×
309

310
    gwv.Resize(newSize);
×
UNCOV
311
}
×
312

UNCOV
313
void dskGameInterface::Msg_ButtonClick(const unsigned ctrl_id)
×
314
{
UNCOV
315
    switch(ctrl_id)
×
316
    {
317
        case ID_btMap: WINDOWMANAGER.ToggleWindow(std::make_unique<iwMinimap>(minimap, gwv)); break;
×
318
        case ID_btOptions: WINDOWMANAGER.ToggleWindow(std::make_unique<iwMainMenu>(gwv, GAMECLIENT)); break;
×
319
        case ID_btConstructionAid:
×
320
            if(WINDOWMANAGER.IsDesktopActive())
×
321
                gwv.ToggleShowBQ();
×
322
            break;
×
323
        case ID_btPost:
×
324
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwPostWindow>(gwv, GetPostBox()));
×
325
            UpdatePostIcon(GetPostBox().GetNumMsgs(), false);
×
UNCOV
326
            break;
×
327
    }
UNCOV
328
}
×
329

UNCOV
330
void dskGameInterface::Msg_PaintBefore()
×
331
{
UNCOV
332
    Desktop::Msg_PaintBefore();
×
333

334
    // Spiel ausführen
UNCOV
335
    Run();
×
336

337
    /// Padding of the figures
338
    const DrawPoint figPadding(12, 12);
×
UNCOV
339
    const DrawPoint screenSize(VIDEODRIVER.GetRenderSize());
×
340
    // Rahmen zeichnen
341
    borders[0]->DrawFull(DrawPoint(0, 0));                                      // oben (mit Ecken)
×
342
    borders[1]->DrawFull(DrawPoint(0, screenSize.y - figPadding.y));            // unten (mit Ecken)
×
343
    borders[2]->DrawFull(DrawPoint(0, figPadding.y));                           // links
×
UNCOV
344
    borders[3]->DrawFull(DrawPoint(screenSize.x - figPadding.x, figPadding.y)); // rechts
×
345

346
    // The figure/statues and the button bar
347
    glArchivItem_Bitmap& imgFigLeftTop = *LOADER.GetImageN("resource", 17);
×
348
    glArchivItem_Bitmap& imgFigRightTop = *LOADER.GetImageN("resource", 18);
×
349
    glArchivItem_Bitmap& imgFigLeftBot = *LOADER.GetImageN("resource", 19);
×
350
    glArchivItem_Bitmap& imgFigRightBot = *LOADER.GetImageN("resource", 20);
×
351
    imgFigLeftTop.DrawFull(figPadding);
×
352
    imgFigRightTop.DrawFull(DrawPoint(screenSize.x - figPadding.x - imgFigRightTop.getWidth(), figPadding.y));
×
353
    imgFigLeftBot.DrawFull(DrawPoint(figPadding.x, screenSize.y - figPadding.y - imgFigLeftBot.getHeight()));
×
UNCOV
354
    imgFigRightBot.DrawFull(screenSize - figPadding - imgFigRightBot.GetSize());
×
355

356
    glArchivItem_Bitmap& imgButtonBar = *LOADER.GetImageN("resource", 29);
×
357
    imgButtonBar.DrawFull(
×
358
      DrawPoint((screenSize.x - imgButtonBar.getWidth()) / 2, screenSize.y - imgButtonBar.getHeight()));
×
UNCOV
359
}
×
360

UNCOV
361
void dskGameInterface::Msg_PaintAfter()
×
362
{
UNCOV
363
    Desktop::Msg_PaintAfter();
×
364

UNCOV
365
    const GameWorldBase& world = worldViewer.GetWorld();
×
366

UNCOV
367
    if(SETTINGS.global.showGFInfo)
×
368
    {
369
        std::array<char, 256> nwf_string;
UNCOV
370
        if(GAMECLIENT.IsReplayModeOn())
×
371
        {
UNCOV
372
            snprintf(nwf_string.data(), nwf_string.size(),
×
373
                     _("(Replay-Mode) Current GF: %u (End at: %u) / GF length: %u ms / NWF length: %u gf (%u ms)"),
374
                     world.GetEvMgr().GetCurrentGF(), GAMECLIENT.GetLastReplayGF(),
×
375
                     GAMECLIENT.GetGFLength() / FramesInfo::milliseconds32_t(1), GAMECLIENT.GetNWFLength(),
×
UNCOV
376
                     GAMECLIENT.GetNWFLength() * GAMECLIENT.GetGFLength() / FramesInfo::milliseconds32_t(1));
×
377
        } else
UNCOV
378
            snprintf(nwf_string.data(), nwf_string.size(),
×
379
                     _("Current GF: %u / GF length: %u ms / NWF length: %u gf (%u ms) /  Ping: %u ms"),
380
                     world.GetEvMgr().GetCurrentGF(), GAMECLIENT.GetGFLength() / FramesInfo::milliseconds32_t(1),
×
381
                     GAMECLIENT.GetNWFLength(),
×
382
                     GAMECLIENT.GetNWFLength() * GAMECLIENT.GetGFLength() / FramesInfo::milliseconds32_t(1),
×
383
                     worldViewer.GetPlayer().ping);
×
UNCOV
384
        NormalFont->Draw(DrawPoint(30, 1), nwf_string.data(), FontStyle{}, COLOR_YELLOW);
×
385
    }
386

387
    // tournament mode?
388
    const unsigned tournamentDuration = GAMECLIENT.GetTournamentModeDuration();
×
UNCOV
389
    if(tournamentDuration)
×
390
    {
391
        unsigned curGF = world.GetEvMgr().GetCurrentGF();
×
392
        std::string tournamentNotice;
×
393
        if(curGF >= tournamentDuration)
×
UNCOV
394
            tournamentNotice = _("Tournament finished");
×
395
        else
396
        {
397
            tournamentNotice =
UNCOV
398
              helpers::format("Tournament mode: %1% remaining", GAMECLIENT.FormatGFTime(tournamentDuration - curGF));
×
399
        }
UNCOV
400
        NormalFont->Draw(DrawPoint(VIDEODRIVER.GetRenderSize().x - 30, 1), tournamentNotice, FontStyle::AlignH::RIGHT,
×
401
                         COLOR_YELLOW);
402
    }
403

404
    // Replaydateianzeige in der linken unteren Ecke
UNCOV
405
    if(GAMECLIENT.IsReplayModeOn())
×
406
    {
UNCOV
407
        NormalFont->Draw(DrawPoint(0, VIDEODRIVER.GetRenderSize().y), GAMECLIENT.GetReplayFilename().string(),
×
408
                         FontStyle::BOTTOM, COLOR_YELLOW);
409
    } else
410
    {
411
        // Laggende Spieler anzeigen in Form von Schnecken
412
        DrawPoint snailPos(VIDEODRIVER.GetRenderSize().x - 70, 35);
×
UNCOV
413
        for(const NWFPlayerInfo& player : nwfInfo_->getPlayerInfos())
×
414
        {
UNCOV
415
            if(player.isLagging)
×
416
            {
417
                LOADER.GetPlayerImage("rttr", 0)->DrawFull(Rect(snailPos, 30, 30), COLOR_WHITE,
×
418
                                                           game_->world_.GetPlayer(player.id).color);
×
UNCOV
419
                snailPos.x -= 40;
×
420
            }
421
        }
422
    }
423

424
    // Show icons in the upper right corner of the game interface
UNCOV
425
    DrawPoint iconPos(VIDEODRIVER.GetRenderSize().x - 56, 32);
×
426

427
    // Draw cheating indicator icon (WINTER)
UNCOV
428
    if(cheats_.isCheatModeOn())
×
429
    {
430
        glArchivItem_Bitmap* cheatingImg = LOADER.GetImageN("io", 75);
×
431
        cheatingImg->DrawFull(iconPos);
×
UNCOV
432
        iconPos -= DrawPoint(cheatingImg->getWidth() + 6, 0);
×
433
    }
434

435
    // Draw speed indicator icon
436
    const int speedStep =
UNCOV
437
      static_cast<int>(SPEED_GF_LENGTHS[referenceSpeed] / 10ms) - static_cast<int>(GAMECLIENT.GetGFLength() / 10ms);
×
438

UNCOV
439
    if(speedStep != 0)
×
440
    {
UNCOV
441
        glArchivItem_Bitmap* runnerImg = LOADER.GetImageN("io", 164);
×
442

UNCOV
443
        runnerImg->DrawFull(iconPos);
×
444

UNCOV
445
        if(speedStep != 1)
×
446
        {
447
            std::string multiplier = helpers::toString(std::abs(speedStep));
×
UNCOV
448
            NormalFont->Draw(iconPos - runnerImg->GetOrigin() + DrawPoint(19, 6), multiplier, FontStyle::LEFT,
×
449
                             speedStep > 0 ? COLOR_YELLOW : COLOR_RED);
450
        }
UNCOV
451
        iconPos -= DrawPoint(runnerImg->getWidth() + 4, 0);
×
452
    }
453

454
    // Draw zoom level indicator icon
UNCOV
455
    if(gwv.GetCurrentTargetZoomFactor() != 1.f) //-V550
×
456
    {
UNCOV
457
        glArchivItem_Bitmap* magnifierImg = LOADER.GetImageN("io", 36);
×
458

UNCOV
459
        magnifierImg->DrawFull(iconPos);
×
460

461
        std::string zoom_percent = helpers::toString((int)(gwv.GetCurrentTargetZoomFactor() * 100)) + "%";
×
UNCOV
462
        NormalFont->Draw(iconPos - magnifierImg->GetOrigin() + DrawPoint(9, 7), zoom_percent, FontStyle::CENTER,
×
463
                         COLOR_YELLOW);
UNCOV
464
        iconPos -= DrawPoint(magnifierImg->getWidth() + 4, 0);
×
465
    }
UNCOV
466
}
×
467

468
bool dskGameInterface::Msg_LeftDown(const MouseCoords& mc)
3✔
469
{
470
    DrawPoint btOrig(VIDEODRIVER.GetRenderSize().x / 2 - LOADER.GetImageN("resource", 29)->getWidth() / 2 + 44,
6✔
471
                     VIDEODRIVER.GetRenderSize().y - LOADER.GetImageN("resource", 29)->getHeight() + 4);
9✔
472
    Extent btSize = Extent(37, 32) * 4u;
3✔
473
    if(IsPointInRect(mc.GetPos(), Rect(btOrig, btSize)))
3✔
UNCOV
474
        return false;
×
475

476
    // Start scrolling also on Ctrl + left click
477
    if(VIDEODRIVER.GetModKeyState().ctrl)
3✔
478
    {
479
        Msg_RightDown(mc);
1✔
480
        return true;
1✔
481
    } else if(isScrolling)
2✔
482
        StopScrolling();
2✔
483

484
    // Unterscheiden je nachdem Straäcnbaumodus an oder aus ist
485
    if(road.mode != RoadBuildMode::Disabled)
2✔
486
    {
487
        // in "richtige" Map-Koordinaten Konvertieren, den aktuellen selektierten Punkt
488
        const MapPoint selPt = gwv.GetSelectedPt();
1✔
489

490
        if(selPt == road.point)
1✔
491
        {
492
            // Selektierter Punkt ist der gleiche wie der Straßenpunkt --> Fenster mit Wegbau abbrechen
UNCOV
493
            ShowRoadWindow(mc.GetPos());
×
494
        } else
495
        {
496
            // altes Roadwindow schließen
497
            WINDOWMANAGER.Close((unsigned)CGI_ROADWINDOW);
1✔
498

499
            // Ist das ein gültiger neuer Wegpunkt?
500
            if(worldViewer.IsRoadAvailable(road.mode == RoadBuildMode::Boat, selPt)
1✔
501
               && worldViewer.IsPlayerTerritory(selPt))
1✔
502
            {
503
                MapPoint targetPt = selPt;
1✔
504
                if(!BuildRoadPart(targetPt))
1✔
505
                    ShowRoadWindow(mc.GetPos());
×
UNCOV
506
            } else if(worldViewer.GetBQ(selPt) != BuildingQuality::Nothing)
×
507
            {
508
                // Wurde bereits auf das gebaute Stück geklickt?
509
                unsigned idOnRoad = GetIdInCurBuildRoad(selPt);
×
510
                if(idOnRoad)
×
UNCOV
511
                    DemolishRoad(idOnRoad);
×
512
                else
513
                {
514
                    MapPoint targetPt = selPt;
×
UNCOV
515
                    if(BuildRoadPart(targetPt))
×
516
                    {
517
                        // Ist der Zielpunkt der gleiche geblieben?
518
                        if(selPt == targetPt)
×
519
                            GI_BuildRoad();
×
520
                    } else if(selPt == targetPt)
×
UNCOV
521
                        ShowRoadWindow(mc.GetPos());
×
522
                }
523
            }
524
            // Wurde auf eine Flagge geklickt und ist diese Flagge nicht der Weganfangspunkt?
UNCOV
525
            else if(worldViewer.GetWorld().GetNO(selPt)->GetType() == NodalObjectType::Flag && selPt != road.start)
×
526
            {
527
                MapPoint targetPt = selPt;
×
UNCOV
528
                if(BuildRoadPart(targetPt))
×
529
                {
530
                    if(selPt == targetPt)
×
531
                        GI_BuildRoad();
×
532
                } else if(selPt == targetPt)
×
UNCOV
533
                    ShowRoadWindow(mc.GetPos());
×
534
            } else
535
            {
UNCOV
536
                unsigned tbr = GetIdInCurBuildRoad(selPt);
×
537
                // Wurde bereits auf das gebaute Stück geklickt?
538
                if(tbr)
×
UNCOV
539
                    DemolishRoad(tbr);
×
540
                else
UNCOV
541
                    ShowRoadWindow(mc.GetPos());
×
542
            }
543
        }
544
    } else
545
    {
546
        bool enable_military_buildings = false;
1✔
547

548
        iwAction::Tabs action_tabs;
1✔
549

550
        const MapPoint cSel = gwv.GetSelectedPt();
1✔
551

552
        // Vielleicht steht hier auch ein Schiff?
553
        if(const noShip* ship = worldViewer.GetShip(cSel))
1✔
554
        {
555
            WINDOWMANAGER.Show(std::make_unique<iwShip>(gwv, GAMECLIENT, ship));
×
UNCOV
556
            return true;
×
557
        }
558

559
        // Evtl ists nen Haus? (unser Haus)
560
        const noBase& selObj = *worldViewer.GetWorld().GetNO(cSel);
1✔
561
        if(selObj.GetType() == NodalObjectType::Building && worldViewer.IsOwner(cSel))
1✔
562
        {
UNCOV
563
            if(auto* wnd = WINDOWMANAGER.FindNonModalWindow(CGI_BUILDING + MapBase::CreateGUIID(cSel)))
×
564
            {
565
                WINDOWMANAGER.SetActiveWindow(*wnd);
×
UNCOV
566
                return true;
×
567
            }
UNCOV
568
            BuildingType bt = static_cast<const noBuilding&>(selObj).GetBuildingType();
×
569
            // HQ
570
            if(bt == BuildingType::Headquarters)
×
571
                WINDOWMANAGER.Show(
×
UNCOV
572
                  std::make_unique<iwHQ>(gwv, GAMECLIENT, worldViewer.GetWorldNonConst().GetSpecObj<nobHQ>(cSel)));
×
573
            // Lagerhäuser
574
            else if(bt == BuildingType::Storehouse)
×
575
                WINDOWMANAGER.Show(std::make_unique<iwBaseWarehouse>(
×
UNCOV
576
                  gwv, GAMECLIENT, worldViewer.GetWorldNonConst().GetSpecObj<nobStorehouse>(cSel)));
×
577
            // Hafengebäude
578
            else if(bt == BuildingType::HarborBuilding)
×
579
                WINDOWMANAGER.Show(std::make_unique<iwHarborBuilding>(
×
UNCOV
580
                  gwv, GAMECLIENT, worldViewer.GetWorldNonConst().GetSpecObj<nobHarborBuilding>(cSel)));
×
581
            // Militärgebäude
582
            else if(BuildingProperties::IsMilitary(bt))
×
583
                WINDOWMANAGER.Show(std::make_unique<iwMilitaryBuilding>(
×
584
                  gwv, GAMECLIENT, worldViewer.GetWorldNonConst().GetSpecObj<nobMilitary>(cSel)));
×
585
            else if(bt == BuildingType::Temple)
×
586
                WINDOWMANAGER.Show(std::make_unique<iwTempleBuilding>(
×
UNCOV
587
                  gwv, GAMECLIENT, worldViewer.GetWorldNonConst().GetSpecObj<nobTemple>(cSel)));
×
588
            else
589
                WINDOWMANAGER.Show(std::make_unique<iwBuilding>(
×
590
                  gwv, GAMECLIENT, worldViewer.GetWorldNonConst().GetSpecObj<nobUsual>(cSel)));
×
UNCOV
591
            return true;
×
592
        }
593
        // oder vielleicht eine Baustelle?
594
        else if(selObj.GetType() == NodalObjectType::Buildingsite && worldViewer.IsOwner(cSel))
1✔
595
        {
596
            if(!WINDOWMANAGER.FindNonModalWindow(CGI_BUILDING + MapBase::CreateGUIID(cSel)))
×
597
                WINDOWMANAGER.Show(
×
598
                  std::make_unique<iwBuildingSite>(gwv, worldViewer.GetWorld().GetSpecObj<noBuildingSite>(cSel)));
×
UNCOV
599
            return true;
×
600
        }
601

602
        action_tabs.watch = true;
1✔
603
        // Unser Land
604
        if(worldViewer.IsOwner(cSel))
1✔
605
        {
606
            const BuildingQuality bq = worldViewer.GetBQ(cSel);
1✔
607
            // Kann hier was gebaut werden?
608
            if(bq >= BuildingQuality::Mine)
1✔
609
            {
610
                action_tabs.build = true;
1✔
611

612
                // Welches Gebäude kann gebaut werden?
613
                switch(bq)
1✔
614
                {
615
                    case BuildingQuality::Mine: action_tabs.build_tabs = iwAction::BuildTab::Mine; break;
×
616
                    case BuildingQuality::Hut: action_tabs.build_tabs = iwAction::BuildTab::Hut; break;
×
UNCOV
617
                    case BuildingQuality::House: action_tabs.build_tabs = iwAction::BuildTab::House; break;
×
618
                    case BuildingQuality::Castle: action_tabs.build_tabs = iwAction::BuildTab::Castle; break;
1✔
619
                    case BuildingQuality::Harbor: action_tabs.build_tabs = iwAction::BuildTab::Harbor; break;
×
UNCOV
620
                    default: break;
×
621
                }
622

623
                if(!worldViewer.GetWorld().IsFlagAround(cSel))
1✔
624
                    action_tabs.setflag = true;
1✔
625

626
                // Prüfen, ob sich Militärgebäude in der Nähe befinden, wenn nein, können auch eigene
627
                // Militärgebäude gebaut werden
628
                enable_military_buildings =
1✔
629
                  !worldViewer.GetWorld().IsMilitaryBuildingNearNode(cSel, worldViewer.GetPlayerId());
1✔
630
            } else if(bq == BuildingQuality::Flag)
×
631
                action_tabs.setflag = true;
×
632
            else if(selObj.GetType() == NodalObjectType::Flag)
×
UNCOV
633
                action_tabs.flag = true;
×
634

635
            if(selObj.GetType() != NodalObjectType::Flag && selObj.GetType() != NodalObjectType::Building)
1✔
636
            {
637
                // Check if there are roads
638
                for(const Direction dir : helpers::EnumRange<Direction>{})
16✔
639
                {
640
                    const PointRoad curRoad = worldViewer.GetVisiblePointRoad(cSel, dir);
6✔
641
                    if(curRoad != PointRoad::None)
6✔
642
                    {
643
                        action_tabs.cutroad = true;
×
UNCOV
644
                        action_tabs.upgradeRoad |= (curRoad == PointRoad::Normal);
×
645
                    }
646
                }
647
            }
648
        }
649
        // evtl ists ein feindliches Militärgebäude, welches NICHT im Nebel liegt?
UNCOV
650
        else if(worldViewer.GetVisibility(cSel) == Visibility::Visible)
×
651
        {
UNCOV
652
            if(selObj.GetType() == NodalObjectType::Building)
×
653
            {
654
                const auto* building = worldViewer.GetWorld().GetSpecObj<noBuilding>(cSel); //-V807
×
UNCOV
655
                BuildingType bt = building->GetBuildingType();
×
656

657
                // Only if trade is enabled
UNCOV
658
                if(worldViewer.GetWorld().GetGGS().isEnabled(AddonId::TRADE))
×
659
                {
660
                    // Allied warehouse? -> Show trade window
UNCOV
661
                    if(BuildingProperties::IsWareHouse(bt) && worldViewer.GetPlayer().IsAlly(building->GetPlayer()))
×
662
                    {
663
                        WINDOWMANAGER.Show(std::make_unique<iwTrade>(*static_cast<const nobBaseWarehouse*>(building),
×
664
                                                                     worldViewer, GAMECLIENT));
×
UNCOV
665
                        return true;
×
666
                    }
667
                }
668

669
                // Ist es ein gewöhnliches Militärgebäude?
UNCOV
670
                if(BuildingProperties::IsMilitary(bt))
×
671
                {
672
                    // Dann darf es nicht neu gebaut sein!
673
                    if(!static_cast<const nobMilitary*>(building)->IsNewBuilt())
×
UNCOV
674
                        action_tabs.attack = true;
×
675
                }
676
                // oder ein HQ oder Hafen?
677
                else if(bt == BuildingType::Headquarters || bt == BuildingType::HarborBuilding)
×
678
                    action_tabs.attack = true;
×
679
                action_tabs.sea_attack =
×
UNCOV
680
                  action_tabs.attack && worldViewer.GetWorld().GetGGS().isEnabled(AddonId::SEA_ATTACK);
×
681
            }
682
        }
683

684
        // Bisheriges Actionfenster schließen, falls es eins gab
685
        // aktuelle Mausposition merken, da diese durch das Schließen verändert werden kann
686
        if(actionwindow)
1✔
UNCOV
687
            actionwindow->Close();
×
688
        VIDEODRIVER.SetMousePos(mc.GetPos());
1✔
689

690
        ShowActionWindow(action_tabs, cSel, mc.GetPos(), enable_military_buildings);
1✔
691
    }
692

693
    return true;
2✔
694
}
695

696
bool dskGameInterface::Msg_LeftUp(const MouseCoords&)
2✔
697
{
698
    if(isScrolling)
2✔
699
    {
700
        StopScrolling();
1✔
701
        return true;
1✔
702
    }
703
    return false;
1✔
704
}
705

706
bool dskGameInterface::Msg_MouseMove(const MouseCoords& mc)
20✔
707
{
708
    if(!isScrolling)
20✔
709
        return false;
12✔
710

711
    int acceleration = SETTINGS.global.smartCursor ? 2 : 3;
8✔
712

713
    if(SETTINGS.interface.invertMouse)
8✔
714
        acceleration = -acceleration;
1✔
715

716
    gwv.MoveBy((mc.GetPos() - startScrollPt) * acceleration);
8✔
717
    VIDEODRIVER.SetMousePos(startScrollPt);
8✔
718

719
    if(!SETTINGS.global.smartCursor)
8✔
UNCOV
720
        startScrollPt = mc.GetPos();
×
721
    return true;
8✔
722
}
723

724
bool dskGameInterface::Msg_RightDown(const MouseCoords& mc)
11✔
725
{
726
    StartScrolling(mc.pos);
11✔
727
    return true;
11✔
728
}
729

730
bool dskGameInterface::Msg_RightUp(const MouseCoords& /*mc*/) //-V524
3✔
731
{
732
    if(isScrolling)
3✔
733
        StopScrolling();
3✔
734
    return false;
3✔
735
}
736

737
/**
738
 *  Druck von Spezialtasten auswerten.
739
 */
740
bool dskGameInterface::Msg_KeyDown(const KeyEvent& ke)
2✔
741
{
742
    cheatCommandTracker_.onKeyEvent(ke);
2✔
743

744
    switch(ke.kt)
2✔
745
    {
746
        default: break;
2✔
747
        case KeyType::Return: // Chatfenster öffnen
×
748
            WINDOWMANAGER.Show(std::make_unique<iwChat>(this));
×
UNCOV
749
            return true;
×
750

751
        case KeyType::Space: // Bauqualitäten anzeigen
×
752
            gwv.ToggleShowBQ();
×
UNCOV
753
            return true;
×
754

755
        case KeyType::Left: // Nach Links Scrollen
×
756
            gwv.MoveBy({-30, 0});
×
757
            return true;
×
758
        case KeyType::Right: // Nach Rechts Scrollen
×
759
            gwv.MoveBy({30, 0});
×
760
            return true;
×
761
        case KeyType::Up: // Nach Oben Scrollen
×
762
            gwv.MoveBy({0, -30});
×
763
            return true;
×
764
        case KeyType::Down: // Nach Unten Scrollen
×
765
            gwv.MoveBy({0, 30});
×
UNCOV
766
            return true;
×
767

768
        case KeyType::F2: // Spiel speichern
×
769
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwSave>());
×
770
            return true;
×
UNCOV
771
        case KeyType::F3: // Map debug window/ Multiplayer coordinates
×
772
        {
773
            const bool replayMode = GAMECLIENT.IsReplayModeOn();
×
774
            if(replayMode)
×
775
                DisableFoW(true);
×
776
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwMapDebug>(gwv, game_->world_.IsSinglePlayer() || replayMode));
×
UNCOV
777
            return true;
×
778
        }
779
        case KeyType::F8: // Tastaturbelegung
×
780
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwTextfile>("keyboardlayout.txt", _("Keyboard layout")));
×
781
            return true;
×
782
        case KeyType::F9: // Readme
×
783
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwTextfile>("readme.txt", _("Readme!")));
×
784
            return true;
×
UNCOV
785
        case KeyType::F11: // Music player (midi files)
×
786
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwMusicPlayer>());
×
787
            return true;
×
788
        case KeyType::F12: // Optionsfenster
×
789
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwOptionsWindow>(gwv.GetSoundMgr()));
×
UNCOV
790
            return true;
×
791
    }
792

793
    switch(ke.c)
2✔
794
    {
795
        case '+':
×
796
            if(GAMECLIENT.IsReplayModeOn() || game_->world_.IsSinglePlayer())
×
797
                GAMECLIENT.IncreaseSpeed();
×
798
            return true;
×
799
        case '-':
×
800
            if(GAMECLIENT.IsReplayModeOn() || game_->world_.IsSinglePlayer())
×
801
                GAMECLIENT.DecreaseSpeed();
×
UNCOV
802
            return true;
×
803

UNCOV
804
        case '1':
×
805
        case '2':
806
        case '3': // Spieler umschalten
807
        case '4':
808
        case '5':
809
        case '6':
810
        case '7':
811
        case '8':
812
        {
813
            unsigned playerIdx = ke.c - '1';
×
UNCOV
814
            if(GAMECLIENT.IsReplayModeOn())
×
815
            {
816
                unsigned oldPlayerId = worldViewer.GetPlayerId();
×
817
                GAMECLIENT.ChangePlayerIngame(worldViewer.GetPlayerId(), playerIdx);
×
818
                RTTR_Assert(worldViewer.GetPlayerId() == oldPlayerId || worldViewer.GetPlayerId() == playerIdx);
×
UNCOV
819
            } else if(playerIdx < worldViewer.GetWorld().GetNumPlayers())
×
820
            {
821
                // On mutiplayer this currently asyncs, but as this is a debug feature anyway just disable it there.
822
                // If this should be enabled again, look into the handling/clearing of accumulated GCs
UNCOV
823
                if(game_->world_.IsSinglePlayer())
×
824
                {
825
                    const GamePlayer& player = worldViewer.GetWorld().GetPlayer(playerIdx);
×
826
                    if(player.ps == PlayerState::AI && player.aiInfo.type == AI::Type::Dummy)
×
UNCOV
827
                        GAMECLIENT.RequestSwapToPlayer(playerIdx);
×
828
                }
829
            }
UNCOV
830
            return true;
×
831
        }
832

833
        case 'b': // Zur lezten Position zurückspringen
×
834
            gwv.MoveToLastPosition();
×
835
            return true;
×
836
        case 'v':
×
837
            if(game_->world_.IsSinglePlayer())
×
838
                GAMECLIENT.IncreaseSpeed();
×
839
            return true;
×
840
        case 'c': // Gebäudenamen anzeigen
×
841
            gwv.ToggleShowNames();
×
842
            return true;
×
843
        case 'd': // Replay: FoW an/ausschalten
×
844
            ToggleFoW();
×
845
            return true;
×
UNCOV
846
        case 'h': // Zum HQ springen
×
847
        {
UNCOV
848
            const GamePlayer& player = worldViewer.GetPlayer();
×
849
            // Prüfen, ob dieses überhaupt noch existiert
850
            if(player.GetHQPos().isValid())
×
UNCOV
851
                gwv.MoveToMapPt(player.GetHQPos());
×
852
        }
853
            return true;
×
854
        case 'i': // Show inventory
×
855
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwInventory>(worldViewer.GetPlayer()));
×
856
            return true;
×
857
        case 'j': // GFs überspringen
×
858
            if(game_->world_.IsSinglePlayer() || GAMECLIENT.IsReplayModeOn())
×
859
                WINDOWMANAGER.ToggleWindow(std::make_unique<iwSkipGFs>(gwv));
×
860
            return true;
×
861
        case 'l': // Minimap anzeigen
×
862
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwMinimap>(minimap, gwv));
×
UNCOV
863
            return true;
×
864
        case 'm': // Hauptauswahl
2✔
865
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwMainMenu>(gwv, GAMECLIENT));
2✔
866
            return true;
2✔
867
        case 'n': // Show Post window
×
868
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwPostWindow>(gwv, GetPostBox()));
×
869
            UpdatePostIcon(GetPostBox().GetNumMsgs(), false);
×
870
            return true;
×
871
        case 'p': // Pause
×
872
            GAMECLIENT.TogglePause();
×
873
            return true;
×
874
        case 'q': // Spiel verlassen
×
875
            if(ke.alt)
×
876
                WINDOWMANAGER.ToggleWindow(std::make_unique<iwEndgame>());
×
877
            return true;
×
878
        case 's': // Produktivität anzeigen
×
879
            gwv.ToggleShowProductivity();
×
880
            return true;
×
881
        case 26: // ctrl+z
×
882
            gwv.SetZoomFactor(ZOOM_FACTORS[ZOOM_DEFAULT_INDEX]);
×
883
            return true;
×
884
        case 'z': // zoom
×
885
            if(++zoomLvl >= ZOOM_FACTORS.size())
×
UNCOV
886
                zoomLvl = 0;
×
887

888
            gwv.SetZoomFactor(ZOOM_FACTORS[zoomLvl]);
×
889
            return true;
×
890
        case 'Z': // shift-z, reverse zoom
×
891
            if(zoomLvl == 0)
×
UNCOV
892
                zoomLvl = ZOOM_FACTORS.size() - 1;
×
893
            else
UNCOV
894
                zoomLvl--;
×
895

896
            gwv.SetZoomFactor(ZOOM_FACTORS[zoomLvl]);
×
UNCOV
897
            return true;
×
898
    }
899

UNCOV
900
    return false;
×
901
}
902

UNCOV
903
bool dskGameInterface::Msg_WheelUp(const MouseCoords&)
×
904
{
905
    WheelZoom(ZOOM_WHEEL_INCREMENT);
×
UNCOV
906
    return true;
×
907
}
UNCOV
908
bool dskGameInterface::Msg_WheelDown(const MouseCoords&)
×
909
{
910
    WheelZoom(-ZOOM_WHEEL_INCREMENT);
×
UNCOV
911
    return true;
×
912
}
913

UNCOV
914
void dskGameInterface::WheelZoom(float step)
×
915
{
916
    float new_zoom = gwv.GetCurrentTargetZoomFactor() * (1 + step);
×
UNCOV
917
    gwv.SetZoomFactor(new_zoom);
×
918

919
    // also keep track in terms of fixed defined zoom levels
920
    zoomLvl = ZOOM_DEFAULT_INDEX;
×
UNCOV
921
    for(size_t i = ZOOM_DEFAULT_INDEX; i < ZOOM_FACTORS.size(); ++i)
×
922
    {
923
        if(ZOOM_FACTORS[i] < new_zoom)
×
UNCOV
924
            zoomLvl = i;
×
925
    }
926

UNCOV
927
    for(size_t i = ZOOM_DEFAULT_INDEX; i-- > 0;)
×
928
    {
929
        if(ZOOM_FACTORS[i] > new_zoom)
×
UNCOV
930
            zoomLvl = i;
×
931
    }
UNCOV
932
}
×
933

UNCOV
934
void dskGameInterface::OnBuildingNote(const BuildingNote& note)
×
935
{
UNCOV
936
    switch(note.type)
×
937
    {
UNCOV
938
        case BuildingNote::Constructed:
×
939
        case BuildingNote::Destroyed:
940
        case BuildingNote::Lost:
941
            // Close the related window as the building does not exist anymore
942
            // In "Constructed" this means the buildingsite
943
            WINDOWMANAGER.Close(CGI_BUILDING + MapBase::CreateGUIID(note.pos));
×
944
            break;
×
UNCOV
945
        default: break;
×
946
    }
UNCOV
947
}
×
948

UNCOV
949
void dskGameInterface::Run()
×
950
{
951
    // Reset draw counter of the trees before drawing
UNCOV
952
    noTree::ResetDrawCounter();
×
953

954
    unsigned water_percent;
955
    // Draw mouse only if not on window
956
    bool drawMouse = WINDOWMANAGER.FindWindowAtPos(VIDEODRIVER.GetMousePos()) == nullptr;
×
UNCOV
957
    gwv.Draw(road, actionwindow != nullptr ? actionwindow->GetSelectedPt() : MapPoint::Invalid(), drawMouse,
×
958
             &water_percent);
959

960
    // Indicate that the game is paused by darkening the screen (dark semi-transparent overlay)
961
    if(GAMECLIENT.IsPaused())
×
UNCOV
962
        DrawRectangle(Rect(DrawPoint(0, 0), VIDEODRIVER.GetRenderSize()), COLOR_SHADOW);
×
963
    else
964
    {
965
        // Play ambient sounds if game is not paused
966
        worldViewer.GetSoundMgr().playOceanBrawling(water_percent);
×
UNCOV
967
        worldViewer.GetSoundMgr().playBirdSounds(noTree::QueryDrawCounter());
×
968
    }
969

970
    messenger.Draw();
×
UNCOV
971
}
×
972

973
void dskGameInterface::GI_StartRoadBuilding(const MapPoint startPt, bool waterRoad)
3✔
974
{
975
    // Im Replay keine Straßen bauen
976
    if(GAMECLIENT.IsReplayModeOn())
3✔
UNCOV
977
        return;
×
978

979
    road.mode = waterRoad ? RoadBuildMode::Boat : RoadBuildMode::Normal;
3✔
980
    road.route.clear();
3✔
981
    road.start = road.point = startPt;
3✔
982
    WINDOWMANAGER.SetCursor(Cursor::Remove);
3✔
983
}
984

985
void dskGameInterface::GI_CancelRoadBuilding()
2✔
986
{
987
    if(road.mode == RoadBuildMode::Disabled)
2✔
UNCOV
988
        return;
×
989
    road.mode = RoadBuildMode::Disabled;
2✔
990
    worldViewer.RemoveVisualRoad(road.start, road.route);
2✔
991
    WINDOWMANAGER.SetCursor(isScrolling ? Cursor::Scroll : Cursor::Hand);
2✔
992
}
993

994
bool dskGameInterface::BuildRoadPart(MapPoint& cSel)
3✔
995
{
996
    std::vector<Direction> new_route =
997
      FindPathForRoad(worldViewer, road.point, cSel, road.mode == RoadBuildMode::Boat, 100);
6✔
998
    // Weg gefunden?
999
    if(new_route.empty())
3✔
UNCOV
1000
        return false;
×
1001

1002
    // Test on water way length
1003
    if(road.mode == RoadBuildMode::Boat)
3✔
1004
    {
UNCOV
1005
        unsigned char index = worldViewer.GetWorld().GetGGS().getSelection(AddonId::MAX_WATERWAY_LENGTH);
×
1006

1007
        RTTR_Assert(index < waterwayLengths.size());
×
UNCOV
1008
        const unsigned max_length = waterwayLengths[index];
×
1009

UNCOV
1010
        unsigned length = road.route.size() + new_route.size();
×
1011

1012
        // max_length == 0 heißt beliebig lang, ansonsten
1013
        // Weg zurechtstutzen.
UNCOV
1014
        if(max_length > 0)
×
1015
        {
UNCOV
1016
            while(length > max_length)
×
1017
            {
1018
                new_route.pop_back();
×
UNCOV
1019
                --length;
×
1020
            }
1021
        }
1022
    }
1023

1024
    // Weg (visuell) bauen
1025
    for(const auto dir : new_route)
22✔
1026
    {
1027
        worldViewer.SetVisiblePointRoad(road.point, dir,
19✔
1028
                                        (road.mode == RoadBuildMode::Boat) ? PointRoad::Boat : PointRoad::Normal);
19✔
1029
        worldViewer.RecalcBQForRoad(road.point);
19✔
1030
        road.point = worldViewer.GetWorld().GetNeighbour(road.point, dir);
19✔
1031
    }
1032
    worldViewer.RecalcBQForRoad(road.point);
3✔
1033

1034
    // Zielpunkt updaten (für Wasserweg)
1035
    cSel = road.point;
3✔
1036

1037
    road.route.insert(road.route.end(), new_route.begin(), new_route.end());
3✔
1038

1039
    return true;
3✔
1040
}
1041

1042
unsigned dskGameInterface::GetIdInCurBuildRoad(const MapPoint pt)
1✔
1043
{
1044
    MapPoint curPt = road.start;
1✔
1045
    for(unsigned i = 0; i < road.route.size(); ++i)
2✔
1046
    {
1047
        if(curPt == pt)
2✔
1048
            return i + 1;
1✔
1049

1050
        curPt = worldViewer.GetNeighbour(curPt, road.route[i]);
1✔
1051
    }
UNCOV
1052
    return 0;
×
1053
}
1054

UNCOV
1055
void dskGameInterface::ShowRoadWindow(const Position& mousePos)
×
1056
{
1057
    roadwindow = &WINDOWMANAGER.Show(
×
1058
      std::make_unique<iwRoadWindow>(*this, worldViewer.GetBQ(road.point) != BuildingQuality::Nothing, mousePos), true);
×
UNCOV
1059
}
×
1060

1061
void dskGameInterface::ShowActionWindow(const iwAction::Tabs& action_tabs, MapPoint cSel, const DrawPoint& mousePos,
2✔
1062
                                        const bool enable_military_buildings)
1063
{
1064
    const GameWorldBase& world = worldViewer.GetWorld();
2✔
1065

1066
    iwAction::Params params;
2✔
1067

1068
    // Sind wir am Wasser?
1069
    if(action_tabs.setflag)
2✔
1070
    {
1071
        auto isWater = [](const auto& desc) { return desc.kind == TerrainKind::Water; };
6✔
1072
        if(world.HasTerrain(cSel, isWater))
1✔
UNCOV
1073
            params = iwAction::FlagType::WaterFlag;
×
1074
    }
1075

1076
    // Wenn es einen Flaggen-Tab gibt, dann den Flaggentyp herausfinden und die Art des Fensters entsprechende setzen
1077
    if(action_tabs.flag)
2✔
1078
    {
1079
        if(world.GetNO(world.GetNeighbour(cSel, Direction::NorthWest))->GetGOT() == GO_Type::NobHq)
×
1080
            params = iwAction::FlagType::HQ;
×
UNCOV
1081
        else if(world.GetNO(cSel)->GetType() == NodalObjectType::Flag)
×
1082
        {
1083
            if(world.GetSpecObj<noFlag>(cSel)->GetFlagType() == FlagType::Water)
×
UNCOV
1084
                params = iwAction::FlagType::WaterFlag;
×
1085
        }
1086
    }
1087

1088
    // Angriffstab muss wissen, wieviel Soldaten maximal entsendet werden können
1089
    if(action_tabs.attack)
2✔
1090
    {
UNCOV
1091
        params = worldViewer.GetNumSoldiersForAttack(cSel);
×
1092
    }
1093

1094
    actionwindow = &WINDOWMANAGER.Show(
2✔
1095
      std::make_unique<iwAction>(*this, gwv, action_tabs, cSel, mousePos, params, enable_military_buildings), true);
2✔
1096
}
2✔
1097

UNCOV
1098
void dskGameInterface::OnChatCommand(const std::string& cmd)
×
1099
{
NEW
1100
    cheatCommandTracker_.onChatCommand(cmd);
×
1101

1102
    if(cmd == "surrender")
×
1103
        GAMECLIENT.Surrender();
×
1104
    else if(cmd == "async")
×
1105
        (void)RANDOM.Rand(RANDOM_CONTEXT2(0), 255);
×
UNCOV
1106
    else if(cmd == "segfault")
×
1107
    {
1108
        char* x = nullptr;
×
1109
        *x = 1; //-V522 // NOLINT
×
UNCOV
1110
    } else if(cmd == "reload")
×
1111
    {
1112
        WorldDescription newDesc;
×
1113
        GameDataLoader gdLoader(newDesc);
×
UNCOV
1114
        if(gdLoader.Load())
×
1115
        {
1116
            const_cast<GameWorld&>(game_->world_).GetDescriptionWriteable() = newDesc;
×
UNCOV
1117
            worldViewer.InitTerrainRenderer();
×
1118
        }
1119
    }
UNCOV
1120
}
×
1121

UNCOV
1122
void dskGameInterface::GI_BuildRoad()
×
1123
{
UNCOV
1124
    if(GAMECLIENT.BuildRoad(road.start, road.mode == RoadBuildMode::Boat, road.route))
×
1125
    {
1126
        road.mode = RoadBuildMode::Disabled;
×
UNCOV
1127
        WINDOWMANAGER.SetCursor(Cursor::Hand);
×
1128
    }
UNCOV
1129
}
×
1130

1131
void dskGameInterface::Msg_WindowClosed(IngameWindow& wnd)
2✔
1132
{
1133
    if(actionwindow == &wnd)
2✔
1134
        actionwindow = nullptr;
1✔
1135
    else if(roadwindow == &wnd)
1✔
UNCOV
1136
        roadwindow = nullptr;
×
1137
}
2✔
1138

UNCOV
1139
void dskGameInterface::GI_FlagDestroyed(const MapPoint pt)
×
1140
{
1141
    // Im Wegbaumodus und haben wir von hier eine Flagge gebaut?
UNCOV
1142
    if(road.mode != RoadBuildMode::Disabled && road.start == pt)
×
1143
    {
UNCOV
1144
        GI_CancelRoadBuilding();
×
1145
    }
1146

1147
    // Evtl Actionfenster schließen, da sich das ja auch auf diese Flagge bezieht
UNCOV
1148
    if(actionwindow)
×
1149
    {
1150
        if(actionwindow->GetSelectedPt() == pt)
×
UNCOV
1151
            actionwindow->Close();
×
1152
    }
UNCOV
1153
}
×
1154

UNCOV
1155
void dskGameInterface::CI_PlayerLeft(const unsigned playerId)
×
1156
{
1157
    // Info-Meldung ausgeben
1158
    std::string text =
1159
      helpers::format(_("Player '%s' left the game!"), worldViewer.GetWorld().GetPlayer(playerId).name);
×
UNCOV
1160
    messenger.AddMessage("", 0, ChatDestination::System, text, COLOR_RED);
×
1161
    // Im Spiel anzeigen, dass die KI das Spiel betreten hat
1162
    text = helpers::format(_("Player '%s' joined the game!"), "KI");
×
1163
    messenger.AddMessage("", 0, ChatDestination::System, text, COLOR_GREEN);
×
UNCOV
1164
}
×
1165

UNCOV
1166
void dskGameInterface::CI_GGSChanged(const GlobalGameSettings& /*ggs*/)
×
1167
{
1168
    // TODO: print what has changed
1169
    const std::string text = helpers::format(_("Note: Game settings changed by the server%s"), "");
×
1170
    messenger.AddMessage("", 0, ChatDestination::System, text);
×
UNCOV
1171
}
×
1172

UNCOV
1173
void dskGameInterface::CI_Chat(const unsigned playerId, const ChatDestination cd, const std::string& msg)
×
1174
{
1175
    messenger.AddMessage(worldViewer.GetWorld().GetPlayer(playerId).name,
×
1176
                         worldViewer.GetWorld().GetPlayer(playerId).color, cd, msg);
×
UNCOV
1177
}
×
1178

UNCOV
1179
void dskGameInterface::CI_Async(const std::string& checksums_list)
×
1180
{
UNCOV
1181
    messenger.AddMessage("", 0, ChatDestination::System,
×
1182
                         _("The Game is not in sync. Checksums of some players don't match."), COLOR_RED);
1183
    messenger.AddMessage("", 0, ChatDestination::System, checksums_list, COLOR_YELLOW);
×
1184
    messenger.AddMessage("", 0, ChatDestination::System, _("A auto-savegame is created..."), COLOR_RED);
×
UNCOV
1185
}
×
1186

UNCOV
1187
void dskGameInterface::CI_ReplayAsync(const std::string& msg)
×
1188
{
1189
    messenger.AddMessage("", 0, ChatDestination::System, msg, COLOR_RED);
×
UNCOV
1190
}
×
1191

UNCOV
1192
void dskGameInterface::CI_ReplayEndReached(const std::string& msg)
×
1193
{
1194
    messenger.AddMessage("", 0, ChatDestination::System, msg, COLOR_BLUE);
×
UNCOV
1195
}
×
1196

UNCOV
1197
void dskGameInterface::CI_GamePaused()
×
1198
{
1199
    messenger.AddMessage(_("SYSTEM"), COLOR_GREY, ChatDestination::System, _("Game was paused."));
×
UNCOV
1200
}
×
1201

UNCOV
1202
void dskGameInterface::CI_GameResumed()
×
1203
{
1204
    messenger.AddMessage(_("SYSTEM"), COLOR_GREY, ChatDestination::System, _("Game was resumed."));
×
UNCOV
1205
}
×
1206

UNCOV
1207
void dskGameInterface::CI_Error(const ClientError ce)
×
1208
{
1209
    messenger.AddMessage("", 0, ChatDestination::System, ClientErrorToStr(ce), COLOR_RED);
×
1210
    GAMECLIENT.SetPause(true);
×
UNCOV
1211
}
×
1212

1213
/**
1214
 *  Status: Verbindung verloren.
1215
 */
UNCOV
1216
void dskGameInterface::LC_Status_ConnectionLost()
×
1217
{
1218
    messenger.AddMessage("", 0, ChatDestination::System, _("Lost connection to lobby!"), COLOR_RED);
×
UNCOV
1219
}
×
1220

1221
/**
1222
 *  (Lobby-)Status: Benutzerdefinierter Fehler
1223
 */
UNCOV
1224
void dskGameInterface::LC_Status_Error(const std::string& error)
×
1225
{
1226
    messenger.AddMessage("", 0, ChatDestination::System, error, COLOR_RED);
×
UNCOV
1227
}
×
1228

UNCOV
1229
void dskGameInterface::CI_PlayersSwapped(const unsigned player1, const unsigned player2)
×
1230
{
1231
    // Meldung anzeigen
1232
    std::string text = "Player '" + worldViewer.GetWorld().GetPlayer(player1).name + "' switched to player '"
×
1233
                       + worldViewer.GetWorld().GetPlayer(player2).name + "'";
×
UNCOV
1234
    messenger.AddMessage("", 0, ChatDestination::System, text, COLOR_YELLOW);
×
1235

1236
    // Sichtbarkeiten und Minimap neu berechnen, wenn wir ein von den beiden Spielern sind
1237
    const unsigned localPlayerId = worldViewer.GetPlayerId();
×
UNCOV
1238
    if(player1 == localPlayerId || player2 == localPlayerId)
×
1239
    {
UNCOV
1240
        worldViewer.ChangePlayer(player1 == localPlayerId ? player2 : player1);
×
1241
        // Set visual settings back to the actual ones
1242
        GAMECLIENT.ResetVisualSettings();
×
1243
        minimap.UpdateAll();
×
UNCOV
1244
        InitPlayer();
×
1245
    }
UNCOV
1246
}
×
1247

1248
/**
1249
 *  Wenn ein Spieler verloren hat
1250
 */
UNCOV
1251
void dskGameInterface::GI_PlayerDefeated(const unsigned playerId)
×
1252
{
1253
    const std::string text =
1254
      helpers::format(_("Player '%s' was defeated!"), worldViewer.GetWorld().GetPlayer(playerId).name);
×
UNCOV
1255
    messenger.AddMessage("", 0, ChatDestination::System, text, COLOR_ORANGE);
×
1256

1257
    /// Lokaler Spieler?
UNCOV
1258
    if(playerId == worldViewer.GetPlayerId())
×
1259
    {
1260
        /// Sichtbarkeiten neu berechnen
UNCOV
1261
        worldViewer.RecalcAllColors();
×
1262
        // Minimap updaten
UNCOV
1263
        minimap.UpdateAll();
×
1264
    }
UNCOV
1265
}
×
1266

UNCOV
1267
void dskGameInterface::GI_UpdateMinimap(const MapPoint pt)
×
1268
{
1269
    // Minimap Bescheid sagen
1270
    minimap.UpdateNode(pt);
×
UNCOV
1271
}
×
1272

UNCOV
1273
void dskGameInterface::GI_UpdateMapVisibility()
×
1274
{
1275
    // recalculate visibility
UNCOV
1276
    worldViewer.RecalcAllColors();
×
1277
    // update minimap
1278
    minimap.UpdateAll();
×
UNCOV
1279
}
×
1280

1281
/**
1282
 *  Bündnisvertrag wurde abgeschlossen oder abgebrochen --> Minimap updaten
1283
 */
UNCOV
1284
void dskGameInterface::GI_TreatyOfAllianceChanged(unsigned playerId)
×
1285
{
1286
    // Nur wenn Team-Sicht aktiviert ist, können sihc die Sichtbarkeiten auch ändern
UNCOV
1287
    if(playerId == worldViewer.GetPlayerId() && worldViewer.GetWorld().GetGGS().teamView)
×
1288
    {
1289
        /// Sichtbarkeiten neu berechnen
UNCOV
1290
        worldViewer.RecalcAllColors();
×
1291
        // Minimap updaten
UNCOV
1292
        minimap.UpdateAll();
×
1293
    }
UNCOV
1294
}
×
1295

1296
/**
1297
 *  Baut Weg zurück von Ende bis zu start_id
1298
 */
1299
void dskGameInterface::DemolishRoad(const unsigned start_id)
1✔
1300
{
1301
    RTTR_Assert(start_id > 0);
1✔
1302
    for(unsigned i = road.route.size(); i >= start_id; --i)
6✔
1303
    {
1304
        MapPoint t = road.point;
5✔
1305
        road.point = worldViewer.GetWorld().GetNeighbour(road.point, road.route[i - 1] + 3u);
5✔
1306
        worldViewer.SetVisiblePointRoad(road.point, road.route[i - 1], PointRoad::None);
5✔
1307
        worldViewer.RecalcBQForRoad(t);
5✔
1308
    }
1309

1310
    road.route.resize(start_id - 1);
1✔
1311
}
1✔
1312

1313
/**
1314
 *  Updatet das Post-Icon mit der Nachrichtenanzahl und der Taube
1315
 */
1316
void dskGameInterface::UpdatePostIcon(const unsigned postmessages_count, bool showPigeon)
5✔
1317
{
1318
    // Taube setzen oder nicht (Post)
1319
    if(postmessages_count == 0 || !showPigeon)
5✔
1320
        GetCtrl<ctrlImageButton>(3)->SetImage(LOADER.GetImageN("io", 62));
10✔
1321
    else
UNCOV
1322
        GetCtrl<ctrlImageButton>(3)->SetImage(LOADER.GetImageN("io", 59));
×
1323

1324
    // und Anzahl der Postnachrichten aktualisieren
1325
    if(postmessages_count > 0)
5✔
1326
    {
UNCOV
1327
        GetCtrl<ctrlText>(ID_txtNumMsg)->SetText(std::to_string(postmessages_count));
×
1328
    } else
1329
        GetCtrl<ctrlText>(ID_txtNumMsg)->SetText("");
5✔
1330
}
5✔
1331

1332
/**
1333
 *  Neue Post-Nachricht eingetroffen
1334
 */
UNCOV
1335
void dskGameInterface::NewPostMessage(const PostMsg& msg, const unsigned msgCt)
×
1336
{
1337
    UpdatePostIcon(msgCt, true);
×
1338
    SoundEffect soundEffect = msg.GetSoundEffect();
×
UNCOV
1339
    switch(soundEffect)
×
1340
    {
1341
        case SoundEffect::Pidgeon: LOADER.GetSoundN("sound", 114)->Play(100, false); break;
×
UNCOV
1342
        case SoundEffect::Fanfare: LOADER.GetSoundN("sound", 110)->Play(100, false);
×
1343
    }
UNCOV
1344
}
×
1345

1346
/**
1347
 *  Es wurde eine Postnachricht vom Spieler gelöscht
1348
 */
UNCOV
1349
void dskGameInterface::PostMessageDeleted(const unsigned msgCt)
×
1350
{
1351
    UpdatePostIcon(msgCt, false);
×
UNCOV
1352
}
×
1353

1354
/**
1355
 *  Ein Spieler hat das Spiel gewonnen.
1356
 */
UNCOV
1357
void dskGameInterface::GI_Winner(const unsigned playerId)
×
1358
{
1359
    const std::string name = worldViewer.GetWorld().GetPlayer(playerId).name;
×
1360
    const std::string text = (boost::format(_("Player '%s' is the winner!")) % name).str();
×
1361
    messenger.AddMessage("", 0, ChatDestination::System, text, COLOR_ORANGE);
×
1362
    WINDOWMANAGER.Show(std::make_unique<iwVictory>(std::vector<std::string>(1, name)));
×
UNCOV
1363
}
×
1364

1365
/**
1366
 *  Ein Team hat das Spiel gewonnen.
1367
 */
UNCOV
1368
void dskGameInterface::GI_TeamWinner(const unsigned playerMask)
×
1369
{
1370
    std::vector<std::string> winners;
×
1371
    const GameWorldBase& world = worldViewer.GetWorld();
×
UNCOV
1372
    for(unsigned i = 0; i < world.GetNumPlayers(); i++)
×
1373
    {
1374
        if(playerMask & (1 << i))
×
UNCOV
1375
            winners.push_back(world.GetPlayer(i).name);
×
1376
    }
1377
    const std::string text =
1378
      (boost::format(_("%1% are the winners!")) % helpers::join(winners, ", ", _(" and "))).str();
×
1379
    messenger.AddMessage("", 0, ChatDestination::System, text, COLOR_ORANGE);
×
1380
    WINDOWMANAGER.Show(std::make_unique<iwVictory>(winners));
×
UNCOV
1381
}
×
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