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

Return-To-The-Roots / s25client / 15662121431

15 Jun 2025 10:14AM UTC coverage: 50.446% (-0.03%) from 50.476%
15662121431

Pull #1534

github

web-flow
Merge 6d324f19c into 19c9b91c8
Pull Request #1534: Portrait support

57 of 160 new or added lines in 17 files covered. (35.63%)

27 existing lines in 6 files now uncovered.

22503 of 44608 relevant lines covered (50.45%)

35416.78 hits per line

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

41.24
/libs/s25main/desktops/dskGameLobby.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 "dskGameLobby.h"
6
#include "GameLobby.h"
7
#include "GameLobbyController.h"
8
#include "ILobbyClient.hpp"
9
#include "JoinPlayerInfo.h"
10
#include "Loader.h"
11
#include "WindowManager.h"
12
#include "animation/BlinkButtonAnim.h"
13
#include "controls/ctrlBaseColor.h"
14
#include "controls/ctrlChat.h"
15
#include "controls/ctrlCheck.h"
16
#include "controls/ctrlComboBox.h"
17
#include "controls/ctrlEdit.h"
18
#include "controls/ctrlGroup.h"
19
#include "controls/ctrlImageButton.h"
20
#include "controls/ctrlOptionGroup.h"
21
#include "controls/ctrlPreviewMinimap.h"
22
#include "controls/ctrlText.h"
23
#include "controls/ctrlTextButton.h"
24
#include "controls/ctrlVarDeepening.h"
25
#include "desktops/dskDirectIP.h"
26
#include "desktops/dskGameLoader.h"
27
#include "desktops/dskLAN.h"
28
#include "desktops/dskLobby.h"
29
#include "desktops/dskSinglePlayer.h"
30
#include "helpers/containerUtils.h"
31
#include "helpers/format.hpp"
32
#include "ingameWindows/iwAddons.h"
33
#include "ingameWindows/iwMsgbox.h"
34
#include "lua/LuaInterfaceSettings.h"
35
#include "network/GameClient.h"
36
#include "ogl/FontStyle.h"
37
#include "gameData/GameConsts.h"
38
#include "gameData/PortraitConsts.h"
39
#include "gameData/const_gui_ids.h"
40
#include "liblobby/LobbyPlayerInfo.h"
41
#include "libsiedler2/ArchivItem_Map.h"
42
#include "libsiedler2/ErrorCodes.h"
43
#include "libsiedler2/prototypen.h"
44
#include "s25util/Log.h"
45
#include "s25util/MyTime.h"
46
#include <memory>
47
#include <mygettext/mygettext.h>
48
#include <set>
49

50
namespace {
51
enum CtrlIds
52
{
53
    ID_btStartGame,
54
    ID_btReturn,
55
    ID_chkLockTeams,
56
    ID_chkSharedView,
57
    ID_chkRandomSpawn,
58
    ID_txtAddons,
59
    ID_btSettings,
60
    ID_txtColPastPlayer,
61
    ID_txtColSwap,
62
    ID_txtColName,
63
    ID_txtColRace,
64
    ID_txtColColor,
65
    ID_txtColTeam,
66
    ID_txtColReady,
67
    ID_txtColPing,
68
    ID_txtGameName,
69
    ID_txtExploration,
70
    ID_cbExploration,
71
    ID_txtGoods,
72
    ID_cbGoods,
73
    ID_txtGoals,
74
    ID_cbGoals,
75
    ID_txtSpeed,
76
    ID_cbSpeed,
77
    ID_txtNoPreview,
78
    ID_txtMapName,
79
    ID_miniMap,
80
    ID_btPlayerState,
81
    ID_btNation,
82
    ID_btPortrait,
83
    ID_btColor,
84
    ID_btTeam,
85
    ID_chkReady,
86
    ID_txtPing,
87
    ID_cbMove,
88
    ID_mbLuaLoadError,
89
    ID_mbLuaVersionError,
90
    ID_mbMapLoadError,
91
    ID_mbError,
92
    ID_mbStartErrror,
93
    ID_mbQuestionEconomy,
94
    ID_mbQuestionPeaceful,
95
    ID_chatGame,
96
    ID_chatLobby,
97
    ID_edtChatMsg,
98
    ID_optChatTab,
99
    ID_btChatGame,
100
    ID_btChatLobby,
101
    ID_grpPlayerStart,                           // up to and including ID_grpPlayerStart + MAX_PLAYERS - 1
102
    ID_btSwap = ID_grpPlayerStart + MAX_PLAYERS, // up to and including ID_btSwap + MAX_PLAYERS - 1
103
};
104
template<typename T>
105
constexpr T nextEnumValue(T value)
×
106
{
107
    return T((rttr::enum_cast(value) + 1) % helpers::NumEnumValues_v<T>);
×
108
}
109
} // namespace
110

111
dskGameLobby::dskGameLobby(ServerType serverType, std::shared_ptr<GameLobby> gameLobby, unsigned playerId,
2✔
112
                           std::unique_ptr<ILobbyClient> lobbyClient)
2✔
113
    : Desktop(LOADER.GetImageN("setup015", 0)), serverType(serverType), gameLobby_(std::move(gameLobby)),
2✔
114
      localPlayerId_(playerId), lobbyClient_(std::move(lobbyClient)), hasCountdown_(false), wasActivated(false),
2✔
115
      gameChat(nullptr), lobbyChat(nullptr), lobbyChatTabAnimId(0), localChatTabAnimId(0)
4✔
116
{
117
    // If no lobby don't do anything else
118
    if(!gameLobby_)
2✔
119
        return;
1✔
120

121
    const bool loadLua = !GAMECLIENT.GetLuaFilePath().empty();
1✔
122

123
    // The lobby controller for clients is only used by lua
124
    if(gameLobby_->isHost() || loadLua)
1✔
125
        lobbyController = std::make_unique<GameLobbyController>(gameLobby_, GAMECLIENT.GetMainPlayer());
1✔
126

127
    if(loadLua)
1✔
128
    {
129
        lua = std::make_unique<LuaInterfaceSettings>(*lobbyController, GAMECLIENT);
×
130
        if(!lua->loadScript(GAMECLIENT.GetLuaFilePath()))
×
131
        {
132
            WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwMsgbox>(
×
133
              _("Error"), _("Lua script was found but failed to load. Map might not work as expected!"), this,
×
134
              MsgboxButton::Ok, MsgboxIcon::ExclamationRed, ID_mbLuaLoadError));
×
135
            lua.reset();
×
136
        } else if(!lua->CheckScriptVersion())
×
137
        {
138
            WINDOWMANAGER.ShowAfterSwitch(std::make_unique<iwMsgbox>(
×
139
              _("Error"), _("Lua script uses a different version and cannot be used. Map might not work as expected!"),
×
140
              this, MsgboxButton::Ok, MsgboxIcon::ExclamationRed, ID_mbLuaVersionError));
×
141
            lua.reset();
×
142
        } else if(!lua->EventSettingsInit(serverType == ServerType::Local, gameLobby_->isSavegame()))
×
143
        {
144
            // This should have been detected for the host so others won't even see the script
145
            RTTR_Assert(gameLobby_->isHost());
×
146
            LOG.write(_("Lua was disabled by the script itself\n"));
×
147
            lua.reset();
×
148
        }
149
        if(!lua && gameLobby_->isHost())
×
150
            lobbyController->RemoveLuaScript();
×
151
    }
152

153
    const bool readonlySettings = !gameLobby_->isHost() || gameLobby_->isSavegame() || !IsChangeAllowed("general");
