1 /////////////////////////////////////////
2 //
3 // OpenLieroX
4 //
5 // code under LGPL, based on JasonBs work,
6 // enhanced by Dark Charlie and Albert Zeyer
7 //
8 //
9 /////////////////////////////////////////
10
11
12 // Local menu
13 // Created 30/6/02
14 // Jason Boettcher
15
16
17 #include <assert.h>
18 #include <string>
19
20 #include "LieroX.h"
21 #include "CGameScript.h"
22 #include "AuxLib.h"
23 #include "DeprecatedGUI/Graphics.h"
24 #include "CClient.h"
25 #include "CServer.h"
26 #include "DeprecatedGUI/Menu.h"
27 #include "DeprecatedGUI/CListview.h"
28 #include "GfxPrimitives.h"
29 #include "FindFile.h"
30 #include "StringUtils.h"
31 #include "DeprecatedGUI/CButton.h"
32 #include "DeprecatedGUI/CLabel.h"
33 #include "DeprecatedGUI/CImage.h"
34 #include "DeprecatedGUI/CTextbox.h"
35 #include "DeprecatedGUI/CSlider.h"
36 #include "DeprecatedGUI/CCheckbox.h"
37 #include "DeprecatedGUI/CTextButton.h"
38 #include "DeprecatedGUI/CListview.h"
39 #include "DeprecatedGUI/CGuiSkinnedLayout.h"
40 #include "DeprecatedGUI/CBrowser.h"
41 #include "Sounds.h"
42 #include "ProfileSystem.h"
43 #include "FeatureList.h"
44 #include "Options.h"
45 #include "CGameMode.h"
46
47
48 namespace DeprecatedGUI {
49
50
51 /*
52 =======================
53
54 Game Settings
55
56 For both local & host
57
58 =======================
59 */
60
61 //short GameTabPane = 0;
62 CGuiLayout cGameSettings;
63 //CGuiLayout cGeneralSettings;
64 //CGuiLayout cBonusSettings;
65
66 // Game Settings
67 enum {
68 gs_Ok,
69 gs_Default,
70 gs_AdvancedLevel,
71 gs_AdvancedLevelLabel,
72
73 gs_FeaturesList,
74 gs_FeaturesListLabel,
75
76 };
77
78 static void initFeaturesList(CListview* l);
79
80 ///////////////////
81 // Initialize the game settings
Menu_GameSettings()82 void Menu_GameSettings()
83 {
84 //GameTabPane = 0;
85 // Setup the buffer
86 Menu_DrawBox(tMenu->bmpBuffer.get(), 80,120, 560,460);
87 DrawRectFillA(tMenu->bmpBuffer.get(), 82,122, 558,458, tLX->clDialogBackground, 245);
88
89 Menu_RedrawMouse(true);
90
91
92 // Shutdowns all 3 following instances
93 Menu_GameSettingsShutdown();
94
95 cGameSettings.Initialize();
96 //cGeneralSettings.Initialize();
97
98 // Keep text, it's the window text - the rest you can easily figure out by yourself.
99 cGameSettings.Add( new CLabel("Game Settings", tLX->clNormalLabel), -1, 280,135, 0, 0);
100
101 // Game settings, stuff on each pane.
102
103 cGameSettings.Add( new CButton(BUT_OK, DeprecatedGUI::tMenu->bmpButtons), gs_Ok, 180,435, 40,15);
104 cGameSettings.Add( new CButton(BUT_DEFAULT, DeprecatedGUI::tMenu->bmpButtons), gs_Default, 390,435, 80,15);
105
106 cGameSettings.Add( new CSlider(__AdvancedLevelType_Count - 1, 0, tLXOptions->iAdvancedLevelLimit), gs_AdvancedLevel, 365, 155, 80,15);
107 cGameSettings.Add( new CLabel("Detail Level:", tLX->clNormalLabel), -1, 285, 155, 70, 15);
108 float warningCoeff = CLAMP((float)tLXOptions->iAdvancedLevelLimit / (__AdvancedLevelType_Count - 1), 0.0f, 1.0f);
109 cGameSettings.Add( new CLabel(AdvancedLevelShortDescription((AdvancedLevel)tLXOptions->iAdvancedLevelLimit), tLX->clNormalLabel * (1.0f - warningCoeff) + tLX->clError * warningCoeff), gs_AdvancedLevelLabel, 450, 155, 70, 15);
110
111 CListview* features = new CListview();
112 cGameSettings.Add( features, gs_FeaturesList, 95, 170, 450, 205);
113
114 features->setDrawBorder(true);
115 features->setRedrawMenu(false);
116 features->setShowSelect(false);
117 features->setOldStyle(true);
118 features->subItemsAreAligned() = true;
119 features->setMouseOverEventEnabled(true);
120
121 int maxWidth = 0; // Width of the widest item in this column + some space
122 CScriptableVars::const_iterator upper_bound = CScriptableVars::upper_bound("GameOptions.");
123 for( CScriptableVars::const_iterator it = CScriptableVars::lower_bound("GameOptions."); it != upper_bound; it++ )
124 {
125 if( tLX->cFont.GetWidth(it->second.shortDesc) > maxWidth )
126 maxWidth = tLX->cFont.GetWidth(it->second.shortDesc);
127 }
128
129 features->AddColumn("", maxWidth + 10);
130 features->AddColumn("", 190);
131
132 if( Menu_IsKeyboardNavigationUsed() ) {
133 // Expand every group
134 for( int group = 0; group < GIG_Size; group++ ) {
135 tLXOptions->iGameInfoGroupsShown[group] = true;
136 }
137 }
138
139 initFeaturesList(features);
140
141 cGameSettings.Add( new CLabel("", tLX->clNormalLabel), gs_FeaturesListLabel, 95, 390, 450, 40);
142 }
143
144 // Features listview
145
addFeautureListGroupHeading(CListview * l,GameInfoGroup group)146 static void addFeautureListGroupHeading(CListview* l, GameInfoGroup group) {
147 if( l->getItemCount() > 0 )
148 l->AddItem("", l->getNumItems(), tLX->clNormalLabel); // Empty line
149 l->AddItem(GameInfoGroupDescriptions[group][0], l->getNumItems(), tLX->clHeading);
150 l->AddSubitem(LVS_TEXT, std::string("--- ") + GameInfoGroupDescriptions[group][0] + " ---" +
151 (tLXOptions->iGameInfoGroupsShown[group] ? " [-]" : " [+]"), (DynDrawIntf*)NULL, NULL);
152 }
153
getListItemGroupInfoNr(const std::string & sindex)154 static int getListItemGroupInfoNr(const std::string& sindex) {
155 for( int group = 0; group < GIG_Size; group++ )
156 if( sindex == GameInfoGroupDescriptions[group][0] )
157 return group;
158 return -1;
159 }
160
161
updateFeatureListItemColor(lv_item_t * item)162 static void updateFeatureListItemColor(lv_item_t* item) {
163 lv_subitem_t* sub = item->tSubitems; if(!sub) return;
164 if(((CListview*)cGameSettings.getWidget(gs_FeaturesList))->getMouseOverSIndex() == item->sIndex) {
165 sub->iColour = tLX->clMouseOver;
166 return;
167 }
168
169 int group = getListItemGroupInfoNr(item->sIndex);
170 if(group >= 0) sub->iColour = tLX->clHeading;
171 else {
172 // Note: commented out because I am not sure if it is that nice
173 //RegisteredVar* var = CScriptableVars::GetVar(item->sIndex);
174 float warningCoeff = 0.0f; //CLAMP((float)var->advancedLevel / (__AdvancedLevelType_Count - 1), 0.0f, 1.0f);
175 sub->iColour = tLX->clNormalLabel * (1.0f - warningCoeff) + tLX->clError * warningCoeff;
176 }
177 }
178
179
initFeaturesList(CListview * l)180 static void initFeaturesList(CListview* l)
181 {
182 l->Clear();
183 for( GameInfoGroup group = (GameInfoGroup)0; group < GIG_Size; group = (GameInfoGroup)(group + 1) )
184 {
185 if( group == GIG_GameModeSpecific_Start )
186 continue;
187 if( group > GIG_GameModeSpecific_Start &&
188 tLXOptions->tGameInfo.gameMode->getGameInfoGroupInOptions() != group )
189 continue;
190
191 size_t countGroupOpts = 0;
192 CScriptableVars::const_iterator upper_bound = CScriptableVars::upper_bound("GameOptions.");
193 for( CScriptableVars::const_iterator it = CScriptableVars::lower_bound("GameOptions."); it != upper_bound; it++ )
194 {
195 if( it->second.group != group ) continue;
196 if( (int)it->second.advancedLevel > tLXOptions->iAdvancedLevelLimit ) continue;
197
198 if( it->second.var.s == &tLXOptions->tGameInfo.sModDir ||
199 it->second.var.s == &tLXOptions->tGameInfo.sMapFile ||
200 it->first == "GameOptions.GameInfo.GameType" ||
201 it->second.var.i == &tLXOptions->tGameInfo.iMaxPlayers )
202 continue; // We have nice comboboxes for them, skip them in the list
203
204 if( tMenu && tMenu->iMenuType == MNU_LOCAL )
205 if( it->second.var.b == &tLXOptions->tGameInfo.bAllowConnectDuringGame )
206 continue;
207
208 if( !tLXOptions->tGameInfo.gameMode || !tLXOptions->tGameInfo.gameMode->isTeamGame() ) {
209 if( it->second.var.i == &tLXOptions->iRandomTeamForNewWorm ) continue;
210 if( it->second.var.b == &tLXOptions->tGameInfo.bRespawnGroupTeams ) continue;
211 if( it->second.var == &tLXOptions->tGameInfo.features[FT_TeamScoreLimit] ) continue;
212 if( it->second.var == &tLXOptions->tGameInfo.features[FT_TeamkillDecreasesScore] ) continue;
213 if( it->second.var == &tLXOptions->tGameInfo.features[FT_TeamInjure] ) continue;
214 if( it->second.var == &tLXOptions->tGameInfo.features[FT_TeamHit] ) continue;
215 }
216
217 if(countGroupOpts == 0)
218 addFeautureListGroupHeading(l, group);
219 countGroupOpts++;
220
221 if( ! tLXOptions->iGameInfoGroupsShown[group] )
222 continue;
223
224 lv_item_t * item = l->AddItem(it->first, l->getNumItems(), tLX->clNormalLabel);
225 updateFeatureListItemColor(item);
226 l->AddSubitem(LVS_TEXT, it->second.shortDesc, (DynDrawIntf*)NULL, NULL);
227 item->iHeight = 24; // So checkbox / textbox will fit okay
228
229 if( it->second.var.type == SVT_BOOL )
230 {
231 CCheckbox * cb = new CCheckbox( * it->second.var.b );
232 l->AddSubitem(LVS_WIDGET, "", (DynDrawIntf*)NULL, cb);
233 cb->Create();
234 cb->Setup(-1, 0, 0, 20, 20);
235 }
236 else
237 {
238 int textboxSize = 228;
239 if( it->second.haveMinMax() )
240 {
241 int imin=0, imax=0;
242 float fScale = 1.0f;
243 int iVal = 0;
244 if( it->second.var.type == SVT_FLOAT )
245 {
246 // Adding some small number to round it up correctly
247 imin = int( float(it->second.min) *10.0f + 0.00001f ); // Scale them up
248 imax = int( float(it->second.max) *10.0f + 0.00001f );
249 iVal = int( (*it->second.var.f) * 10.0f + 0.00001f );
250 fScale = 0.1f;
251 } else {
252 imin = it->second.min;
253 imax = it->second.max;
254 iVal = * it->second.var.i;
255 }
256 CSlider * sld = new CSlider( imax, imin, imin, false, 190, 0, tLX->clNormalLabel, fScale );
257 CLAMP_DIRECT(iVal, sld->getMin(), sld->getMax() );
258 sld->setValue(iVal);
259 l->AddSubitem(LVS_WIDGET, "", (DynDrawIntf*)NULL, sld);
260 sld->Create();
261 sld->Setup(-1, 0, 0, 180, tLX->cFont.GetHeight());
262 textboxSize = 40;
263 }
264 CTextbox * txt = new CTextbox();
265 l->AddSubitem(LVS_WIDGET, "", (DynDrawIntf*)NULL, txt);
266 txt->Create();
267 txt->Setup(-1, 0, 0, textboxSize, tLX->cFont.GetHeight());
268 if ((it->second.var.type == SVT_INT && it->second.var.isUnsigned && *it->second.var.i < 0) ||
269 (it->second.var.type == SVT_FLOAT && it->second.var.isUnsigned && *it->second.var.f < 0))
270 txt->setText(""); // Leave blank for infinite values
271 else
272 txt->setText( it->second.var.toString() );
273 }
274 }
275 }
276 l->AddItem("", l->getNumItems(), tLX->clNormalLabel); // Empty line to fix keyboard navigation bug
277 }
278
279
280 // Copy values from listview to features list
updateFeaturesList(CListview * l)281 static void updateFeaturesList(CListview* l)
282 {
283 CScriptableVars::const_iterator upper_bound = CScriptableVars::upper_bound("GameOptions.");
284 for( CScriptableVars::const_iterator it = CScriptableVars::lower_bound("GameOptions."); it != upper_bound; it++ )
285 {
286 if( it->second.group == GIG_Invalid ) continue;
287
288 lv_item_t * item = l->getItem(it->first);
289 if( ! item )
290 continue;
291 lv_subitem_t * si = item->tSubitems->tNext;
292 if( ! si )
293 continue;
294 CWidget * w = si->tWidget;
295 if( ! w )
296 continue;
297
298 if( it->second.var.type == SVT_BOOL )
299 {
300 if( w->getType() == wid_Checkbox )
301 {
302 * it->second.var.b = ((CCheckbox *)w)->getValue();
303 }
304 }
305 else
306 {
307 if( w->getType() == wid_Textbox )
308 {
309 it->second.var.fromString( ((CTextbox *)w)->getText() );
310 }
311 if( w->getType() == wid_Slider &&
312 si->tNext && si->tNext->tWidget && si->tNext->tWidget->getType() == wid_Textbox &&
313 l->getWidgetEvent() && l->getWidgetEvent()->cWidget )
314 {
315 CSlider *slider = (CSlider *)w;
316 CTextbox *textBox = (CTextbox *)si->tNext->tWidget;
317
318 if( l->getWidgetEvent()->cWidget->getType() == wid_Slider ) // User moved slider - update textbox
319 {
320 int iVal = slider->getValue();
321 if( it->second.var.type == SVT_INT )
322 {
323 * it->second.var.i = iVal;
324 textBox->setText(itoa(iVal));
325 if( it->second.var.isUnsigned && iVal < 0 )
326 textBox->setText("");
327 }
328 if( it->second.var.type == SVT_FLOAT )
329 {
330 * it->second.var.f = iVal / 10.0f;
331 textBox->setText(to_string<float>(iVal / 10.0f));
332 if( it->second.var.isUnsigned && iVal < 0 )
333 textBox->setText("");
334 }
335 }
336 if( l->getWidgetEvent()->cWidget->getType() == wid_Textbox ) // User typed in textbox - update slider
337 {
338 it->second.var.fromString(textBox->getText());
339 int iVal = 0;
340 if( it->second.var.type == SVT_INT )
341 {
342 // Do not do min/max check on typed value, it's sole user responsibility if game crashes (though it should not)
343 //CLAMP_DIRECT(* it->second.var.i, it->second.min.i, it->second.max.i );
344 iVal = * it->second.var.i;
345 }
346 if( it->second.var.type == SVT_FLOAT )
347 {
348 // Do not do min/max check on typed value, it's sole user responsibility if game crashes (though it should not)
349 //CLAMP_DIRECT(*it->second.var.f, it->second.min.f, it->second.max.f );
350 iVal = int(* it->second.var.f * 10.0f);
351 }
352 CLAMP_DIRECT(iVal, slider->getMin(), slider->getMax() );
353 slider->setValue(iVal);
354 }
355 }
356 }
357 }
358 if( tLXOptions->tGameInfo.iLives < 0 )
359 tLXOptions->tGameInfo.iLives = WRM_UNLIM;
360 }
361
362 /////////////
363 // Shutdown
Menu_GameSettingsShutdown()364 void Menu_GameSettingsShutdown()
365 {
366 cGameSettings.Shutdown();
367 }
368
369
370
371 ///////////////////
372 // Game settings frame
373 // Returns whether of not we have finised with the game settings
Menu_GameSettings_Frame()374 bool Menu_GameSettings_Frame()
375 {
376 gui_event_t *ev = NULL;
377
378 ev = cGameSettings.Process();
379
380 if(ev)
381 {
382
383 switch(ev->iControlID)
384 {
385
386 // OK, done
387 case gs_Ok:
388 if(ev->iEventMsg == BTN_CLICKED)
389 {
390 Menu_GameSettings_GrabInfo();
391 Menu_GameSettingsShutdown();
392
393 return true;
394 }
395 break;
396
397 // Set the default values
398 case gs_Default:
399 if( ev->iEventMsg == BTN_CLICKED ) {
400 Menu_GameSettings_Default();
401 }
402 break;
403
404 case gs_AdvancedLevel:
405 if( ev->iEventMsg == SLD_CHANGE ) {
406 tLXOptions->iAdvancedLevelLimit = ((CSlider*)cGameSettings.getWidget(gs_AdvancedLevel))->getValue();
407 CListview* features = (CListview*)cGameSettings.getWidget(gs_FeaturesList);
408 features->SaveScrollbarPos();
409 initFeaturesList(features);
410 features->RestoreScrollbarPos();
411
412 CLabel* featuresLabel = (CLabel*)cGameSettings.getWidget(gs_FeaturesListLabel);
413 float warningCoeff = CLAMP((float)tLXOptions->iAdvancedLevelLimit / (__AdvancedLevelType_Count - 1), 0.0f, 1.0f);
414 featuresLabel->ChangeColour( tLX->clNormalLabel * (1.0f - warningCoeff) + tLX->clError * warningCoeff );
415 featuresLabel->setText( splitStringWithNewLine(AdvancedLevelDescription((AdvancedLevel)tLXOptions->iAdvancedLevelLimit), (size_t)-1, 450, tLX->cFont) );
416
417 CLabel* advancenessLabel = (CLabel*)cGameSettings.getWidget(gs_AdvancedLevelLabel);
418 advancenessLabel->setText( AdvancedLevelShortDescription((AdvancedLevel)tLXOptions->iAdvancedLevelLimit) );
419 advancenessLabel->ChangeColour( tLX->clNormalLabel * (1.0f - warningCoeff) + tLX->clError * warningCoeff );
420 }
421 break;
422
423 case gs_FeaturesList:
424 CListview* features = (CListview*)ev->cWidget;
425 if( ev->iEventMsg == LV_WIDGETEVENT )
426 {
427 updateFeaturesList(features);
428 }
429 if( ev->iEventMsg == LV_MOUSEOVER )
430 {
431 CLabel* featuresLabel = (CLabel*)cGameSettings.getWidget(gs_FeaturesListLabel);
432 featuresLabel->ChangeColour( tLX->clNormalLabel );
433 featuresLabel->setText( "" );
434 for(lv_item_t* item = features->getItems(); item != NULL; item = item->tNext)
435 updateFeatureListItemColor(item);
436 if( features->getMouseOverSIndex() != "" )
437 {
438 {
439 lv_item_t* item = features->getItem(features->getMouseOverSIndex());
440 if(item && item->tSubitems)
441 item->tSubitems->iColour = tLX->clMouseOver;
442 }
443 std::string desc;
444 {
445 int group = getListItemGroupInfoNr(features->getMouseOverSIndex());
446 if(group >= 0)
447 desc = GameInfoGroupDescriptions[group][1];
448 }
449 if(desc == "") {
450 RegisteredVar* var = CScriptableVars::GetVar( features->getMouseOverSIndex() );
451 if(var) {
452 desc = var->longDesc;
453 float warningCoeff = CLAMP((float)var->advancedLevel / (__AdvancedLevelType_Count - 1), 0.0f, 1.0f);
454 featuresLabel->ChangeColour( tLX->clNormalLabel * (1.0f - warningCoeff) + tLX->clError * warningCoeff );
455 }
456 }
457 featuresLabel->setText( splitStringWithNewLine(desc, (size_t)-1, 450, tLX->cFont) );
458 }
459 }
460 if( ev->iEventMsg == LV_CHANGED && !Menu_IsKeyboardNavigationUsed() )
461 {
462 for( int group = 0; group < GIG_Size; group++ )
463 if( features->getMouseOverSIndex() == GameInfoGroupDescriptions[group][0] ) {
464 tLXOptions->iGameInfoGroupsShown[group] = ! tLXOptions->iGameInfoGroupsShown[group];
465 features->SaveScrollbarPos();
466 initFeaturesList(features);
467 features->RestoreScrollbarPos();
468 break;
469 }
470 }
471 break;
472 }
473
474 }
475
476 DrawImageAdv(VideoPostProcessor::videoSurface(), tMenu->bmpBuffer, 120,150, 120,150, 400,300);
477 cGameSettings.Draw(VideoPostProcessor::videoSurface());
478
479 // Draw the mouse
480 DrawCursor(VideoPostProcessor::videoSurface());
481
482 return false;
483 }
484
485
486 ///////////////////
487 // Grab the game settings info
Menu_GameSettings_GrabInfo()488 void Menu_GameSettings_GrabInfo()
489 {
490 // Stub
491 }
492
493
494 ///////////////////
495 // Set the default game settings info
Menu_GameSettings_Default()496 void Menu_GameSettings_Default()
497 {
498 CScriptableVars::const_iterator upper_bound = CScriptableVars::upper_bound("GameOptions.");
499 for( CScriptableVars::const_iterator it = CScriptableVars::lower_bound("GameOptions."); it != upper_bound; it++ )
500 {
501 if( it->second.group == GIG_Invalid ) continue;
502
503 if( it->first == "GameOptions.GameInfo.ModName" ||
504 it->first == "GameOptions.GameInfo.LevelName" ||
505 it->first == "GameOptions.GameInfo.GameType" )
506 continue; // We have nice comboboxes for them, skip them in the list
507
508 it->second.var.setDefault();
509 }
510
511 CListview * features = (CListview *)cGameSettings.getWidget(gs_FeaturesList);
512 features->Clear();
513 initFeaturesList(features);
514
515 }
516
517 }; // namespace DeprecatedGUI
518