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