1✔
154
    allowAddonChange = gameLobby_->isHost() && !gameLobby_->isSavegame()
2✔
155
                       && (IsChangeAllowed("addonsAll") || IsChangeAllowed("addonsSome"));
3✔
156

157
    AddText(ID_txtGameName, DrawPoint(400, 5), GAMECLIENT.GetGameName(), COLOR_YELLOW, FontStyle::CENTER, LargeFont);
1✔
158

159
    AddText(ID_txtColName, DrawPoint(125, 40), _("Player Name"), COLOR_YELLOW, FontStyle::CENTER, NormalFont);
1✔
160
    AddText(ID_txtColRace, DrawPoint(262, 40), _("Race"), COLOR_YELLOW, FontStyle::CENTER, NormalFont);
1✔
161
    AddText(ID_txtColColor, DrawPoint(369, 40), _("Color"), COLOR_YELLOW, FontStyle::CENTER, NormalFont);
1✔
162
    AddText(ID_txtColTeam, DrawPoint(419, 40), _("Team"), COLOR_YELLOW, FontStyle::CENTER, NormalFont);
1✔
163

164
    if(!IsSinglePlayer())
1✔
165
    {
166
        AddText(ID_txtColReady, DrawPoint(479, 40), _("Ready?"), COLOR_YELLOW, FontStyle::CENTER, NormalFont);
1✔
167
        AddText(ID_txtColPing, DrawPoint(530, 40), _("Ping"), COLOR_YELLOW, FontStyle::CENTER, NormalFont);
1✔
168
    }
169
    if(gameLobby_->isHost() && !gameLobby_->isSavegame())
1✔
170
        AddText(ID_txtColSwap, DrawPoint(0, 40), _("Swap"), COLOR_YELLOW, FontStyle::LEFT, NormalFont);
1✔
171
    if(gameLobby_->isSavegame())
1✔
172
        AddText(ID_txtColPastPlayer, DrawPoint(645, 40), _("Past player"), COLOR_YELLOW, FontStyle::CENTER, NormalFont);
×
173

174
    if(!IsSinglePlayer())
1✔
175
    {
176
        // Enable lobby chat when we are logged in
177
        if(lobbyClient_ && lobbyClient_->IsLoggedIn())
1✔
178
        {
179
            ctrlOptionGroup* chatTab = AddOptionGroup(ID_optChatTab, GroupSelectType::Check);
1✔
180
            chatTab->AddTextButton(ID_btChatGame, DrawPoint(20, 320), Extent(178, 22), TextureColor::Green2,
3✔
181
                                   _("Game Chat"), NormalFont);
2✔
182
            chatTab->AddTextButton(ID_btChatLobby, DrawPoint(202, 320), Extent(178, 22), TextureColor::Green2,
3✔
183
                                   _("Lobby Chat"), NormalFont);
2✔
184
            gameChat =
1✔
185
              AddChatCtrl(ID_chatGame, DrawPoint(20, 345), Extent(360, 218 - 25), TextureColor::Grey, NormalFont);
1✔
186
            lobbyChat =
1✔
187
              AddChatCtrl(ID_chatLobby, DrawPoint(20, 345), Extent(360, 218 - 25), TextureColor::Grey, NormalFont);
1✔
188
            chatTab->SetSelection(ID_btChatGame, true);
1✔
189
        } else
190
        {
191
            gameChat = AddChatCtrl(ID_chatGame, DrawPoint(20, 320), Extent(360, 218), TextureColor::Grey, NormalFont);
×
192
        }
193
        AddEdit(ID_edtChatMsg, DrawPoint(20, 540), Extent(360, 22), TextureColor::Grey, NormalFont);
1✔
194
    }
195

196
    AddTextButton(ID_btStartGame, DrawPoint(600, 560), Extent(180, 22), TextureColor::Green2,
4✔
197
                  (gameLobby_->isHost() ? _("Start game") : _("Ready")), NormalFont);
3✔
198

199
    AddTextButton(ID_btReturn, DrawPoint(400, 560), Extent(180, 22), TextureColor::Red1, _("Return"), NormalFont);
1✔
200

201
    AddCheckBox(ID_chkLockTeams, DrawPoint(400, 460), Extent(180, 26), TextureColor::Grey, _("Lock teams:"), NormalFont,
2✔
202
                readonlySettings);
1✔
203
    AddCheckBox(ID_chkSharedView, DrawPoint(600, 460), Extent(180, 26), TextureColor::Grey, _("Shared team view"),
2✔
204
                NormalFont, readonlySettings);
2✔
205
    AddCheckBox(ID_chkRandomSpawn, DrawPoint(600, 430), Extent(180, 26), TextureColor::Grey,
2✔
206
                _("Random start locations"), NormalFont, readonlySettings);
2✔
207

208
    AddText(ID_txtAddons, DrawPoint(400, 499), _("Addons:"), COLOR_YELLOW, FontStyle{}, NormalFont);
1✔
209
    AddTextButton(ID_btSettings, DrawPoint(600, 495), Extent(180, 22), TextureColor::Green2,
3✔
210
                  allowAddonChange ? _("Change Settings...") : _("View Settings..."), NormalFont);
2✔
211

212
    ctrlComboBox* combo;
213

214
    // umgedrehte Reihenfolge, damit die Listen nicht dahinter sind
215

216
    AddText(ID_txtExploration, DrawPoint(400, 405), _("Exploration:"), COLOR_YELLOW, FontStyle{}, NormalFont);
1✔
217
    combo = AddComboBox(ID_cbExploration, DrawPoint(600, 400), Extent(180, 20), TextureColor::Grey, NormalFont, 100,
2✔
218
                        readonlySettings);
1✔
219
    combo->AddString(_("Off (all visible)"));
1✔
220
    combo->AddString(_("Classic (Settlers 2)"));
1✔
221
    combo->AddString(_("Fog of War"));
1✔
222
    combo->AddString(_("FoW - all explored"));
1✔
223

224
    AddText(ID_txtGoods, DrawPoint(400, 375), _("Goods at start:"), COLOR_YELLOW, FontStyle{}, NormalFont);
1✔
225
    combo = AddComboBox(ID_cbGoods, DrawPoint(600, 370), Extent(180, 20), TextureColor::Grey, NormalFont, 100,
2✔
226
                        readonlySettings);
1✔
227
    combo->AddString(_("Very Low"));
1✔
228
    combo->AddString(_("Low"));
1✔
229
    combo->AddString(_("Normal"));
1✔
230
    combo->AddString(_("A lot"));
1✔
231

232
    AddText(ID_txtGoals, DrawPoint(400, 345), _("Goals:"), COLOR_YELLOW, FontStyle{}, NormalFont);
1✔
233
    combo = AddComboBox(ID_cbGoals, DrawPoint(600, 340), Extent(180, 20), TextureColor::Grey, NormalFont, 100,
2✔
234
                        readonlySettings);
1✔
235
    combo->AddString(_("None"));
1✔
236
    combo->AddString(_("Conquer 3/4 of map"));
1✔
237
    combo->AddString(_("Total domination"));
1✔
238
    combo->AddString(_("Economy mode"));
1✔
239

240
    // Lobby game?
241
    if(lobbyClient_ && lobbyClient_->IsLoggedIn())
1✔
242
    {
243
        // Then add tournament modes as possible "objectives"
244
        for(const unsigned duration : TOURNAMENT_MODES_DURATION)
6✔
245
            combo->AddString(helpers::format(_("Tournament: %u minutes"), duration));
5✔
246
    }
