| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2005, Sven2 |
| 6 | * Copyright (c) 2017-2021, The LegacyClonk Team and contributors |
| 7 | * |
| 8 | * Distributed under the terms of the ISC license; see accompanying file |
| 9 | * "COPYING" for details. |
| 10 | * |
| 11 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 12 | * See accompanying file "TRADEMARK" for details. |
| 13 | * |
| 14 | * To redistribute this file separately, substitute the full license texts |
| 15 | * for the above references. |
| 16 | */ |
| 17 | |
| 18 | // Custom game options and configuration dialog |
| 19 | |
| 20 | #include "C4GuiComboBox.h" |
| 21 | #include "C4GuiResource.h" |
| 22 | #include "C4Include.h" |
| 23 | #include "C4GameOptions.h" |
| 24 | #include "C4Game.h" |
| 25 | |
| 26 | // C4GameOptionsList::Option |
| 27 | |
| 28 | C4GameOptionsList::Option::Option(C4GameOptionsList *pForDlg) |
| 29 | : BaseClass(C4Rect(0, 0, 0, 0)), pPrimarySubcomponent(nullptr) {} |
| 30 | |
| 31 | void C4GameOptionsList::Option::InitOption(C4GameOptionsList *pForDlg) |
| 32 | { |
| 33 | // post-call after initialization: Adds to list box and does initial update |
| 34 | // add to listbox (will eventually get moved) |
| 35 | pForDlg->AddElement(pChild: this); |
| 36 | // first-time update |
| 37 | Update(); |
| 38 | } |
| 39 | |
| 40 | // C4GameOptionsList::OptionDropdown |
| 41 | |
| 42 | C4GameOptionsList::OptionDropdown::OptionDropdown(C4GameOptionsList *pForDlg, const char *szCaption, bool fReadOnly) |
| 43 | : Option(pForDlg) |
| 44 | { |
| 45 | CStdFont &rUseFont = C4GUI::GetRes()->TextFont; |
| 46 | // get size of caption label |
| 47 | bool fTabular = pForDlg->IsTabular(); |
| 48 | int32_t iCaptWidth, iCaptHeight; |
| 49 | if (fTabular) |
| 50 | { |
| 51 | // tabular layout: Caption label width by largest caption |
| 52 | rUseFont.GetTextExtent(szText: LoadResStr(id: C4ResStrTableKey::IDS_NET_RUNTIMEJOIN), rsx&: iCaptWidth, rsy&: iCaptHeight, fCheckMarkup: true); |
| 53 | iCaptWidth = iCaptWidth * 5 / 4; |
| 54 | } |
| 55 | else |
| 56 | { |
| 57 | rUseFont.GetTextExtent(szText: szCaption, rsx&: iCaptWidth, rsy&: iCaptHeight, fCheckMarkup: true); |
| 58 | } |
| 59 | // calc total height for component |
| 60 | int iHorizontalMargin = 1; |
| 61 | int iVerticalMargin = 1; |
| 62 | int iComboMargin = 5; |
| 63 | int iSelComboHgt = C4GUI::ComboBox::GetDefaultHeight(); |
| 64 | SetBounds(C4Rect(0, 0, pForDlg->GetItemWidth(), (!fTabular) * (iCaptHeight + iVerticalMargin * 2) + iVerticalMargin * 2 + iSelComboHgt)); |
| 65 | C4GUI::ComponentAligner ca(GetContainedClientRect(), iHorizontalMargin, iVerticalMargin); |
| 66 | // create subcomponents |
| 67 | AddElement(pChild: pCaption = new C4GUI::Label(std::format(fmt: "{}:" , args&: szCaption).c_str(), fTabular ? ca.GetFromLeft(iWdt: iCaptWidth, iHgt: iCaptHeight) : ca.GetFromTop(iHgt: iCaptHeight), ALeft)); |
| 68 | ca.ExpandLeft(iByWdt: -iComboMargin); |
| 69 | AddElement(pChild: pPrimarySubcomponent = pDropdownList = new C4GUI::ComboBox(ca.GetAll())); |
| 70 | pDropdownList->SetReadOnly(fReadOnly); |
| 71 | pDropdownList->SetComboCB(new C4GUI::ComboBox_FillCallback<C4GameOptionsList::OptionDropdown>(this, &C4GameOptionsList::OptionDropdown::OnDropdownFill, &C4GameOptionsList::OptionDropdown::OnDropdownSelChange)); |
| 72 | // final init |
| 73 | InitOption(pForDlg); |
| 74 | } |
| 75 | |
| 76 | // C4GameOptionsList::OptionControlMode |
| 77 | |
| 78 | // Unfortunately, the control mode cannot be changed in the lobby |
| 79 | C4GameOptionsList::OptionControlMode::OptionControlMode(C4GameOptionsList *pForDlg) |
| 80 | : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr(id: C4ResStrTableKey::IDS_TEXT_CONTROLMODE), !Game.Control.isCtrlHost() || !Game.Control.isNetwork() || !Game.Control.Network.IsEnabled() || !pForDlg->IsRuntime()) |
| 81 | { |
| 82 | SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_CHANGESTHEWAYCONTROLDATAI)); |
| 83 | } |
| 84 | |
| 85 | void C4GameOptionsList::OptionControlMode::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller) |
| 86 | { |
| 87 | // change possible? |
| 88 | if (!Game.Control.isNetwork() || !Game.Control.Network.IsEnabled() || !Game.Control.isCtrlHost()) return; |
| 89 | // add possible modes |
| 90 | pFiller->AddEntry(szText: LoadResStr(id: C4ResStrTableKey::IDS_NET_CTRLMODE_CENTRAL), id: CNM_Central); |
| 91 | pFiller->AddEntry(szText: LoadResStr(id: C4ResStrTableKey::IDS_NET_CTRLMODE_DECENTRAL), id: CNM_Decentral); |
| 92 | if (!Game.Parameters.isLeague()) |
| 93 | pFiller->AddEntry(szText: "[!]Asynchroner Netzwerkmodus (experimentell!)" , id: CNM_Async); |
| 94 | } |
| 95 | |
| 96 | void C4GameOptionsList::OptionControlMode::DoDropdownSelChange(int32_t idNewSelection) |
| 97 | { |
| 98 | // change possible? |
| 99 | if (!Game.Control.isNetwork() || !Game.Control.Network.IsEnabled() || !Game.Control.isCtrlHost()) return; |
| 100 | // perform it |
| 101 | Game.Network.SetCtrlMode(idNewSelection); |
| 102 | // update done in parent call |
| 103 | } |
| 104 | |
| 105 | void C4GameOptionsList::OptionControlMode::Update() |
| 106 | { |
| 107 | const char *szControlMode; |
| 108 | if (!Game.Control.isNetwork() || !Game.Control.Network.IsEnabled()) |
| 109 | szControlMode = LoadResStr(id: C4ResStrTableKey::IDS_NET_NONET); |
| 110 | else |
| 111 | { |
| 112 | switch (Game.Control.Network.GetCtrlMode()) |
| 113 | { |
| 114 | case CNM_Central: szControlMode = LoadResStr(id: C4ResStrTableKey::IDS_NET_CTRLMODE_CENTRAL); break; |
| 115 | case CNM_Decentral: szControlMode = LoadResStr(id: C4ResStrTableKey::IDS_NET_CTRLMODE_DECENTRAL); break; |
| 116 | case CNM_Async: szControlMode = "[!]Asynchroner Netzwerkmodus (experimentell!)" ; break; |
| 117 | default: szControlMode = LoadResStr(id: C4ResStrTableKey::IDS_NET_CTRLMODE_NONE); break; |
| 118 | } |
| 119 | } |
| 120 | pDropdownList->SetText(szControlMode); |
| 121 | } |
| 122 | |
| 123 | // C4GameOptionsList::OptionControlRate |
| 124 | |
| 125 | C4GameOptionsList::OptionControlRate::OptionControlRate(C4GameOptionsList *pForDlg) |
| 126 | : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr(id: C4ResStrTableKey::IDS_CTL_CONTROLRATE), !Game.Control.isCtrlHost()) |
| 127 | { |
| 128 | SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_CTL_CONTROLRATE_DESC)); |
| 129 | } |
| 130 | |
| 131 | void C4GameOptionsList::OptionControlRate::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller) |
| 132 | { |
| 133 | for (int i = 1; i < (std::min)(a: C4MaxControlRate, b: 10); ++i) |
| 134 | pFiller->AddEntry(szText: std::format(fmt: "{}" , args&: i).c_str(), id: i); |
| 135 | } |
| 136 | |
| 137 | void C4GameOptionsList::OptionControlRate::DoDropdownSelChange(int32_t idNewSelection) |
| 138 | { |
| 139 | // adjust rate |
| 140 | int32_t iNewRate = idNewSelection; |
| 141 | if (!iNewRate || iNewRate == Game.Control.ControlRate) return; |
| 142 | Game.Control.AdjustControlRate(iBy: iNewRate - Game.Control.ControlRate); |
| 143 | } |
| 144 | |
| 145 | void C4GameOptionsList::OptionControlRate::Update() |
| 146 | { |
| 147 | if (atoi(nptr: pDropdownList->GetText().getData()) == Game.Control.ControlRate) return; |
| 148 | pDropdownList->SetText(std::format(fmt: "{}" , args&: Game.Control.ControlRate).c_str()); |
| 149 | } |
| 150 | |
| 151 | // C4GameOptionsList::OptionRuntimeJoin |
| 152 | |
| 153 | C4GameOptionsList::OptionRuntimeJoin::OptionRuntimeJoin(C4GameOptionsList *pForDlg) |
| 154 | : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr(id: C4ResStrTableKey::IDS_NET_RUNTIMEJOIN), !Game.Network.isHost()) |
| 155 | { |
| 156 | SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_NET_RUNTIMEJOIN_DESC)); |
| 157 | } |
| 158 | |
| 159 | void C4GameOptionsList::OptionRuntimeJoin::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller) |
| 160 | { |
| 161 | pFiller->AddEntry(szText: LoadResStr(id: C4ResStrTableKey::IDS_NET_RUNTIMEJOINBARRED), id: 0); |
| 162 | pFiller->AddEntry(szText: LoadResStr(id: C4ResStrTableKey::IDS_NET_RUNTIMEJOINFREE), id: 1); |
| 163 | } |
| 164 | |
| 165 | void C4GameOptionsList::OptionRuntimeJoin::DoDropdownSelChange(int32_t idNewSelection) |
| 166 | { |
| 167 | // adjust mode |
| 168 | bool fAllowed = !!idNewSelection; |
| 169 | Config.Network.NoRuntimeJoin = !fAllowed; |
| 170 | if (Game.IsRunning) Game.Network.AllowJoin(fAllow: fAllowed); |
| 171 | } |
| 172 | |
| 173 | void C4GameOptionsList::OptionRuntimeJoin::Update() |
| 174 | { |
| 175 | const char *szText; |
| 176 | if (Config.Network.NoRuntimeJoin) |
| 177 | szText = LoadResStr(id: C4ResStrTableKey::IDS_NET_RUNTIMEJOINBARRED); |
| 178 | else |
| 179 | szText = LoadResStr(id: C4ResStrTableKey::IDS_NET_RUNTIMEJOINFREE); |
| 180 | pDropdownList->SetText(szText); |
| 181 | } |
| 182 | |
| 183 | // C4GameOptionsList::OptionTeamDist |
| 184 | |
| 185 | C4GameOptionsList::OptionTeamDist::OptionTeamDist(C4GameOptionsList *pForDlg) |
| 186 | : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr(id: C4ResStrTableKey::IDS_MSG_TEAMDIST), !Game.Control.isCtrlHost()), optionsList{pForDlg} |
| 187 | { |
| 188 | SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_TEAMDIST_DESC)); |
| 189 | } |
| 190 | |
| 191 | void C4GameOptionsList::OptionTeamDist::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller) |
| 192 | { |
| 193 | Game.Teams.FillTeamDistOptions(pFiller); |
| 194 | } |
| 195 | |
| 196 | void C4GameOptionsList::OptionTeamDist::DoDropdownSelChange(int32_t idNewSelection) |
| 197 | { |
| 198 | // adjust team distribution |
| 199 | Game.Teams.SendSetTeamDist(eNewDist: C4TeamList::TeamDist(idNewSelection)); |
| 200 | optionsList->Update(); |
| 201 | } |
| 202 | |
| 203 | void C4GameOptionsList::OptionTeamDist::Update() |
| 204 | { |
| 205 | pDropdownList->SetText(Game.Teams.GetTeamDistString().c_str()); |
| 206 | } |
| 207 | |
| 208 | // C4GameOptionsList::OptionTeamColors |
| 209 | |
| 210 | C4GameOptionsList::OptionTeamColors::OptionTeamColors(C4GameOptionsList *pForDlg) |
| 211 | : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr(id: C4ResStrTableKey::IDS_MSG_TEAMCOLORS), !Game.Control.isCtrlHost()) |
| 212 | { |
| 213 | SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_TEAMCOLORS_DESC)); |
| 214 | } |
| 215 | |
| 216 | void C4GameOptionsList::OptionTeamColors::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller) |
| 217 | { |
| 218 | pFiller->AddEntry(szText: LoadResStr(id: C4ResStrTableKey::IDS_MSG_ENABLED), id: 1); |
| 219 | pFiller->AddEntry(szText: LoadResStr(id: C4ResStrTableKey::IDS_MSG_DISABLED), id: 0); |
| 220 | } |
| 221 | |
| 222 | void C4GameOptionsList::OptionTeamColors::DoDropdownSelChange(int32_t idNewSelection) |
| 223 | { |
| 224 | bool fEnabled = !!idNewSelection; |
| 225 | Game.Teams.SendSetTeamColors(fEnabled); |
| 226 | } |
| 227 | |
| 228 | void C4GameOptionsList::OptionTeamColors::Update() |
| 229 | { |
| 230 | pDropdownList->SetText(Game.Teams.IsTeamColors() ? LoadResStr(id: C4ResStrTableKey::IDS_MSG_ENABLED) : LoadResStr(id: C4ResStrTableKey::IDS_MSG_DISABLED)); |
| 231 | } |
| 232 | |
| 233 | C4GameOptionsList::OptionRandomTeamCount::OptionRandomTeamCount(C4GameOptionsList *forDlg) |
| 234 | : OptionDropdown(forDlg, LoadResStr(id: C4ResStrTableKey::IDS_MSG_RANDOMTEAMCOUNT), !Game.Network.isHost()) |
| 235 | { |
| 236 | SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_RANDOMTEAMCOUNT_DESC)); |
| 237 | } |
| 238 | |
| 239 | void C4GameOptionsList::OptionRandomTeamCount::DoDropdownFill(C4GUI::ComboBox_FillCB *filler) |
| 240 | { |
| 241 | filler->AddEntry(szText: LoadResStr(id: C4ResStrTableKey::IDS_MSG_TEAMCOUNT_AUTO), id: 0, desc: LoadResStr(id: C4ResStrTableKey::IDS_MSG_TEAMCOUNT_AUTO_DESC)); |
| 242 | for (int32_t i = 2, max = Game.Teams.IsAutoGenerateTeams() ? Game.PlayerInfos.GetActivePlayerCount(fCountInvisible: true) : Game.Teams.GetTeamCount(); i <= max; ++i) |
| 243 | { |
| 244 | filler->AddEntry(szText: std::to_string(val: i).c_str(), id: i); |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | void C4GameOptionsList::OptionRandomTeamCount::DoDropdownSelChange(int32_t newSelection) |
| 249 | { |
| 250 | Game.Teams.SetRandomTeamCount(newSelection); |
| 251 | } |
| 252 | |
| 253 | void C4GameOptionsList::OptionRandomTeamCount::Update() |
| 254 | { |
| 255 | const auto count = Game.Teams.GetRandomTeamCount(); |
| 256 | pDropdownList->SetText(count > 1 ? std::to_string(val: count).c_str() : LoadResStr(id: C4ResStrTableKey::IDS_MSG_TEAMCOUNT_AUTO)); |
| 257 | } |
| 258 | |
| 259 | // C4GameOptionsList |
| 260 | |
| 261 | C4GameOptionsList::C4GameOptionsList(const C4Rect &rcBounds, bool fActive, bool fRuntime) |
| 262 | : C4GUI::ListBox(rcBounds), pSec1Timer(nullptr), fRuntime(fRuntime) |
| 263 | { |
| 264 | // initial option fill |
| 265 | InitOptions(); |
| 266 | if (fActive) Activate(); |
| 267 | } |
| 268 | |
| 269 | void C4GameOptionsList::InitOptions() |
| 270 | { |
| 271 | // creates option selection components |
| 272 | new OptionControlMode(this); |
| 273 | new OptionControlRate(this); |
| 274 | if (Game.Network.isHost()) new OptionRuntimeJoin(this); |
| 275 | if (!IsRuntime()) |
| 276 | { |
| 277 | if (Game.Teams.HasTeamDistOptions()) new OptionTeamDist(this); |
| 278 | if (Game.Teams.IsMultiTeams()) new OptionTeamColors(this); |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | void C4GameOptionsList::Update() |
| 283 | { |
| 284 | const auto haveRandomTeamCount = !IsRuntime() && Game.Teams.HasTeamDistOptions() && Game.Network.isHost() && Game.Teams.IsRandomTeam(); |
| 285 | if (haveRandomTeamCount != (randomTeamCount != nullptr)) |
| 286 | { |
| 287 | if (!randomTeamCount) |
| 288 | { |
| 289 | randomTeamCount = new OptionRandomTeamCount{this}; |
| 290 | } |
| 291 | else |
| 292 | { |
| 293 | delete randomTeamCount; |
| 294 | randomTeamCount = nullptr; |
| 295 | } |
| 296 | } |
| 297 | // update all option items |
| 298 | for (Option *pItem = static_cast<Option *>(pClientWindow->GetFirst()); pItem; pItem = pItem->GetNext()) |
| 299 | pItem->Update(); |
| 300 | } |
| 301 | |
| 302 | void C4GameOptionsList::Activate() |
| 303 | { |
| 304 | // create timer if necessary |
| 305 | if (!pSec1Timer) pSec1Timer = new C4Sec1TimerCallback<C4GameOptionsList>(this); |
| 306 | // force an update |
| 307 | Update(); |
| 308 | } |
| 309 | |
| 310 | void C4GameOptionsList::Deactivate() |
| 311 | { |
| 312 | // release timer if set |
| 313 | if (pSec1Timer) |
| 314 | { |
| 315 | pSec1Timer->Release(); |
| 316 | pSec1Timer = nullptr; |
| 317 | } |
| 318 | } |
| 319 | |