1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/
5 * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16 // Custom game options and configuration dialog
17
18 #include "C4Include.h"
19 #include "gui/C4GameOptions.h"
20
21 #include "control/C4GameControl.h"
22 #include "game/C4Application.h"
23 #include "graphics/C4GraphicsResource.h"
24 #include "gui/C4GameLobby.h"
25 #include "gui/C4Startup.h"
26
27 // ----------- C4GameOptionsList::Option ----------------------------------------------------------------
28
Option(C4GameOptionsList * pForDlg)29 C4GameOptionsList::Option::Option(C4GameOptionsList *pForDlg) :
30 BaseClass(C4Rect(0, 0, 0, 0)), pForDlg(pForDlg), pPrimarySubcomponent(nullptr)
31 {}
32
InitOption(C4GameOptionsList * pForDlg)33 void C4GameOptionsList::Option::InitOption(C4GameOptionsList *pForDlg)
34 {
35 // post-call after initialization: Adds to list box and does initial update
36 // add to listbox (will eventually get moved)
37 pForDlg->AddElement(this);
38 // first-time update
39 Update();
40 }
41
42
43
44 // ----------- C4GameOptionsList::OptionDropdown ----------------------------------------------------------------
45
OptionDropdown(class C4GameOptionsList * pForDlg,const char * szCaption,bool fReadOnly)46 C4GameOptionsList::OptionDropdown::OptionDropdown(class C4GameOptionsList *pForDlg, const char *szCaption, bool fReadOnly)
47 : Option(pForDlg), fReadOnly(fReadOnly)
48 {
49 bool fIsPreGame = pForDlg->IsPreGame();
50 CStdFont &rUseFont = fIsPreGame ? C4Startup::Get()->Graphics.BookFont : ::GraphicsResource.TextFont;
51 uint32_t dwFontClr = fIsPreGame ? C4StartupFontClr : 0xffffffff;
52 // get size of caption label
53 bool fTabular = pForDlg->IsTabular();
54 int32_t iCaptWidth, iCaptHeight;
55 if (fTabular)
56 {
57 // tabular layout: Caption label width by largest caption on runtime; fixed 1/3rd on startup
58 rUseFont.GetTextExtent(LoadResStr("IDS_NET_RUNTIMEJOIN"), iCaptWidth, iCaptHeight, true);
59 if (pForDlg->IsPreGame())
60 {
61 iCaptWidth = pForDlg->GetItemWidth() * 1 / 3;
62 }
63 else
64 {
65 iCaptWidth = iCaptWidth * 5 / 4;
66 }
67 }
68 else
69 {
70 rUseFont.GetTextExtent(szCaption, iCaptWidth, iCaptHeight, true);
71 }
72 // calc total height for component
73 int iHorizontalMargin = 1;
74 int iVerticalMargin = 1;
75 int iComboMargin = 5;
76 int iSelComboHgt = C4GUI::ComboBox::GetDefaultHeight();
77 SetBounds(C4Rect(0, 0, pForDlg->GetItemWidth(), (!fTabular) * (iCaptHeight + iVerticalMargin*2) + iVerticalMargin*2 + iSelComboHgt));
78 C4GUI::ComponentAligner ca(GetContainedClientRect(), iHorizontalMargin, iVerticalMargin);
79 // create subcomponents
80 AddElement(pCaption = new C4GUI::Label(FormatString("%s:", szCaption).getData(), fTabular ? ca.GetFromLeft(iCaptWidth, iCaptHeight) : ca.GetFromTop(iCaptHeight), ALeft, dwFontClr, &rUseFont, false, false));
81 ca.ExpandLeft(-iComboMargin);
82 AddElement(pPrimarySubcomponent = pDropdownList = new C4GUI::ComboBox(ca.GetAll()));
83 pDropdownList->SetReadOnly(fReadOnly);
84 pDropdownList->SetComboCB(new C4GUI::ComboBox_FillCallback<C4GameOptionsList::OptionDropdown>(this, &C4GameOptionsList::OptionDropdown::OnDropdownFill, &C4GameOptionsList::OptionDropdown::OnDropdownSelChange));
85 if (fIsPreGame)
86 {
87 pDropdownList->SetColors(C4StartupFontClr, C4StartupEditBGColor, C4StartupEditBorderColor);
88 pDropdownList->SetFont(&rUseFont);
89 pDropdownList->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
90 }
91 // final init
92 InitOption(pForDlg);
93 }
94
95
96
97 // ----------- C4GameOptionsList::OptionScenarioParameter----------------------------------------------------------------
98
OptionScenarioParameter(class C4GameOptionsList * pForDlg,const class C4ScenarioParameterDef * parameter_def)99 C4GameOptionsList::OptionScenarioParameter::OptionScenarioParameter(class C4GameOptionsList *pForDlg, const class C4ScenarioParameterDef *parameter_def)
100 : C4GameOptionsList::OptionDropdown(pForDlg, parameter_def->GetName(), !pForDlg->IsPreGame() && (!::Control.isCtrlHost() || ::Game.C4S.Head.SaveGame)), ParameterDef(parameter_def), LastValue(0), LastValueValid(false)
101 {
102 SetToolTip(parameter_def->GetDescription());
103 }
104
DoDropdownFill(C4GUI::ComboBox_FillCB * pFiller)105 void C4GameOptionsList::OptionScenarioParameter::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller)
106 {
107 // Fill dropdown menuy with known possible options for this parameter
108 size_t idx=0; const C4ScenarioParameterDef::Option *option;
109 while ((option = ParameterDef->GetOptionByIndex(idx++)))
110 {
111 pFiller->AddEntry(option->Name.getData(), option->Value);
112 }
113 }
114
DoDropdownSelChange(int32_t idNewSelection)115 void C4GameOptionsList::OptionScenarioParameter::DoDropdownSelChange(int32_t idNewSelection)
116 {
117 // runtime change needs to be synchronized
118 if (!pForDlg->IsPreGame())
119 {
120 // change possible?
121 if (!::Control.isCtrlHost()) return;
122 // Then initiate an update of the parameters on all clients
123 C4GameLobby::C4PacketSetScenarioParameter pck(ParameterDef->GetID(), idNewSelection);
124 ::Network.Clients.BroadcastMsgToClients(MkC4NetIOPacket(PID_SetScenarioParameter, pck));
125 }
126 // also process on host (and standalone pre-game)
127 pForDlg->GetParameters()->SetValue(ParameterDef->GetID(), idNewSelection, false);
128 if (pForDlg->IsPreGame()) Update();
129 }
130
Update()131 void C4GameOptionsList::OptionScenarioParameter::Update()
132 {
133 int32_t val=0;
134 // display forced league value?
135 bool fLeagueReadOnly = false;
136 if (::Config.Network.LeagueServerSignUp && !fReadOnly && !pForDlg->IsPreGameSingle()) val = ParameterDef->GetLeagueValue();
137 if (val)
138 fLeagueReadOnly = true;
139 else
140 val = pForDlg->GetParameters()->GetValueByID(ParameterDef->GetID(), ParameterDef->GetDefault());
141 if (!fReadOnly) pDropdownList->SetReadOnly(fLeagueReadOnly);
142 // update data to currently set option
143 if (LastValueValid && val == LastValue) return;
144 const C4ScenarioParameterDef::Option *option = ParameterDef->GetOptionByValue(val);
145 if (option)
146 pDropdownList->SetText(option->Name.getData());
147 else
148 pDropdownList->SetText(FormatString("%d", (int)val).getData());
149 LastValueValid = true;
150 LastValue = val;
151 }
152
153
154
155 // ----------- C4GameOptionsList::OptionControlMode ----------------------------------------------------------------
156
157 // Unfortunately, the control mode cannot be changed in the lobby
OptionControlMode(class C4GameOptionsList * pForDlg)158 C4GameOptionsList::OptionControlMode::OptionControlMode(class C4GameOptionsList *pForDlg)
159 : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr("IDS_TEXT_CONTROLMODE"), !::Control.isCtrlHost() || !::Control.isNetwork() || !::Control.Network.IsEnabled())
160 {
161 SetToolTip(LoadResStr("IDS_DESC_CHANGESTHEWAYCONTROLDATAI"));
162 }
163
DoDropdownFill(C4GUI::ComboBox_FillCB * pFiller)164 void C4GameOptionsList::OptionControlMode::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller)
165 {
166 // change possible?
167 if (!::Control.isNetwork() || !::Control.Network.IsEnabled() || !::Control.isCtrlHost()) return;
168 // add possible modes
169 pFiller->AddEntry(LoadResStr("IDS_NET_CTRLMODE_CENTRAL"), CNM_Central);
170 pFiller->AddEntry(LoadResStr("IDS_NET_CTRLMODE_DECENTRAL"), CNM_Decentral);
171 }
172
DoDropdownSelChange(int32_t idNewSelection)173 void C4GameOptionsList::OptionControlMode::DoDropdownSelChange(int32_t idNewSelection)
174 {
175 // change possible?
176 if (!::Control.isNetwork() || !::Control.Network.IsEnabled() || !::Control.isCtrlHost()) return;
177 // perform it
178 ::Network.SetCtrlMode(idNewSelection);
179 // update for clients done by packet; host needs to set it manually
180 Update();
181 }
182
Update()183 void C4GameOptionsList::OptionControlMode::Update()
184 {
185 const char *szControlMode;
186 if (!::Control.isNetwork() || !::Control.Network.IsEnabled())
187 szControlMode = LoadResStr("IDS_NET_NONET");
188 else
189 {
190 switch (::Network.Status.getCtrlMode())
191 {
192 case CNM_Central: szControlMode = LoadResStr("IDS_NET_CTRLMODE_CENTRAL"); break;
193 case CNM_Decentral: szControlMode = LoadResStr("IDS_NET_CTRLMODE_DECENTRAL"); break;
194 default: szControlMode = LoadResStr("IDS_NET_CTRLMODE_NONE"); break;
195 }
196 }
197 pDropdownList->SetText(szControlMode);
198 }
199
200
201 // ----------- C4GameOptionsList::OptionControlRate ----------------------------------------------------------------
202
OptionControlRate(class C4GameOptionsList * pForDlg)203 C4GameOptionsList::OptionControlRate::OptionControlRate(class C4GameOptionsList *pForDlg)
204 : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr("IDS_CTL_CONTROLRATE"), !::Control.isCtrlHost())
205 {
206 SetToolTip(LoadResStr("IDS_CTL_CONTROLRATE_DESC"));
207 }
208
DoDropdownFill(C4GUI::ComboBox_FillCB * pFiller)209 void C4GameOptionsList::OptionControlRate::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller)
210 {
211 for (int i = 1; i < std::min(C4MaxControlRate, 10); ++i)
212 pFiller->AddEntry(FormatString("%d", i).getData(), i);
213 }
214
DoDropdownSelChange(int32_t idNewSelection)215 void C4GameOptionsList::OptionControlRate::DoDropdownSelChange(int32_t idNewSelection)
216 {
217 // adjust rate
218 int32_t iNewRate = idNewSelection;
219 if (!iNewRate || iNewRate == ::Control.ControlRate) return;
220 ::Control.AdjustControlRate(iNewRate - ::Control.ControlRate);
221 }
222
Update()223 void C4GameOptionsList::OptionControlRate::Update()
224 {
225 if (atoi(pDropdownList->GetText().getData()) == ::Control.ControlRate) return;
226 pDropdownList->SetText(FormatString("%d", ::Control.ControlRate).getData());
227 }
228
229
230 // ----------- C4GameOptionsList::OptionRuntimeJoin ----------------------------------------------------------------
231
OptionRuntimeJoin(class C4GameOptionsList * pForDlg)232 C4GameOptionsList::OptionRuntimeJoin::OptionRuntimeJoin(class C4GameOptionsList *pForDlg)
233 : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr("IDS_NET_RUNTIMEJOIN"), !::Network.isHost())
234 {
235 SetToolTip(LoadResStr("IDS_NET_RUNTIMEJOIN_DESC"));
236 }
237
DoDropdownFill(C4GUI::ComboBox_FillCB * pFiller)238 void C4GameOptionsList::OptionRuntimeJoin::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller)
239 {
240 pFiller->AddEntry(LoadResStr("IDS_NET_RUNTIMEJOINBARRED"), 0);
241 pFiller->AddEntry(LoadResStr("IDS_NET_RUNTIMEJOINFREE"), 1);
242 }
243
DoDropdownSelChange(int32_t idNewSelection)244 void C4GameOptionsList::OptionRuntimeJoin::DoDropdownSelChange(int32_t idNewSelection)
245 {
246 // adjust mode
247 bool fAllowed = !!idNewSelection;
248 Config.Network.NoRuntimeJoin = !fAllowed;
249 if (Game.IsRunning) ::Network.AllowJoin(fAllowed);
250 }
251
Update()252 void C4GameOptionsList::OptionRuntimeJoin::Update()
253 {
254 const char *szText;
255 if (Config.Network.NoRuntimeJoin)
256 szText = LoadResStr("IDS_NET_RUNTIMEJOINBARRED");
257 else
258 szText = LoadResStr("IDS_NET_RUNTIMEJOINFREE");
259 pDropdownList->SetText(szText);
260 }
261
262
263 // ----------- C4GameOptionsList::OptionTeamDist ----------------------------------------------------------------
264
OptionTeamDist(class C4GameOptionsList * pForDlg)265 C4GameOptionsList::OptionTeamDist::OptionTeamDist(class C4GameOptionsList *pForDlg)
266 : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr("IDS_MSG_TEAMDIST"), !::Control.isCtrlHost())
267 {
268 SetToolTip(LoadResStr("IDS_MSG_TEAMDIST_DESC"));
269 }
270
DoDropdownFill(C4GUI::ComboBox_FillCB * pFiller)271 void C4GameOptionsList::OptionTeamDist::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller)
272 {
273 Game.Teams.FillTeamDistOptions(pFiller);
274 }
275
DoDropdownSelChange(int32_t idNewSelection)276 void C4GameOptionsList::OptionTeamDist::DoDropdownSelChange(int32_t idNewSelection)
277 {
278 // adjust team distribution
279 Game.Teams.SendSetTeamDist(C4TeamList::TeamDist(idNewSelection));
280 }
281
Update()282 void C4GameOptionsList::OptionTeamDist::Update()
283 {
284 StdStrBuf sOption; sOption.Take(Game.Teams.GetTeamDistString());
285 pDropdownList->SetText(sOption.getData());
286 }
287
288
289 // ----------- C4GameOptionsList::OptionTeamColors ----------------------------------------------------------------
290
OptionTeamColors(class C4GameOptionsList * pForDlg)291 C4GameOptionsList::OptionTeamColors::OptionTeamColors(class C4GameOptionsList *pForDlg)
292 : C4GameOptionsList::OptionDropdown(pForDlg, LoadResStr("IDS_MSG_TEAMCOLORS"), !::Control.isCtrlHost())
293 {
294 SetToolTip(LoadResStr("IDS_MSG_TEAMCOLORS_DESC"));
295 }
296
DoDropdownFill(C4GUI::ComboBox_FillCB * pFiller)297 void C4GameOptionsList::OptionTeamColors::DoDropdownFill(C4GUI::ComboBox_FillCB *pFiller)
298 {
299 pFiller->AddEntry(LoadResStr("IDS_MSG_ENABLED"), 1);
300 pFiller->AddEntry(LoadResStr("IDS_MSG_DISABLED"), 0);
301 }
302
DoDropdownSelChange(int32_t idNewSelection)303 void C4GameOptionsList::OptionTeamColors::DoDropdownSelChange(int32_t idNewSelection)
304 {
305 bool fEnabled = !!idNewSelection;
306 Game.Teams.SendSetTeamColors(fEnabled);
307 }
308
Update()309 void C4GameOptionsList::OptionTeamColors::Update()
310 {
311 pDropdownList->SetText(Game.Teams.IsTeamColors() ? LoadResStr("IDS_MSG_ENABLED") : LoadResStr("IDS_MSG_DISABLED"));
312 }
313
314 // ----------- C4GameOptionsList -----------------------------------------------------------------------
315
C4GameOptionsList(const C4Rect & rcBounds,bool fActive,C4GameOptionsListSource source,C4ScenarioParameterDefs * param_defs,C4ScenarioParameters * params)316 C4GameOptionsList::C4GameOptionsList(const C4Rect &rcBounds, bool fActive, C4GameOptionsListSource source, C4ScenarioParameterDefs *param_defs, C4ScenarioParameters *params)
317 : C4GUI::ListBox(rcBounds), source(source), param_defs(param_defs), params(params)
318 {
319 // default parameter defs
320 if (!IsPreGame())
321 {
322 if (!this->param_defs) this->param_defs = &::Game.ScenarioParameterDefs;
323 if (!this->params) this->params = &::Game.Parameters.ScenarioParameters;
324 }
325 // initial option fill
326 InitOptions();
327 if (fActive) Activate();
328 }
329
InitOptions()330 void C4GameOptionsList::InitOptions()
331 {
332 // create options for custom scenario parameters
333 if (param_defs)
334 {
335 size_t idx = 0; const C4ScenarioParameterDef *def;
336 while ((def = param_defs->GetParameterDefByIndex(idx++)))
337 if (!def->IsAchievement()) // achievements are displayed in scenario selection. no need to repeat them here
338 new OptionScenarioParameter(this, def);
339 }
340 // create lobby and runtime option selection components
341 if (!IsPreGame())
342 {
343 new OptionControlMode(this);
344 new OptionControlRate(this);
345 if (::Network.isHost()) new OptionRuntimeJoin(this);
346 if (!IsRuntime())
347 {
348 if (Game.Teams.HasTeamDistOptions()) new OptionTeamDist(this);
349 if (Game.Teams.IsMultiTeams()) new OptionTeamColors(this);
350 }
351 }
352 }
353
ClearOptions()354 void C4GameOptionsList::ClearOptions()
355 {
356 C4GUI::Element *pFirst;
357 while ((pFirst = GetFirst())) delete pFirst;
358 }
359
Update()360 void C4GameOptionsList::Update()
361 {
362 // update all option items
363 for (Option *pItem = static_cast<Option *>(pClientWindow->GetFirst()); pItem; pItem = pItem->GetNext())
364 pItem->Update();
365 }
366
Activate()367 void C4GameOptionsList::Activate()
368 {
369 // register timer if necessary
370 Application.Add(this);
371 // force an update
372 Update();
373 }
374
Deactivate()375 void C4GameOptionsList::Deactivate()
376 {
377 // release timer if set
378 Application.Remove(this);
379 }
380
SetParameters(C4ScenarioParameterDefs * param_defs,C4ScenarioParameters * params)381 void C4GameOptionsList::SetParameters(C4ScenarioParameterDefs *param_defs, C4ScenarioParameters *params)
382 {
383 // update to new parameter set
384 ClearOptions();
385 this->param_defs = param_defs;
386 this->params = params;
387 InitOptions();
388 }
389
390