247

248
    AddText(ID_txtSpeed, DrawPoint(400, 315), _("Speed:"), COLOR_YELLOW, FontStyle{}, NormalFont);
1✔
249
    combo = AddComboBox(ID_cbSpeed, DrawPoint(600, 310), Extent(180, 20), TextureColor::Grey, NormalFont, 100,
2✔
250
                        !gameLobby_->isHost());
2✔
251
    combo->AddString(_("Very slow"));
1✔
252
    combo->AddString(_("Slow"));
1✔
253
    combo->AddString(_("Normal"));
1✔
254
    combo->AddString(_("Fast"));
1✔
255
    combo->AddString(_("Very fast"));
1✔
256

257
    // Karte laden, um Kartenvorschau anzuzeigen
258
    if(!gameLobby_->isSavegame())
1✔
259
    {
260
        const bool isMapPreviewEnabled = !lua || lua->IsMapPreviewEnabled();
1✔
261
        if(!isMapPreviewEnabled)
1✔
262
        {
263
            AddTextDeepening(ID_txtNoPreview, DrawPoint(560, 40), Extent(220, 220), TextureColor::Grey, _("No preview"),
×
264
                             LargeFont, COLOR_YELLOW);
×
265
            AddText(ID_txtMapName, DrawPoint(670, 40 + 220 + 10), _("Map: ") + GAMECLIENT.GetMapTitle(), COLOR_YELLOW,
×
266
                    FontStyle::CENTER, NormalFont);
×
267
        } else
268
        {
269
            // Map laden
270
            libsiedler2::Archiv mapArchiv;
2✔
271
            // Karteninformationen laden
272
            if(int ec = libsiedler2::loader::LoadMAP(GAMECLIENT.GetMapPath(), mapArchiv))
1✔
273
            {
274
                WINDOWMANAGER.ShowAfterSwitch(
2✔
275
                  std::make_unique<iwMsgbox>(_("Error"), _("Could not load map:\n") + libsiedler2::getErrorString(ec),
2✔
276
                                             this, MsgboxButton::Ok, MsgboxIcon::ExclamationRed, ID_mbMapLoadError));
2✔
277
            } else
278
            {
279
                auto* map = static_cast<libsiedler2::ArchivItem_Map*>(mapArchiv.get(0));
×
280
                ctrlPreviewMinimap* preview = AddPreviewMinimap(ID_miniMap, DrawPoint(560, 40), Extent(220, 220), map);
×
281

282
                // Titel der Karte, Y-Position relativ je nach Höhe der Minimap festlegen, daher nochmals danach
283
                // verschieben, da diese Position sonst skaliert wird!
284
                ctrlText* text = AddText(ID_txtMapName, DrawPoint(670, 0), _("Map: ") + GAMECLIENT.GetMapTitle(),
×
285
                                         COLOR_YELLOW, FontStyle::CENTER, NormalFont);
×
286
                text->SetPos(DrawPoint(text->GetPos().x, preview->GetPos().y + preview->GetMapArea().bottom + 10));
×
287
            }
288
        }
289
    }
290

291
    if(GAMECLIENT.IsAIBattleModeOn())
1✔
292
    {
293
        const auto& aiBattlePlayers = GAMECLIENT.GetAIBattlePlayers();
×
294

295
        // Initialize AI battle players
296
        for(unsigned i = 0; i < gameLobby_->getNumPlayers(); i++)
×
297
        {
298
            if(i < aiBattlePlayers.size())
×
299
                lobbyController->SetPlayerState(i, PlayerState::AI, aiBattlePlayers[i]);
×
300
            else
301
                lobbyController->CloseSlot(i); // Close remaining slots
×
302
        }
303

304
        // Set name of host to the corresponding AI for local player
305
        if(localPlayerId_ < aiBattlePlayers.size())
×
306
            lobbyController->SetName(localPlayerId_,
×
307
                                     JoinPlayerInfo::MakeAIName(aiBattlePlayers[localPlayerId_], localPlayerId_));
×
308
    } else if(IsSinglePlayer() && !gameLobby_->isSavegame())
1✔
309
    {
310
        // Setze initial auf KI
311
        for(unsigned i = 0; i < gameLobby_->getNumPlayers(); i++)
×
312
        {
313
            if(!gameLobby_->getPlayer(i).isHost)
×
314
                lobbyController->SetPlayerState(i, PlayerState::AI, AI::Info(AI::Type::Default, AI::Level::Easy));
×
315
        }
316
    }
317

318
    // Alle Spielercontrols erstellen
319
    for(unsigned i = 0; i < gameLobby_->getNumPlayers(); i++)
3✔
320
        UpdatePlayerRow(i);
2✔
321
    // swap buttons erstellen
322
    if(gameLobby_->isHost() && !gameLobby_->isSavegame() && IsChangeAllowed("swapping"))
1✔
323
    {
324
        for(unsigned i = 0; i < gameLobby_->getNumPlayers(); i++)
3✔
325
        {
326
            int rowPos = GetCtrl<Window>(ID_grpPlayerStart + i)->GetCtrl<Window>(ID_btPlayerState)->GetPos().y;
2✔
327
            ctrlButton* bt =
328
              AddTextButton(ID_btSwap + i, DrawPoint(5, 0), Extent(22, 22), TextureColor::Red1, _("-"), NormalFont);
2✔
329
            bt->SetPos(DrawPoint(bt->GetPos().x, rowPos));
2✔
330
        }
331
    }
332
    CI_GGSChanged(gameLobby_->getSettings());
1✔
333

334
    if(serverType == ServerType::Lobby && lobbyClient_ && lobbyClient_->IsLoggedIn())
1✔
335
    {
336
        lobbyClient_->AddListener(this);
1✔
337
        lobbyClient_->SendServerJoinRequest();
1✔
338
    }
339

340
    GAMECLIENT.SetInterface(this);
1✔
341
}
342

343
dskGameLobby::~dskGameLobby()
4✔
344
{
345
    if(lobbyClient_)
2✔
346
        lobbyClient_->RemoveListener(this);
1✔
347
    GAMECLIENT.RemoveInterface(this);
2✔
348
}
4✔
349

350
/**
351
 *  Größe ändern-Reaktionen die nicht vom Skaling-Mechanismus erfasst werden.
352
 */
353
void dskGameLobby::Resize(const Extent& newSize)
×
354
{
355
    Window::Resize(newSize);
×
356

357
    // Text unter der PreviewMinimap verschieben, dessen Höhe von der Höhe der
358
    // PreviewMinimap abhängt, welche sich gerade geändert hat.
359
    auto* preview = GetCtrl<ctrlPreviewMinimap>(ID_miniMap);
×
360
    auto* text = GetCtrl<ctrlText>(ID_txtMapName);
×
361
    if(preview && text)
×
362
    {
363
        DrawPoint txtPos = text->GetPos();
×
364
        txtPos.y = preview->GetPos().y + preview->GetMapArea().bottom + 10;
×
365
        text->SetPos(txtPos);
×
366
    }
367
}
×
368

369
void dskGameLobby::SetActive(bool activate /*= true*/)
4✔
370
{
371
    Desktop::SetActive(activate);
4✔
372
    if(activate && !wasActivated && lua && gameLobby_->isHost())
4✔
373
    {
374
        wasActivated = true;
×
375
        try
376
        {
377
            lua->EventSettingsReady();
×
378
        } catch(const LuaExecutionError&)
×
379
        {
380
            WINDOWMANAGER.Show(std::make_unique<iwMsgbox>(
×
381
              _("Error"), _("Lua script was found but failed to load. Map might not work as expected!"), this,
×
382
              MsgboxButton::Ok, MsgboxIcon::ExclamationRed, ID_mbLuaLoadError));
×
383
            lua.reset();
×
384
        }
385
    }
386
}
4✔
387

388
void dskGameLobby::UpdatePlayerRow(const unsigned row)
2✔
389
{
390
    const JoinPlayerInfo& player = gameLobby_->getPlayer(row);
2✔
391

392
    unsigned cy = 80 + row * 30;
2✔
393
    TextureColor tc = (row & 1 ? TextureColor::Grey : TextureColor::Green2);
2✔
394

395
    // Alle Controls erstmal zerstören (die ganze Gruppe)
396
    DeleteCtrl(ID_grpPlayerStart + row);
2✔
397
    // und neu erzeugen
398
    ctrlGroup* group = AddGroup(ID_grpPlayerStart + row);
2✔
399

400
    std::string name;
4✔
401
    switch(player.ps)
2✔
402
    {
403
        default: name.clear(); break;
×
404
        case PlayerState::Occupied:
1✔
405
        case PlayerState::AI: name = player.name; break;
1✔
406
        case PlayerState::Free: name = _("Open"); break;
1✔
407
        case PlayerState::Locked: name = _("Closed"); break;
×
408
    }
409

410
    if(GetCtrl<ctrlPreviewMinimap>(ID_miniMap))
2✔
411
    {
412
        if(player.isUsed())
×
413
            // Nur KIs und richtige Spieler haben eine Farbe auf der Karte
414
            GetCtrl<ctrlPreviewMinimap>(ID_miniMap)->SetPlayerColor(row, player.color);
×
415
        else
416
            // Keine richtigen Spieler --> Startposition auf der Karte ausblenden
417
            GetCtrl<ctrlPreviewMinimap>(ID_miniMap)->SetPlayerColor(row, 0);
×
418
    }
419

420
    // Spielername, beim Hosts Spielerbuttons, aber nich beim ihm selber, er kann sich ja nich selber kicken!
421
    if(gameLobby_->isHost() && !player.isHost && IsChangeAllowed("playerState"))
2✔
422
        group->AddTextButton(ID_btPlayerState, DrawPoint(30, cy), Extent(180, 22), tc, name, NormalFont);
1✔
423
    else
424
        group->AddTextDeepening(ID_btPlayerState, DrawPoint(30, cy), Extent(180, 22), tc, name, NormalFont,
1✔
425
                                COLOR_YELLOW);
426
    auto* text = group->GetCtrl<ctrlBaseText>(ID_btPlayerState);
2✔
427

428
    // Is das der Host? Dann farblich markieren
429
    if(player.isHost)
2✔
430
        text->SetTextColor(0xFF00FF00);
1✔
431

432
    // Bei geschlossenem nicht sichtbar
433
    if(player.isUsed())
2✔
434
    {
435
        // If not in savegame -> Player can change own row and host can change AIs
436
        const bool allowPlayerChange = ((gameLobby_->isHost() && player.ps == PlayerState::AI) || localPlayerId_ == row)
2✔
437
                                       && !gameLobby_->isSavegame();
2✔
438
        bool allowNationChange = allowPlayerChange;
1✔
439
        bool allowColorChange = allowPlayerChange;
1✔
440
        bool allowTeamChange = allowPlayerChange;
1✔
441
        if(lua)
1✔
442
        {
443
            if(localPlayerId_ == row)
×
444
            {
445
                allowNationChange &= lua->IsChangeAllowed("ownNation", true);
×
446
                allowColorChange &= lua->IsChangeAllowed("ownColor", true);
×
447
                allowTeamChange &= lua->IsChangeAllowed("ownTeam", true);
×
448
            } else
449
            {
450
                allowNationChange &= lua->IsChangeAllowed("aiNation", true);
×
451
                allowColorChange &= lua->IsChangeAllowed("aiColor", true);
×
452
                allowTeamChange &= lua->IsChangeAllowed("aiTeam", true);
×
453
            }
454
        }
455

456
        if(allowNationChange)
1✔
457
            group->AddTextButton(ID_btNation, DrawPoint(215, cy), Extent(95, 22), tc, _(NationNames[Nation::Romans]),
2✔
458
                                 NormalFont);
1✔
459
        else
NEW
460
            group->AddTextDeepening(ID_btNation, DrawPoint(215, cy), Extent(95, 22), tc, _(NationNames[Nation::Romans]),
×
461
                                    NormalFont, COLOR_YELLOW);
×
462

463
        const auto& portrait = Portraits[player.portraitIndex];
1✔
464
        group->AddImageButton(ID_btPortrait, DrawPoint(315, cy), Extent(34, 22), tc,
3✔
465
                              LOADER.GetImageN(portrait.resourceId, portrait.resourceIndex), _(portrait.name));
2✔
466

467
        if(allowColorChange)
1✔
468
            group->AddColorButton(ID_btColor, DrawPoint(354, cy), Extent(30, 22), tc, 0);
1✔
469
        else
NEW
470
            group->AddColorDeepening(ID_btColor, DrawPoint(354, cy), Extent(30, 22), tc, 0);
×
471

472
        if(allowTeamChange)
1✔
473
            group->AddTextButton(ID_btTeam, DrawPoint(394, cy), Extent(50, 22), tc, _("-"), NormalFont);
1✔
474
        else
NEW
475
            group->AddTextDeepening(ID_btTeam, DrawPoint(394, cy), Extent(50, 22), tc, _("-"), NormalFont,
×
476
                                    COLOR_YELLOW);
477

478
        // Ready (not for AIs and Host)
479
        if(player.ps == PlayerState::Occupied && !player.isHost)
1✔
NEW
480
            group->AddCheckBox(ID_chkReady, DrawPoint(464, cy), Extent(22, 22), tc, "", nullptr,
×
481
                               (localPlayerId_ != row));
×
482

483
        ctrlVarDeepening* ping = group->AddVarDeepening(ID_txtPing, DrawPoint(505, cy), Extent(50, 22), tc, _("%d"),
1✔
484
                                                        NormalFont, COLOR_YELLOW, 1, &player.ping); //-V111
1✔
485

486
        // Move (not for Save games and Host)
487
        if(gameLobby_->isSavegame() && player.ps == PlayerState::Occupied)
1✔
488
        {
489
            ctrlComboBox* combo = group->AddComboBox(ID_cbMove, DrawPoint(560, cy), Extent(160, 22), tc, NormalFont,
×
490
                                                     150, !gameLobby_->isHost());
×
491

492
            // Mit den alten Namen füllen
493
            for(unsigned i = 0; i < gameLobby_->getNumPlayers(); ++i)
×
494
            {
495
                if(!gameLobby_->getPlayer(i).originName.empty())
×
496
                {
497
                    combo->AddString(gameLobby_->getPlayer(i).originName);
×
498
                    if(i == row)
×
499
                        combo->SetSelection(combo->GetNumItems() - 1u);
×
500
                }
501
            }
502
        }
503

504
        // Hide ping for AIs or on single player games
505
        if(player.ps == PlayerState::AI || IsSinglePlayer())
1✔
506
            ping->SetVisible(false);
×
507

508
        // Fill fields
509
        ChangeNation(row, player.nation);
1✔
510
        ChangePortrait(row, player.portraitIndex);
1✔
511
        ChangeTeam(row, player.team);
1✔
512
        ChangePing(row);
1✔
513
        ChangeReady(row, player.isReady);
1✔
514
        ChangeColor(row, player.color);
1✔
515
    }
516
    group->SetActive(IsActive());
2✔
517
}
2✔
518

519
/**
520
 *  Methode vor dem Zeichnen
521
 */
522
void dskGameLobby::Msg_PaintBefore()
2✔
523
{
524
    Desktop::Msg_PaintBefore();
2✔
525
    // Chatfenster Fokus geben
526
    if(!IsSinglePlayer())
2✔
527
        GetCtrl<ctrlEdit>(ID_edtChatMsg)->SetFocus();
1✔
528
}
2✔
529

530
void dskGameLobby::Msg_Group_ButtonClick(const unsigned group_id, const unsigned ctrl_id)
×
531
{
532
    unsigned playerId = group_id - ID_grpPlayerStart;
×
533

534
    switch(ctrl_id)
×
535
    {
536
        case ID_btPlayerState:
×
537
        {
538
            if(gameLobby_->isHost())
×
539
                lobbyController->TogglePlayerState(playerId);
×
540
        }
541
        break;
×
542

543
        case ID_btNation:
×
544
        {
545
            SetPlayerReady(playerId, false);
×
546

547
            if(playerId == localPlayerId_ || gameLobby_->isHost())
×
548
            {
549
                JoinPlayerInfo& player = gameLobby_->getPlayer(playerId);
×
550
                player.nation = nextEnumValue(player.nation);
×
551
                if(gameLobby_->isHost())
×
552
                    lobbyController->SetNation(playerId, player.nation);
×
553
                else
554
                    GAMECLIENT.Command_SetNation(player.nation);
×
555
                ChangeNation(playerId, player.nation);
×
556
            }
557
        }
558
        break;
×
559

NEW
560
        case ID_btPortrait:
×
561
        {
NEW
562
            SetPlayerReady(playerId, false);
×
563

NEW
564
            if(playerId == localPlayerId_ || gameLobby_->isHost())
×
565
            {
NEW
566
                JoinPlayerInfo& player = gameLobby_->getPlayer(playerId);
×
NEW
567
                player.portraitIndex = (player.portraitIndex + 1) % Portraits.size();
×
NEW
568
                if(gameLobby_->isHost())
×
NEW
569
                    lobbyController->SetPortrait(playerId, player.portraitIndex);
×
570
                else
NEW
571
                    GAMECLIENT.Command_SetPortrait(player.portraitIndex);
×
NEW
572
                ChangePortrait(playerId, player.portraitIndex);
×
573
            }
574
        }
NEW
575
        break;
×
576

UNCOV
577
        case ID_btColor:
×
578
        {
579
            SetPlayerReady(playerId, false);
×
580

581
            if(playerId == localPlayerId_ || gameLobby_->isHost())
×
582
            {
583
                // Get colors used by other players
584
                std::set<unsigned> takenColors;
×
585
                for(unsigned p = 0; p < gameLobby_->getNumPlayers(); ++p)
×
586
                {
587
                    // Skip self
588
                    if(p == playerId)
×
589
                        continue;
×
590

591
                    const JoinPlayerInfo& otherPlayer = gameLobby_->getPlayer(p);
×
592
                    if(otherPlayer.isUsed())
×
593
                        takenColors.insert(otherPlayer.color);
×
594
                }
595

596
                // Look for a unique color
597
                JoinPlayerInfo& player = gameLobby_->getPlayer(playerId);
×
598
                int newColorIdx = JoinPlayerInfo::GetColorIdx(player.color);
×
599
                do
×
600
                {
601
                    player.color = PLAYER_COLORS[(++newColorIdx) % PLAYER_COLORS.size()];
×
602
                } while(helpers::contains(takenColors, player.color));
×
603

604
                if(gameLobby_->isHost())
×
605
                    lobbyController->SetColor(playerId, player.color);
×
606
                else
607
                    GAMECLIENT.Command_SetColor(player.color);
×
608
                ChangeColor(playerId, player.color);
×
609
            }
610

611
            // Start-Farbe der Minimap ändern
612
        }
613
        break;
×
614

615
        case ID_btTeam:
×
616
        {
617
            SetPlayerReady(playerId, false);
×
618

619
            if(playerId == localPlayerId_ || gameLobby_->isHost())
×
620
            {
621
                JoinPlayerInfo& player = gameLobby_->getPlayer(playerId);
×
622
                player.team = nextEnumValue(player.team);
×
623
                if(gameLobby_->isHost())
×
624
                    lobbyController->SetTeam(playerId, player.team);
×
625
                else
626
                    GAMECLIENT.Command_SetTeam(player.team);
×
627
                ChangeTeam(playerId, player.team);
×
628
            }
629
        }
630
        break;
×
631
    }
632
}
×
633

634
void dskGameLobby::Msg_Group_CheckboxChange(const unsigned group_id, const unsigned /*ctrl_id*/, const bool checked)
×
635
{
636
    unsigned playerId = group_id - ID_grpPlayerStart;
×
637

638
    // Bereit
639
    if(playerId < MAX_PLAYERS)
×
640
        SetPlayerReady(playerId, checked);
×
641
}
×
642

643
void dskGameLobby::Msg_Group_ComboSelectItem(const unsigned group_id, const unsigned /*ctrl_id*/,
×
644
                                             const unsigned selection)
645
{
646
    if(!gameLobby_->isHost())
×
647
        return;
×
648
    // Swap players
649
    const unsigned playerId = group_id - ID_grpPlayerStart;
×
650

651
    int player2 = -1;
×
652
    for(unsigned i = 0, playerCtr = 0; i < gameLobby_->getNumPlayers(); ++i)
×
653
    {
654
        if(!gameLobby_->getPlayer(i).originName.empty() && playerCtr++ == selection)
×
655
        {
656
            player2 = i;
×
657
            break;
×
658
        }
659
    }
660

661
    if(player2 < 0)
×
662
        LOG.write("dskHostGame: ERROR: Selected player not found, stop swapping!\n");
×
663
    else
664
        lobbyController->SwapPlayers(playerId, static_cast<unsigned>(player2));
×
665
}
666

667
void dskGameLobby::GoBack()
×
668
{
669
    if(IsSinglePlayer())
×
670
        WINDOWMANAGER.Switch(std::make_unique<dskSinglePlayer>());
×
671
    else if(serverType == ServerType::LAN)
×
672
        WINDOWMANAGER.Switch(std::make_unique<dskLAN>());
×
673
    else if(serverType == ServerType::Lobby && lobbyClient_ && lobbyClient_->IsLoggedIn())
×
674
        WINDOWMANAGER.Switch(std::make_unique<dskLobby>());
×
675
    else
676
        WINDOWMANAGER.Switch(std::make_unique<dskDirectIP>());
×
677
}
×
678

679
bool dskGameLobby::IsChangeAllowed(const std::string& setting) const
4✔
680
{
681
    return !lua || lua->IsChangeAllowed(setting);
4✔
682
}
683

684
void dskGameLobby::Msg_ButtonClick(const unsigned ctrl_id)
×
685
{
686
    if(ctrl_id >= ID_btSwap && ctrl_id < ID_btSwap + MAX_PLAYERS)
×
687
    {
688
        unsigned targetPlayer = ctrl_id - ID_btSwap;
×
689
        if(targetPlayer != localPlayerId_ && gameLobby_->isHost())
×
690
            lobbyController->SwapPlayers(localPlayerId_, targetPlayer);
×
691
        return;
×
692
    }
693
    switch(ctrl_id)
×
694
    {
695
        case ID_btReturn:
×
696
            GAMECLIENT.Stop();
×
697
            GoBack();
×
698
            break;
×
699

700
        case ID_btStartGame:
×
701
        {
702
            auto* ready = GetCtrl<ctrlTextButton>(ID_btStartGame);
×
703
            if(gameLobby_->isHost())
×
704
            {
705
                if(!checkOptions())
×
706
                    return;
×
707

708
                SetPlayerReady(localPlayerId_, true);
×
709
                if(lua)
×
710
                    lua->EventPlayerReady(localPlayerId_);
×
711
                if(ready->GetText() == _("Start game"))
×
712
                    lobbyController->StartCountdown(5);
×
713
                else
714
                    lobbyController->CancelCountdown();
×
715
            } else
716
            {
717
                if(ready->GetText() == _("Ready"))
×
718
                    SetPlayerReady(localPlayerId_, true);
×
719
                else
720
                    SetPlayerReady(localPlayerId_, false);
×
721
            }
722
        }
723
        break;
×
724
        case ID_btSettings: // Addons
×
725
        {
726
            if(auto* wnd = WINDOWMANAGER.FindNonModalWindow(CGI_ADDONS))
×
727
                wnd->Close();
×
728
            else
729
            {
730
                std::unique_ptr<iwAddons> w;
×
731
                if(!allowAddonChange)
×
732
                    w = std::make_unique<iwAddons>(gameLobby_->getSettings(), this, AddonChangeAllowed::None);
×
733
                else if(IsChangeAllowed("addonsAll"))
×
734
                    w = std::make_unique<iwAddons>(gameLobby_->getSettings(), this, AddonChangeAllowed::All);
×
735
                else
736
                {
737
                    RTTR_Assert(lua); // Otherwise all changes would be allowed
×
738
                    w = std::make_unique<iwAddons>(gameLobby_->getSettings(), this, AddonChangeAllowed::WhitelistOnly,
×
739
                                                   lua->GetAllowedAddons());
×
740
                }
741
                WINDOWMANAGER.Show(std::move(w));
×
742
            }
743
        }
744
        break;
×
745
    }
746
}
747

748
void dskGameLobby::Msg_EditEnter(const unsigned ctrl_id)
×
749
{
750
    if(ctrl_id != ID_edtChatMsg)
×
751
        return;
×
752
    auto* edit = GetCtrl<ctrlEdit>(ctrl_id);
×
753
    const std::string msg = edit->GetText();
×
754
    edit->SetText("");
×
755
    if(gameChat->IsVisible())
×
756
        GAMECLIENT.Command_Chat(msg, ChatDestination::All);
×
757
    else if(lobbyClient_ && lobbyClient_->IsLoggedIn() && lobbyChat->IsVisible())
×
758
        lobbyClient_->SendChat(msg);
×
759
}
760

761
void dskGameLobby::CI_Countdown(unsigned remainingTimeInSec)
×
762
{
763
    if(IsSinglePlayer())
×
764
        return;
×
765

766
    if(!hasCountdown_)
×
767
    {
768
        const std::string startMsg = helpers::format(_("You have %u seconds until game starts"), remainingTimeInSec);
×
769
        gameChat->AddMessage("", "", 0, startMsg, COLOR_RED);
×
770
        gameChat->AddMessage("", "", 0, _("Don't forget to check the addon configuration!"), 0xFFFFDD00);
×
771
        gameChat->AddMessage("", "", 0, "", 0xFFFFCC00);
×
772
        hasCountdown_ = true;
×
773
    }
774

775
    const std::string message =
776
      (remainingTimeInSec > 0) ? " " + std::to_string(remainingTimeInSec) : _("Starting game, please wait");
×
777

778
    gameChat->AddMessage("", "", 0, message, 0xFFFFBB00);
×
779
}
780

781
void dskGameLobby::CI_CancelCountdown(bool error)
×
782
{
783
    if(hasCountdown_)
×
784
    {
785
        hasCountdown_ = false;
×
786
        gameChat->AddMessage("", "", 0xFFCC2222, _("Start aborted"), 0xFFFFCC00);
×
787
        FlashGameChat();
×
788
    }
789

790
    if(gameLobby_->isHost())
×
791
    {
792
        if(error)
×
793
        {
794
            WINDOWMANAGER.Show(std::make_unique<iwMsgbox>(
×
795
              _("Error"),
×
796
              _("Game can only be started as soon as everybody has a unique color,everyone is "
×
797
                "ready and all free slots are closed."),
798
              this, MsgboxButton::Ok, MsgboxIcon::ExclamationRed, ID_mbStartErrror));
×
799
        }
800

801
        ChangeReady(localPlayerId_, true);
×
802
    }
803
}
×
804

805
void dskGameLobby::FlashGameChat()
3✔
806
{
807
    if(!gameChat->IsVisible())
3✔
808
    {
809
        auto* tab = GetCtrl<Window>(ID_optChatTab);
1✔
810
        auto* bt = tab->GetCtrl<ctrlButton>(ID_btChatGame);
1✔
811
        if(!localChatTabAnimId)
1✔
812
            localChatTabAnimId = tab->GetAnimationManager().addAnimation(new BlinkButtonAnim(bt));
1✔
813
    }
814
}
3✔
815

816
void dskGameLobby::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult mbr)
×
817
{
818
    switch(msgbox_id)
×
819
    {
820
        case ID_mbMapLoadError:
×
821
        case ID_mbError:
822
        {
823
            GAMECLIENT.Stop();
×
824

825
            GoBack();
×
826
        }
827
        break;
×
828
        case CGI_ADDONS: // addon-window applied settings?
×
829
        {
830
            if(mbr == MsgboxResult::Yes)
×
831
                UpdateGGS();
×
832
        }
833
        break;
×
834
        case ID_mbQuestionEconomy: // Economy Mode - change Addon Setttings
×
835
        {
836
            if(mbr == MsgboxResult::Yes)
×
837
            {
838
                gameLobby_->getSettings().setSelection(AddonId::PEACEFULMODE, true);
×
839
                gameLobby_->getSettings().setSelection(AddonId::NO_COINS_DEFAULT, true);
×
840
                gameLobby_->getSettings().setSelection(AddonId::LIMIT_CATAPULTS, 2);
×
841
                UpdateGGS();
×
842
            } else if(mbr == MsgboxResult::No)
×
843
            {
844
                forceOptions = true;
×
845
                Msg_ButtonClick(ID_btStartGame);
×
846
            }
847
        }
848
        break;
×
849
        case ID_mbQuestionPeaceful: // Peaceful mode still active but we have an attack based victory condition
×
850
        {
851
            if(mbr == MsgboxResult::Yes)
×
852
            {
853
                gameLobby_->getSettings().setSelection(AddonId::PEACEFULMODE, false);
×
854
            } else if(mbr == MsgboxResult::No)
×
855
            {
856
                forceOptions = true;
×
857
                Msg_ButtonClick(ID_btStartGame);
×
858
            }
859
        }
860
        break;
×
861
    }
862
}
×
863

864
void dskGameLobby::Msg_ComboSelectItem(const unsigned ctrl_id, const unsigned /*selection*/)
×
865
{
866
    switch(ctrl_id)
×
867
    {
868
        default: break;
×
869

870
        case ID_cbSpeed:
×
871
        case ID_cbGoals:
872
        case ID_cbGoods:
873
        case ID_cbExploration:
874
        {
875
            // GameSettings wurden verändert, resetten
876
            UpdateGGS();
×
877
        }
878
        break;
×
879
    }
880
}
×
881

882
void dskGameLobby::Msg_CheckboxChange(const unsigned ctrl_id, const bool /*checked*/)
×
883
{
884
    switch(ctrl_id)
×
885
    {
886
        default: break;
×
887
        case ID_chkSharedView:
×
888
        case ID_chkLockTeams:
889
        case ID_chkRandomSpawn:
890
        {
891
            // GameSettings wurden verändert, resetten
892
            UpdateGGS();
×
893
        }
894
        break;
×
895
    }
896
}
×
897

898
void dskGameLobby::Msg_OptionGroupChange(const unsigned ctrl_id, const unsigned selection)
4✔
899
{
900
    if(ctrl_id == ID_optChatTab)
4✔
901
    {
902
        gameChat->SetVisible(selection == ID_btChatGame);
4✔
903
        lobbyChat->SetVisible(selection == ID_btChatLobby);
4✔
904
        auto* tab = GetCtrl<Window>(ID_optChatTab);
4✔
905
        tab->GetCtrl<ctrlButton>(selection)->SetTexture(TextureColor::Green2);
4✔
906
        if(selection == ID_btChatGame)
4✔
907
        {
908
            tab->GetAnimationManager().finishAnimation(localChatTabAnimId, false);
3✔
909
            localChatTabAnimId = 0;
3✔
910
        } else
911
        {
912
            tab->GetAnimationManager().finishAnimation(lobbyChatTabAnimId, false);
1✔
913
            lobbyChatTabAnimId = 0;
1✔
914
        }
915
    }
916
}
4✔
917

918
void dskGameLobby::UpdateGGS()
×
919
{
920
    RTTR_Assert(gameLobby_->isHost());
×
921

922
    GlobalGameSettings& ggs = gameLobby_->getSettings();
×
923

924
    ggs.speed = static_cast<GameSpeed>(GetCtrl<ctrlComboBox>(ID_cbSpeed)->GetSelection().get());
×
925
    ggs.objective = static_cast<GameObjective>(GetCtrl<ctrlComboBox>(ID_cbGoals)->GetSelection().get());
×
926
    ggs.startWares = static_cast<StartWares>(GetCtrl<ctrlComboBox>(ID_cbGoods)->GetSelection().get());
×
927
    ggs.exploration = static_cast<Exploration>(GetCtrl<ctrlComboBox>(ID_cbExploration)->GetSelection().get());
×
928
    ggs.lockedTeams = GetCtrl<ctrlCheck>(ID_chkLockTeams)->isChecked();
×
929
    ggs.teamView = GetCtrl<ctrlCheck>(ID_chkSharedView)->isChecked();
×
930
    ggs.randomStartPosition = GetCtrl<ctrlCheck>(ID_chkRandomSpawn)->isChecked();
×
931

932
    // An Server übermitteln
933
    lobbyController->ChangeGlobalGameSettings(ggs);
×
934
}
×
935

936
void dskGameLobby::ChangeTeam(const unsigned player, const Team team)
1✔
937
{
938
    constexpr helpers::EnumArray<const char*, Team> teams = {"-", "?", "1", "2", "3", "4", "1-2", "1-3", "1-4"};
1✔
939

940
    GetCtrl<ctrlGroup>(ID_grpPlayerStart + player)->GetCtrl<ctrlBaseText>(ID_btTeam)->SetText(teams[team]);
1✔
941
}
1✔
942

943
void dskGameLobby::ChangeReady(const unsigned player, const bool ready)
2✔
944
{
945
    auto* check = GetCtrl<ctrlGroup>(ID_grpPlayerStart + player)->GetCtrl<ctrlCheck>(ID_chkReady);
2✔
946
    if(check)
2✔
947
        check->setChecked(ready);
×
948

949
    if(player == localPlayerId_)
2✔
950
    {
951
        auto* start = GetCtrl<ctrlTextButton>(ID_btStartGame);
2✔
952
        if(gameLobby_->isHost())
2✔
953
            start->SetText(hasCountdown_ ? _("Cancel start") : _("Start game"));
2✔
954
        else
955
            start->SetText(ready ? _("Not Ready") : _("Ready"));
×
956
    }
957
}
2✔
958

959
void dskGameLobby::ChangeNation(const unsigned player, const Nation nation)
1✔
960
{
961
    GetCtrl<ctrlGroup>(ID_grpPlayerStart + player)->GetCtrl<ctrlBaseText>(ID_btNation)->SetText(_(NationNames[nation]));
1✔
962
}
1✔
963

964
void dskGameLobby::ChangePortrait(const unsigned player, const unsigned portraitIndex)
1✔
965
{
966
    RTTR_Assert(portraitIndex < Portraits.size());
1✔
967
    const auto& portrait = Portraits[portraitIndex];
1✔
968
    auto* ctrl = GetCtrl<ctrlGroup>(ID_grpPlayerStart + player)->GetCtrl<ctrlImageButton>(ID_btPortrait);
1✔
969
    ctrl->SetImage(LOADER.GetImageN(portrait.resourceId, portrait.resourceIndex));
1✔
970
    ctrl->SetTooltip(_(portrait.name));
1✔
971
}
1✔
972

973
void dskGameLobby::ChangePing(unsigned playerId)
1✔
974
{
975
    unsigned color = COLOR_RED;
1✔
976

977
    // Farbe bestimmen
978
    if(gameLobby_->getPlayer(playerId).ping < 300)
1✔
979
        color = COLOR_GREEN;
1✔
980
    else if(gameLobby_->getPlayer(playerId).ping < 800)
×
981
        color = COLOR_YELLOW;
×
982

983
    // und setzen
984
    GetCtrl<ctrlGroup>(ID_grpPlayerStart + playerId)->GetCtrl<ctrlVarDeepening>(ID_txtPing)->SetTextColor(color);
1✔
985
}
1✔
986

987
void dskGameLobby::ChangeColor(const unsigned player, const unsigned color)
1✔
988
{
989
    GetCtrl<ctrlGroup>(ID_grpPlayerStart + player)->GetCtrl<ctrlBaseColor>(ID_btColor)->SetColor(color);
1✔
990

991
    // Minimap-Startfarbe ändern
992
    if(GetCtrl<ctrlPreviewMinimap>(ID_miniMap))
1✔
993
        GetCtrl<ctrlPreviewMinimap>(ID_miniMap)->SetPlayerColor(player, color);
×
994
}
1✔
995

996
void dskGameLobby::SetPlayerReady(unsigned char player, bool ready)
1✔
997
{
998
    if(player != localPlayerId_)
1✔
999
        return;
×
1000
    if(gameLobby_->isHost())
1✔
1001
        ready = true;
1✔
1002
    if(gameLobby_->getPlayer(player).isReady != ready)
1✔
1003
    {
1004
        gameLobby_->getPlayer(player).isReady = ready;
1✔
1005
        GAMECLIENT.Command_SetReady(ready);
1✔
1006
    }
1007
    ChangeReady(player, ready);
1✔
1008
}
1009

1010
void dskGameLobby::CI_NewPlayer(const unsigned playerId)
×
1011
{
1012
    UpdatePlayerRow(playerId);
×
1013

1014
    if(lua && gameLobby_->isHost())
×
1015
        lua->EventPlayerJoined(playerId);
×
1016
}
×
1017

1018
void dskGameLobby::CI_PlayerLeft(const unsigned playerId)
×
1019
{
1020
    UpdatePlayerRow(playerId);
×
1021
    if(lua && gameLobby_->isHost())
×
1022
        lua->EventPlayerLeft(playerId);
×
1023
}
×
1024

1025
void dskGameLobby::CI_GameLoading(std::shared_ptr<Game> game)
×
1026
{
1027
    WINDOWMANAGER.Switch(std::make_unique<dskGameLoader>(std::move(game)));
×
1028
}
×
1029

1030
void dskGameLobby::CI_PlayerDataChanged(unsigned playerId)
×
1031
{
1032
    UpdatePlayerRow(playerId);
×
1033
}
×
1034

1035
void dskGameLobby::CI_PingChanged(const unsigned playerId, const unsigned short /*ping*/)
×
1036
{
1037
    ChangePing(playerId);
×
1038
}
×
1039

1040
void dskGameLobby::CI_ReadyChanged(const unsigned playerId, const bool ready)
×
1041
{
1042
    ChangeReady(playerId, ready);
×
1043
    // Event only called for other players (host ready is done in start game)
1044
    // Also only for host and non-savegames
1045
    if(ready && lua && gameLobby_->isHost() && playerId != localPlayerId_)
×
1046
        lua->EventPlayerReady(playerId);
×
1047
}
×
1048

1049
void dskGameLobby::CI_PlayersSwapped(const unsigned player1, const unsigned player2)
×
1050
{
1051
    if(player1 == localPlayerId_)
×
1052
        localPlayerId_ = player2;
×
1053
    else if(localPlayerId_ == player2)
×
1054
        localPlayerId_ = player1;
×
1055
    // Spieler wurden vertauscht, beide Reihen updaten
1056
    UpdatePlayerRow(player1);
×
1057
    UpdatePlayerRow(player2);
×
1058
}
×
1059

1060
void dskGameLobby::CI_GGSChanged(const GlobalGameSettings& /*ggs*/)
1✔
1061
{
1062
    const GlobalGameSettings& ggs = gameLobby_->getSettings();
1✔
1063

1064
    GetCtrl<ctrlComboBox>(ID_cbSpeed)->SetSelection(static_cast<unsigned short>(ggs.speed));
1✔
1065
    GetCtrl<ctrlComboBox>(ID_cbGoals)->SetSelection(static_cast<unsigned short>(ggs.objective));
1✔
1066
    GetCtrl<ctrlComboBox>(ID_cbGoods)->SetSelection(static_cast<unsigned short>(ggs.startWares));
1✔
1067
    GetCtrl<ctrlComboBox>(ID_cbExploration)->SetSelection(static_cast<unsigned short>(ggs.exploration));
1✔
1068
    GetCtrl<ctrlCheck>(ID_chkLockTeams)->setChecked(ggs.lockedTeams);
1✔
1069
    GetCtrl<ctrlCheck>(ID_chkSharedView)->setChecked(ggs.teamView);
1✔
1070
    GetCtrl<ctrlCheck>(ID_chkRandomSpawn)->setChecked(ggs.randomStartPosition);
1✔
1071

1072
    SetPlayerReady(localPlayerId_, false);
1✔
1073
}
1✔
1074

1075
void dskGameLobby::CI_Chat(const unsigned playerId, const ChatDestination /*cd*/, const std::string& msg)
3✔
1076
{
1077
    if((playerId != 0xFFFFFFFF) && !IsSinglePlayer())
3✔
1078
    {
1079
        std::string time = s25util::Time::FormatTime("(%H:%i:%s)");
9✔
1080

1081
        gameChat->AddMessage(time, gameLobby_->getPlayer(playerId).name, gameLobby_->getPlayer(playerId).color, msg,
3✔
1082
                             0xFFFFFF00); //-V810
1083
        FlashGameChat();
3✔
1084
    }
1085
}
3✔
1086

1087
void dskGameLobby::CI_Error(const ClientError ce)
×
1088
{
1089
    WINDOWMANAGER.Show(std::make_unique<iwMsgbox>(_("Error"), ClientErrorToStr(ce), this, MsgboxButton::Ok,
×
1090
                                                  MsgboxIcon::ExclamationRed, ID_mbError));
×
1091
}
×
1092

1093
/**
1094
 *  (Lobby-)Status: Benutzerdefinierter Fehler (kann auch Conn-Loss o.ä sein)
1095
 */
1096
void dskGameLobby::LC_Status_Error(const std::string& error)
×
1097
{
1098
    WINDOWMANAGER.Show(
×
1099
      std::make_unique<iwMsgbox>(_("Error"), error, this, MsgboxButton::Ok, MsgboxIcon::ExclamationRed, ID_mbError));
×
1100
}
×
1101

1102
void dskGameLobby::LC_Chat(const std::string& player, const std::string& text)
3✔
1103
{
1104
    if(!lobbyChat)
3✔
1105
        return;
×
1106
    lobbyChat->AddMessage("", player, ctrlChat::CalcUniqueColor(player), text, COLOR_YELLOW);
3✔
1107
    if(!lobbyChat->IsVisible())
3✔
1108
    {
1109
        auto* tab = GetCtrl<Window>(ID_optChatTab);
2✔
1110
        auto* bt = tab->GetCtrl<ctrlButton>(ID_btChatLobby);
2✔
1111
        if(!lobbyChatTabAnimId)
2✔
1112
            lobbyChatTabAnimId = tab->GetAnimationManager().addAnimation(new BlinkButtonAnim(bt));
1✔
1113
    }
1114
}
1115

1116
bool dskGameLobby::checkOptions()
×
1117
{
1118
    if(forceOptions)
×
1119
        return true;
×
1120
    const GlobalGameSettings& ggs = gameLobby_->getSettings();
×
1121
    if(ggs.objective == GameObjective::EconomyMode && !ggs.isEnabled(AddonId::PEACEFULMODE))
×
1122
    {
1123
        WINDOWMANAGER.Show(std::make_unique<iwMsgbox>(
×
1124
          _("Economy mode"),
×
1125
          _("You chose the economy mode. In economy mode the player or team that collects the most of certain goods "
×
1126
            "wins (check the Economic progress window in game).\n\n"
1127
            "Some players like to play this objective in peaceful mode. Would you like to adjust settings for a "
1128
            "peaceful game?\n"
1129
            "Choosing Yes will activate peaceful mode, ban catapults and disable buildings receiving coins by default. "
1130
            "After clicking Yes you will be able to review the changes and then start the game by clicking the Start "
1131
            "game button again.\n"
1132
            "Choosing No will start the game without any changes."),
1133
          this, MsgboxButton::YesNoCancel, MsgboxIcon::QuestionGreen, ID_mbQuestionEconomy));
×
1134
        return false;
×
1135
    } else if(ggs.isEnabled(AddonId::PEACEFULMODE)
×
1136
              && (ggs.objective == GameObjective::Conquer3_4 || ggs.objective == GameObjective::TotalDomination))
×
1137
    {
1138
        WINDOWMANAGER.Show(std::make_unique<iwMsgbox>(
×
1139
          _("Peaceful mode"),
×
1140
          _("You chose a war based victory condition but peaceful mode is still active. Would you like to deactivate "
×
1141
            "peaceful mode before you start? Choosing No will start the game, Yes will let you review the changes."),
1142
          this, MsgboxButton::YesNoCancel, MsgboxIcon::QuestionRed, ID_mbQuestionPeaceful));
×
1143
        return false;
×
1144
    }
1145
    return true;
×
1146
}
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