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 // Options
13 // Created 21/7/02
14 // Jason Boettcher
15 
16 #include <ctype.h> // isspace
17 
18 
19 #include "LieroX.h"
20 #include "Debug.h"
21 #include "FindFile.h"
22 #include "StringUtils.h"
23 #include "Options.h"
24 #include "FindFile.h"
25 #include "ConfigHandler.h"
26 #include "CScriptableVars.h"
27 #include "IniReader.h"
28 #include "Version.h"
29 #include "Iterator.h"
30 #include "CGameMode.h"
31 #include "AuxLib.h"
32 #include "CInput.h"
33 
34 
35 
36 GameOptions	*tLXOptions = NULL;
37 NetworkTexts	*networkTexts = NULL;
38 
39 const std::string DefaultCfgFilename = "cfg/options.cfg";
40 
41 const std::string    ply_keys[] = {"Up", "Down", "Left", "Right", "Shoot", "Jump", "SelectWeapon", "Rope", "Strafe", "Dig", "Weapon1", "Weapon2", "Weapon3", "Weapon4", "Weapon5" };
42 const std::string    ply_def1[] =
43 #ifdef MACOSX
44 	{"up", "down", "left", "right", "lalt", "lmeta", "space", "x", "c", "a", "1", "2", "3", "4", "5" };
45 #else
46 	{"up", "down", "left", "right", "lctrl", "lalt", "lshift", "x", "z", "a", "1", "2", "3", "4", "5" };
47 #endif
48 const std::string    ply_def2[] = {"kp 8",  "kp 5",    "kp 4",    "kp 6",     "kp +", "kp enter", "kp 0", "kp -", "kp .", "kp /", "6", "7", "8", "9", "0" };
49 const std::string    gen_keys[] = {"Chat", "ShowScore", "ShowHealth", "ShowSettings",  "TakeScreenshot",  "ViewportManager", "SwitchMode", "ToggleTopBar", "TeamChat",	"IrcChat", "Console"};
50 const std::string    gen_def[]  = {"i",    "tab",		"h",		  "space",	       "F12",				"F2",				 "F5",		   "F8",		   "o",			"F4",	"F3"};
51 
52 static_assert( sizeof(ply_keys) / sizeof(std::string) == __SIN_PLY_BOTTOM, "ply_keys__sizecheck" );
53 static_assert( sizeof(ply_def1) / sizeof(std::string) == __SIN_PLY_BOTTOM, "ply_def1__sizecheck" );
54 static_assert( sizeof(ply_def2) / sizeof(std::string) == __SIN_PLY_BOTTOM, "ply_def2__sizecheck" );
55 static_assert( sizeof(gen_keys) / sizeof(std::string) == __SIN_GENERAL_BOTTOM, "gen_keys__sizecheck" );
56 static_assert( sizeof(gen_def) / sizeof(std::string) == __SIN_GENERAL_BOTTOM, "gen_def__sizecheck" );
57 
58 static const char * defaultMinVersionStr = "OpenLieroX/0.58_rc1";
59 static const Version defaultMinVersion(defaultMinVersionStr);
60 
61 
62 
InitSearchPaths()63 static void InitSearchPaths() {
64 	// have to set to find the config at some of the default places
65 	InitBaseSearchPaths();
66 
67 	std::string value;
68 	int i = 1;
69 	while(true) {
70 		if(!ReadString(DefaultCfgFilename, "FileHandling", "SearchPath" + itoa(i,10), value, ""))
71 			break;
72 
73 		AddToFileList(&tSearchPaths, value);
74 		i++;
75 	}
76 
77 	// add the basesearchpaths to the searchpathlist as they should be saved in the end
78 	for(searchpathlist::const_iterator p1 = basesearchpaths.begin(); p1 != basesearchpaths.end(); i++,p1++)  {
79 		AddToFileList(&tSearchPaths, *p1);
80 	}
81 
82 	// print the searchpaths, this may be very usefull for the user
83 	notes << "I have now the following searchpaths (in this order):\n";
84 	for(searchpathlist::const_iterator p2 = tSearchPaths.begin(); p2 != tSearchPaths.end(); p2++) {
85 		std::string path = *p2;
86 		ReplaceFileVariables(path);
87 		notes << "  " << path << "\n";
88 	}
89 	notes << " And that's all." << endl;
90 }
91 
InitWidgetStates(GameOptions & opts)92 static void InitWidgetStates(GameOptions& opts) {
93 	// this has to be done explicitly at the moment as scriptablevars doesn't support arrays
94 	// TODO: add this feature
95 
96 	const int	 def_widths[] = {32,180,70,80,50,150,22};
97 
98 	for (size_t i=0; i<sizeof(opts.iInternetList)/sizeof(int); i++)
99 		opts.iInternetList[i] = def_widths[i];
100 
101 	for (size_t i=0; i<sizeof(opts.iLANList)/sizeof(int); i++)  {
102 		opts.iLANList[i] = def_widths[i];
103 		opts.iFavouritesList[i] = def_widths[i];
104 	}
105 
106 	for (size_t i=0; i<GIG_Size; i++)
107 		opts.iGameInfoGroupsShown[i] = true;
108 
109 	// Widget states
110 	ReadIntArray(opts.cfgFilename, "Widgets","InternetListCols",	&opts.iInternetList[0],7);
111 	ReadIntArray(opts.cfgFilename, "Widgets","LANListCols",		&opts.iLANList[0],6);
112 	ReadIntArray(opts.cfgFilename, "Widgets","FavouritesListCols",	&opts.iFavouritesList[0],6);
113 	ReadArray<bool>(opts.cfgFilename, "Widgets","GameInfoGroupsShown",	&opts.iGameInfoGroupsShown[0], GIG_Size);
114 
115 }
116 
117 
118 
119 
Init()120 bool GameOptions::Init() {
121 	if(tLXOptions) {
122 		warnings << "it seems that the GameOptions are already inited" << endl;
123 		return true;
124 	}
125 
126 	tLXOptions = new GameOptions;
127 	if(tLXOptions == NULL) {
128 		errors << "not enough mem for GameOptions" << endl;
129 		return false;
130 	}
131 
132 	CScriptableVars::RegisterVars("GameOptions")
133 		( tLXOptions->bFullscreen, "Video.Fullscreen", true )
134 		( tLXOptions->bShowFPS, "Video.ShowFPS", false )
135 		( tLXOptions->bOpenGL, "Video.OpenGL",
136 #ifdef MACOSX
137 			true )
138 #else
139 			false )
140 #endif
141 		( tLXOptions->iColourDepth, "Video.ColourDepth",
142 #ifdef __ANDROID__
143 			16 ) // Faster video output
144 #else
145 			32 )
146 #endif
147 		( tLXOptions->sResolution, "Video.Resolution", "" )
148 		( tLXOptions->sVideoPostProcessor, "Video.PostProcessor", "" )
149 
150 		( tLXOptions->iNetworkPort, "Network.Port", LX_PORT )
151 		( tLXOptions->iNetworkSpeed, "Network.Speed", NST_LAN )
152 		( tLXOptions->bUseIpToCountry, "Network.UseIpToCountry", true )
153 		( tLXOptions->iMaxUploadBandwidth, "Network.MaxUploadBandwidth", 50000 )
154 		( tLXOptions->bCheckBandwidthSanity, "Network.CheckBandwidthSanity", true )
155 		( tLXOptions->sHttpProxy, "Network.HttpProxy", "" )
156 		( tLXOptions->bAutoSetupHttpProxy, "Network.AutoSetupHttpProxy", true )
157 
158 		( tLXOptions->bEnableChat, "Network.EnableChat", true )
159 		( tLXOptions->bEnableMiniChat, "Network.EnableMiniChat", true )
160 		( tLXOptions->sServerName, "Network.ServerName", "OpenLieroX Server" )
161 		( tLXOptions->sWelcomeMessage, "Network.WelcomeMessage", "Welcome to <server>, <player>" )
162 		( tLXOptions->sServerPassword, "Network.Password" )
163 		( tLXOptions->bRegServer, "Network.RegisterServer", true )
164 		( tLXOptions->bAllowWantsJoinMsg, "Network.AllowWantsJoinMsg", true )
165 		( tLXOptions->bWantsJoinBanned, "Network.WantsToJoinFromBanned", true )
166 		( tLXOptions->bAllowRemoteBots, "Network.AllowRemoteBots", true )
167 		( tLXOptions->bForceCompatibleConnect, "Network.ForceCompatibleConnect", true, "Force Compatible", "Don't allow incompatible clients to connect" )
168 		( tLXOptions->sForceMinVersion, "Network.ForceMinVersion", defaultMinVersionStr, "Force Min Version", "Minimal version needed to play on this server" )
169 
170 		( tLXOptions->bFirstHosting, "State.FirstHosting", true )
171 		( tLXOptions->sNewestVersion, "State.NewestVersion", "" )
172 
173 		( tLXOptions->bSoundOn, "Audio.Enabled", true )
174 		( tLXOptions->iSoundVolume, "Audio.Volume", 70 )
175 		( tLXOptions->bMusicOn, "Audio.MusicEnabled", true )
176 		( tLXOptions->iMusicVolume, "Audio.MusicVolume", 70 )
177 
178 		( tLXOptions->iBloodAmount, "Game.Blood", 100 )
179 		( tLXOptions->bShadows, "Game.Shadows", true )
180 		( tLXOptions->bParticles, "Game.Particles", true )
181 		( tLXOptions->bOldSkoolRope, "Game.OldSkoolRope", false )
182 		( tLXOptions->bShowHealth, "Game.ShowWormHealth", false )
183 		( tLXOptions->bColorizeNicks, "Game.ColorizeNicks", true )
184 		( tLXOptions->bAutoTyping, "Game.AutoTyping", false )
185 		( tLXOptions->sTheme, "Game.Theme", "" )
186 		( tLXOptions->bAntiAliasing, "Game.Antialiasing", true )
187 		( tLXOptions->bMouseAiming, "Game.MouseAiming", false ) // TODO: rename to mouse control?
188 		( tLXOptions->iMouseSensity, "Game.MouseSensity", 200 )
189 		( tLXOptions->bAntilagMovementPrediction, "Game.AntilagMovementPrediction", true )
190 		( tLXOptions->sLastSelectedPlayer, "Game.LastSelectedPlayer", "" )
191 		( tLXOptions->sLastSelectedPlayer2, "Game.LastSelectedPlayer2", "" )
192 		( tLXOptions->bTopBarVisible, "Game.TopBarVisible", true )
193 		( tLXOptions->bDamagePopups, "Game.DamagePopups", true )
194 		( tLXOptions->bColorizeDamageByWorm, "Game.ColorizeDamageByWorm", false )
195 		( tLXOptions->iRandomTeamForNewWorm, "Game.RandomTeamForNewWorm", 1, "Random team for new worm", "Joining worms will be randomly in a team of [0,value]", GIG_Other, ALT_Advanced, true, 0, 3 )
196 		( tLXOptions->fCrosshairDistance, "Game.CrosshairDistance", 32.0, "Crosshair distance", "", GIG_Other, ALT_OnlyViaConfig, true, 5, 100 )
197 		( tLXOptions->fAimAcceleration, "Game.AimAcceleration", /* Gusanos promode default */ 1299.91, "Aim speed acceleration", "aim speed acceleration - kind of the sensibility of up/down keys for aiming", GIG_Other, ALT_VeryAdvanced, true, 100, 2000 )
198 		( tLXOptions->fAimMaxSpeed, "Game.AimMaxSpeed", /* Gusanos promode default */ 232.996, "Aim max speed", "maximum possible aim speed for worm", GIG_Other, ALT_VeryAdvanced, true, 20, 1000 )
199 		( tLXOptions->fAimFriction, "Game.AimFriction", /* Gusanos promode default */ 0, "Aim friction", "aim speed friction for worm", GIG_Other, ALT_VeryAdvanced, true, 0, 1 )
200 		( tLXOptions->bAimLikeLX56, "Game.AimLikeLX56", false, "Aim friction like LX56", "aim speed friction behaves like LX56", GIG_Other, ALT_OnlyViaConfig )
201 		( tLXOptions->bTouchscreenTapCycleWeaponsBackwards, "Game.TouchscreenTapCycleWeaponsBackwards", true )
202 		( tLXOptions->iTouchscreenSensitivity, "Game.TouchscreenSensitivity", 5 )
203 
204 		// Killing spree thresholds
205 		( tLXOptions->iSpreeThreshold1, "Game.SpreeThreshold1", 3 )
206 		( tLXOptions->iSpreeThreshold2, "Game.SpreeThreshold2", 5 )
207 		( tLXOptions->iSpreeThreshold3, "Game.SpreeThreshold3", 7 )
208 		( tLXOptions->iSpreeThreshold4, "Game.SpreeThreshold4", 9 )
209 		( tLXOptions->iSpreeThreshold5, "Game.SpreeThreshold5", 10 )
210 		// Dying spree thresholds
211 		( tLXOptions->iDyingSpreeThreshold1, "Game.DyingSpreeThreshold1", 3 )
212 		( tLXOptions->iDyingSpreeThreshold2, "Game.DyingSpreeThreshold2", 5 )
213 		( tLXOptions->iDyingSpreeThreshold3, "Game.DyingSpreeThreshold3", 7 )
214 		( tLXOptions->iDyingSpreeThreshold4, "Game.DyingSpreeThreshold4", 9 )
215 		( tLXOptions->iDyingSpreeThreshold5, "Game.DyingSpreeThreshold5", 10 )
216 
217 
218 		( tLXOptions->nMaxFPS, "Advanced.MaxFPS", 95 )
219 		( tLXOptions->iJpegQuality, "Advanced.JpegQuality", 80 )
220 		( tLXOptions->iMaxCachedEntries, "Advanced.MaxCachedEntries", 300 ) // Should be enough for every mod (we have 2777 .png and .wav files total now) and does not matter anyway with SmartPointer
221 		( tLXOptions->bMatchLogging, "Advanced.MatchLogging", true )
222 		( tLXOptions->bRecoverAfterCrash, "Advanced.RecoverAfterCrash",
223 #ifndef DEDICATED_ONLY
224 																		true )
225 #else
226 																		false )
227 #endif
228 		( tLXOptions->bCheckForUpdates, "Advanced.CheckForUpdates", true )
229 
230 		( tLXOptions->bLogConvos, "Misc.LogConversations", false )
231 		( tLXOptions->bLogServerChatToMainlog, "Network.LogServerChatToMainlog", true)	//Log chat to main log when hosting a server - previously OLX always did this. NOTE: It's under network settings as it affects mostly the server side.
232 		( tLXOptions->iMaxChatMessageLength, "Network.MaxChatMessageLength", 384)	//Max chat message length. NOTE: Includes nickname and ": " after it.
233 		( tLXOptions->bShowPing, "Misc.ShowPing", true )
234 		( tLXOptions->bShowNetRates, "Misc.ShowNetRate", false )
235 		( tLXOptions->bShowProjectileUsage, "Misc.ShowProjectileUsage", false )
236 		( tLXOptions->iScreenshotFormat, "Misc.ScreenshotFormat", FMT_PNG )
237 		( tLXOptions->sDedicatedScript, "Misc.DedicatedScript", "dedicated_control" )
238 		( tLXOptions->sDedicatedScriptArgs, "Misc.DedicatedScriptArgs", "cfg/dedicated_config" )
239 		( tLXOptions->iVerbosity, "Misc.Verbosity", 0 )
240 		( tLXOptions->bLogTimestamps, "Misc.LogTimestamps", false )
241 		( tLXOptions->bAdvancedLobby, "Misc.ShowAdvancedLobby", false )
242 		( tLXOptions->bShowCountryFlags, "Misc.ShowCountryFlags", true )
243 		( tLXOptions->doProjectileSimulationInDedicated, "Misc.DoProjectileSimulationInDedicated", true )
244 		( tLXOptions->bCheckMaxWpnTimeInInstantStart, "Misc.CheckMaxWpnSelectionTimeInInstantStart", true )
245 
246 		( tLXOptions->iInternetSortColumn, "Widgets.InternetSortColumn", 4 )
247 		( tLXOptions->iLANSortColumn, "Widgets.LANSortColumn", 4 )
248 		( tLXOptions->iFavouritesSortColumn, "Widgets.FavouritesSortColumn", 4 )
249 		( tLXOptions->iAdvancedLevelLimit, "Widgets.AdvancedLevelLimit", 0 )
250 		;
251 
252 	for( uint i = 0; i < sizeof(ply_keys) / sizeof(ply_keys[0]) ; i ++ )
253 	{
254 		CScriptableVars::RegisterVars("GameOptions.Ply1Controls") ( tLXOptions->sPlayerControls[0][i], ply_keys[i], ply_def1[i].c_str() );
255 		CScriptableVars::RegisterVars("GameOptions.Ply2Controls") ( tLXOptions->sPlayerControls[1][i], ply_keys[i], ply_def2[i].c_str() );
256 	}
257 	for( uint i = 0; i < sizeof(gen_keys) / sizeof(gen_keys[0]) ; i ++ )
258 	{
259 		CScriptableVars::RegisterVars("GameOptions.GeneralControls") ( tLXOptions->sGeneralControls[i], gen_keys[i], gen_def[i].c_str() );
260 	}
261 
262 	struct GameModeIndexWrapper : DynamicVar<int> {
263 		int get() {
264 			if(tLXOptions) return GetGameModeIndex(tLXOptions->tGameInfo.gameMode);
265 			else errors << "GameModeIndexWrapper:get: options not inited" << endl;
266 			return 0;
267 		}
268 		void set(const int& i) {
269 			if(tLXOptions) {
270 				tLXOptions->tGameInfo.gameMode = GameMode(GameModeIndex(i));
271 				if(tLXOptions->tGameInfo.gameMode == NULL) {
272 					errors << "GameModeIndexWrapper:set: gamemodeindex " << i << " is invalid" << endl;
273 					tLXOptions->tGameInfo.gameMode = GameMode(GM_DEATHMATCH);
274 				}
275 			}
276 			else errors << "GameModeIndexWrapper:set: options not inited" << endl;
277 		}
278 	};
279 	static GameModeIndexWrapper gameModeIndexWrapper;
280 
281 	// Legend:	Name in options, Default value, Human-readable-name, Long description, Group in options, If value unsigned (ints and floats), Min value (ints and floats), Max value (ints and floats)
282 	// If you want to add another in-gmae option, do not add it here, add it to FeatureList.cpp
283 	// TODO: move all options to FeatureList, except for LevelName, ModName and GameType which are comboboxes
284 	CScriptableVars::RegisterVars("GameOptions.GameInfo")
285 		( tLXOptions->tGameInfo.iLives, "Lives", -1, "Lives", "Lives", GIG_General, ALT_Basic, true, -1, 150 )
286 		( tLXOptions->tGameInfo.iKillLimit, "KillLimit", 15, "Max kills", "Game ends when a player reaches the specified number of kills", GIG_General, ALT_Basic, true, -1, 150 )
287 		( tLXOptions->tGameInfo.fTimeLimit, "TimeLimit", 6.0f, "Time limit", "Time limit, in minutes", GIG_General, ALT_Basic, true, -0.15f, 20.0f )
288 		( tLXOptions->tGameInfo.iTagLimit, "TagLimit", 5, "Tag limit", "Tag limit, for Tag game mode. It's the time how long a player must be tagged until the game ends", GIG_Tag, ALT_Basic, true, 1, 150 )
289 		( tLXOptions->tGameInfo.iLoadingTime, "LoadingTime", 100, "Loading time", "Loading time of weapons, in percent", GIG_General, ALT_Basic, true, 0, 500 )
290 		( tLXOptions->tGameInfo.bBonusesOn, "Bonuses", false, "Bonuses", "Bonuses enabled", GIG_Bonus, ALT_Basic )
291 		( tLXOptions->tGameInfo.bShowBonusName, "BonusNames", true, "Show Bonus names", "Show bonus name above its image", GIG_Bonus, ALT_VeryAdvanced )
292 		( tLXOptions->tGameInfo.iMaxPlayers, "MaxPlayers", 14, "Max players", "Max amount of players allowed on server", GIG_General, ALT_Basic, true, 1, 32 )
293 		( tLXOptions->tGameInfo.sMapFile, "LevelName", "Dirt Level.lxl" ) // WARNING: confusing, it is handled like the filename
294 		( &gameModeIndexWrapper, "GameType", (int)GM_DEATHMATCH )
295 		( tLXOptions->tGameInfo.sModDir, "ModName", "Classic" ) // WARNING: confusing, it is handled like the dirname
296 		( tLXOptions->tGameInfo.fBonusFreq, "BonusFrequency", 30.0f, "Bonus spawn time", "How often a new bonus will be spawned (every N seconds)", GIG_Bonus, ALT_Advanced, true, 1.0f, 150.0f )
297 		( tLXOptions->tGameInfo.fBonusLife, "BonusLife", 60.0f, "Bonus life time", "Bonus life time, in seconds", GIG_Bonus, ALT_VeryAdvanced, true, 1.0f, 150.0f )
298 		( tLXOptions->tGameInfo.fRespawnTime, "RespawnTime", 2.5, "Respawn time", "Player respawn time, in seconds", GIG_Advanced, ALT_Advanced, true, 0.0f, 20.0f )
299 		( tLXOptions->tGameInfo.bRespawnGroupTeams, "RespawnGroupTeams", true, "Group teams", "Respawn player closer to its team, and farther from enemy", GIG_Advanced, ALT_Advanced )
300 		( tLXOptions->tGameInfo.bEmptyWeaponsOnRespawn, "EmptyWeaponsOnRespawn", false, "Empty weapons on respawn", "Your weapon ammo is emptied when you respawn", GIG_Weapons, ALT_VeryAdvanced )
301 		( tLXOptions->tGameInfo.fBonusHealthToWeaponChance, "BonusHealthToWeaponChance", 0.5f, "Bonus weapon chance", "Chance of spawning a weapon bonus instead of a health bonus", GIG_Bonus, ALT_Advanced, true, 0.0f, 1.0f )
302 		( tLXOptions->tGameInfo.bForceRandomWeapons, "ForceRandomWeapons", false, "Force random weapons", "Force all players to select random weapons", GIG_Weapons, ALT_Basic )
303 		( tLXOptions->tGameInfo.bSameWeaponsAsHostWorm, "SameWeaponsAsHostWorm", false, "Same weapons as host worm", "Force all players to select the same weapons as host worm", GIG_Weapons, ALT_Advanced )
304 		( tLXOptions->tGameInfo.bAllowConnectDuringGame, "AllowConnectDuringGame", true, "Connect during game", "Allow new players to connect during game", GIG_Advanced, ALT_Basic )
305 		( tLXOptions->tGameInfo.bAllowNickChange, "AllowNickChange", true, "Allow name change", "Allow players to change name with /setmyname command", GIG_Other, ALT_VeryAdvanced )
306 		( tLXOptions->tGameInfo.bAllowStrafing, "AllowStrafing", true, "Allow strafing", "Allow players to use the Strafe key", GIG_Other, ALT_VeryAdvanced )
307 		( tLXOptions->tGameInfo.bServerSideHealth, "ServerSideHealth", false, "Server sided health", "Health is calculated on server, to prevent cheating", GIG_Other, ALT_OnlyViaConfig )
308 		( tLXOptions->tGameInfo.iWeaponSelectionMaxTime, "WeaponSelectionMaxTime", 120, "Weapon selection max time", "Max time to allow players to select weapons, in seconds", GIG_Weapons, ALT_VeryAdvanced, true, 10, 500 )
309 		;
310 
311 	foreach( Feature*, f, Array(featureArray,featureArrayLen()) ) {
312 		CScriptableVars::RegisterVars("GameOptions.GameInfo")
313 		( tLXOptions->tGameInfo.features[f->get()], f->get()->name, f->get()->defaultValue,
314 				f->get()->humanReadableName, f->get()->description, f->get()->group, f->get()->advancedLevel, f->get()->minValue, f->get()->maxValue, f->get()->unsignedValue );
315 	}
316 
317 
318 
319 	// We still use the old ReadKeyword&co functions, they need this.
320 	// It's save to add same keyword multiple times, so no need to care about restarts.
321 	AddKeyword("true",true);
322 	AddKeyword("false",false);
323 
324 	// Load all variables that should be treated specially
325 
326 	// File handling
327 	// read this first, because perhaps we will have new searchpaths
328 	InitSearchPaths();
329 
330 	// first set the standards (else the vars would be undefined if not defined in options.cfg)
331 	for( CScriptableVars::const_iterator it = CScriptableVars::begin();
332 		it != CScriptableVars::end(); it++ )
333 	{
334 		if( it->first.find("GameOptions.") == 0 )
335 		{
336 			it->second.var.setDefault();
337 		}
338 	}
339 
340 	notes << "Reading game options from " << GetFullFileName(tLXOptions->cfgFilename) << endl;
341 	notes << "Will write game options to " << GetWriteFullFileName(tLXOptions->cfgFilename, true) << endl;
342 
343 	bool ret = tLXOptions->LoadFromDisc();
344 
345 	return ret;
346 }
347 
348 
349 
350 ///////////////////
351 // Load the options
LoadFromDisc(const std::string & cfgfilename)352 bool GameOptions::LoadFromDisc(const std::string& cfgfilename)
353 {
354 	additionalOptions.clear();
355 
356 	// TODO: these use arrays which are not handled by scriptablevars
357 	InitWidgetStates(*this);
358 
359 	// define parser handler
360 	struct MyIniReader : public IniReader {
361 		GameOptions* opts;
362 		typedef std::list< std::pair<std::string,std::string> > LX56FallbackList;
363 		LX56FallbackList lx56_LastGameFallback;
364 		bool haveGameInfo;
365 		MyIniReader(const std::string& fn, GameOptions* o) : IniReader(fn), opts(o), haveGameInfo(false) {}
366 
367 		bool OnEntry(const std::string& section, const std::string& propname, const std::string& value) {
368 			// ConfigFileInfo is additional data about the config file itself - we ignore it atm at this place
369 			if(stringcaseequal(section, "ConfigFileInfo")) return true;
370 			if(stringcaseequal(section, "GameInfo")) haveGameInfo = true;
371 
372 			RegisteredVar* var = CScriptableVars::GetVar("GameOptions." + section + "." + propname);
373 			if( var !=  NULL ) { // found entry
374 				CScriptableVars::SetVarByString(var->var, value);
375 			} else {
376 				if( (stringcaseequal(section, "FileHandling") && stringcasefind(propname, "SearchPath") == 0)
377 				|| stringcaseequal(section, "Widgets") ) {
378 					// ignore these atm
379 				} else {
380 					opts->additionalOptions[section + "." + propname] = value;
381 
382 					if(stringcaseequal(section, "LastGame"))
383 						lx56_LastGameFallback.push_back(make_pair(propname, value));
384 					else
385 						notes << "the option \"" << section << "." << propname << "\" defined in " << m_filename << " is unknown" << endl;
386 				}
387 			}
388 
389 			return true;
390 		}
391 	}
392 	iniReader(cfgfilename, this);
393 
394 	// parse the file now
395 	if( ! iniReader.Parse() ) {
396 		hints << cfgfilename << " not found, will use standards" << endl;
397 	}
398 	else {
399 		// Fallback: this is old LX56 config file
400 		if(!iniReader.haveGameInfo && iniReader.lx56_LastGameFallback.size() > 0) {
401 			notes << "Old LX56 config file" << endl;
402 			for(MyIniReader::LX56FallbackList::iterator i = iniReader.lx56_LastGameFallback.begin(); i != iniReader.lx56_LastGameFallback.end(); ++i) {
403 				const std::string& propname = i->first;
404 				const std::string& value = i->second;
405 				RegisteredVar* var = CScriptableVars::GetVar("GameOptions.GameInfo." + propname);
406 				if( var !=  NULL ) // found entry
407 					CScriptableVars::SetVarByString(var->var, value);
408 				else
409 					notes << "the LX56 gameoption " << propname << " is unknown" << endl;
410 			}
411 		}
412 	}
413 
414 	initSpecialSearchPathForTheme();
415 	if(getSpecialSearchPathForTheme()) {
416 		notes << "Special searchpath for the theme: " << *getSpecialSearchPathForTheme() << endl;
417 	} else
418 		notes << "Default theme is used" << endl;
419 
420 
421 	if(additionalOptions.size() > 0) {
422 		hints << "Unknown options were found." << endl;
423 	}
424 
425 	// Clamp the Jpeg quality
426 	if (iJpegQuality < 1)
427 		iJpegQuality = 1;
428 	if (iJpegQuality > 100)
429 		iJpegQuality = 100;
430 
431 	const Version newestVersion(sNewestVersion);
432 	// check if option file was saved with version where we had LX56 as default min version
433 	if(newestVersion <= Version("OpenLieroX/0.58_rc1") ||
434 		(newestVersion >= OLXBetaVersion(0,59,1) && newestVersion <= OLXBetaVersion(0,59,4)))
435 		// overwrite it to new default
436 		tLXOptions->sForceMinVersion = MAX(defaultMinVersion, Version(tLXOptions->sForceMinVersion)).asString();
437 
438 	notes << "DONE loading options" << endl;
439 
440 	return true;
441 }
442 
443 ///////////////////
444 // Save & shutdown the options
ShutdownOptions()445 void ShutdownOptions()
446 {
447 	CScriptableVars::DeRegisterVars("GameOptions");
448 	if(tLXOptions) {
449 		delete tLXOptions;
450 		tLXOptions = NULL;
451 	}
452 
453 	if(networkTexts) {
454 		delete networkTexts;
455 		networkTexts = NULL;
456 	}
457 }
458 
459 
460 ///////////////////
461 // Save the options
SaveToDisc(const std::string & cfgfilename)462 void GameOptions::SaveToDisc(const std::string& cfgfilename)
463 {
464     FILE *fp = OpenGameFile(cfgfilename, "wt");
465     if(fp == NULL) {
466 		errors << "GameOptions::SaveToDisc: cannot open " << cfgfilename << endl;
467         return;
468 	}
469 
470 	// Set the FirstRun to false, we're just quitting
471 	if(tLXOptions->sNewestVersion == "" || Version(tLXOptions->sNewestVersion) < GetGameVersion())
472 		tLXOptions->sNewestVersion = GetGameVersion().asString();
473 
474 	// Hosted a net game? Set the FirstHosting variables to false
475 	if (tLX->bHosted)  {
476 		tLXOptions->bFirstHosting = false;
477 	}
478 
479 	// TODO: remove this UTF8 information here
480 	fprintf(fp, "%c%c%c", 0xEF, 0xBB, 0xBF);  // mark that this file is UTF-8 encoded
481     fprintf(fp, "# OpenLieroX Options File\n");
482     fprintf(fp, "# Note: This file is automatically generated by ");
483 	// just looks better with version-info and is a good information for the user
484 	// this should not be used in parsing to change any behaviour
485 	fprintf(fp, "%s", GetFullGameName());
486 	fprintf(fp, "\n\n");
487 
488 	// Save all variables that should be treated specially
489 	fprintf(fp, "[FileHandling]\n");
490 	{
491 		int i=1;
492 		for(searchpathlist::const_iterator p = tSearchPaths.begin(); p != tSearchPaths.end(); p++, i++)
493 			fprintf(fp, "SearchPath%i = %s\n", i, p->c_str());
494 	}
495 	fprintf(fp,"\n");
496 
497 	// TODO: same issue as in InitWidgetStates()
498 	fprintf(fp, "[Widgets]\n");
499 	fprintf(fp, "InternetListCols = ");
500 	for (int i=0;i<6;i++)
501 		fprintf(fp, "%i,",iInternetList[i]);
502 	fprintf(fp, "%i\n",iInternetList[6]);
503 	fprintf(fp, "LANListCols = ");
504 	for (int i=0;i<5;i++)
505 		fprintf(fp, "%i,",iLANList[i]);
506 	fprintf(fp, "%i\n",iLANList[5]);
507 	fprintf(fp, "FavouritesListCols = ");
508 	for (int i=0;i<5;i++)
509 		fprintf(fp, "%i,",iFavouritesList[i]);
510 	fprintf(fp, "%i\n",iFavouritesList[5]);
511 	fprintf(fp, "GameInfoGroupsShown = ");
512 	for (int i=0;i<GIG_Size-1;i++)
513 		fprintf(fp, "%i,",iGameInfoGroupsShown[i]);
514 	fprintf(fp, "%i\n",iGameInfoGroupsShown[GIG_Size-1]);
515 
516 	// The following part has the disadvantage that section
517 	// could occur multiple times in the config file
518 	// (most often because of additionalOptions).
519 	// The current parsing of a INI-file allowes this but it
520 	// looks not so nice.
521 	// TODO: make it better (but in a clean way, else just leave it!)
522 
523 	// Save variables registered with CGuiSkin
524 	std::string currentSection;
525 	for( CScriptableVars::const_iterator it = CScriptableVars::lower_bound("GameOptions.");
526 			it != CScriptableVars::end(); it++ )
527 	{
528 		if( strStartsWith(it->first, "GameOptions.") )
529 		{
530 			size_t dot1 = it->first.find(".");
531 			size_t dot2 = it->first.find( ".", dot1 + 1 );
532 			if(dot2 == std::string::npos) {
533 				errors << "section " << it->first << " is strange" << endl;
534 				continue;
535 			}
536 			std::string section = it->first.substr( dot1 + 1, dot2 - dot1 - 1 );	// Between two dots
537 			std::string key = it->first.substr( dot2 + 1 );	// After last dot
538 			if( currentSection != section )
539 			{
540 			    fprintf( fp, "\n[%s]\n", section.c_str() );
541 				currentSection = section;
542 			}
543 			fprintf( fp, "%s = %s\n", key.c_str(), it->second.var.toString().c_str() );
544 		}
545 		else
546 			break;
547 	}
548 
549 	fprintf(fp, "\n\n# This is for OLX to know what type of config file this is.\n");
550 	fprintf(fp, "[ConfigFileInfo]\n");
551 	fprintf(fp, "Type = MainConfig\n");
552 	fprintf(fp, "SavedBy = %s\n", GetFullGameName());
553 	fprintf(fp, "Date = %s\n", GetDateTimeText().c_str());
554 
555 	if(additionalOptions.size() > 0)
556 		fprintf(fp, "\n\n# The following options are unknown in %s\n\n", GetFullGameName());
557 
558 	// save additional options
559 	// HINT: as this is done seperatly, some sections may be double; though the current parsing and therefore all future parsings handle this correctly
560 	currentSection = "";
561 	for( std::map< std::string, std::string > :: iterator it = additionalOptions.begin(); it != additionalOptions.end(); it++ ) {
562 		size_t dot = it->first.find(".");
563 		std::string section = it->first.substr( 0, dot ); // before the dot
564 		std::string key = it->first.substr( dot + 1 ); // after the dot
565 		if( currentSection != section )
566 		{
567 			fprintf( fp, "\n[%s]\n", section.c_str() );
568 			currentSection = section;
569 		}
570 		fprintf( fp, "%s = %s\n", key.c_str(), it->second.c_str() );
571 	}
572 
573 	fprintf(fp, "\n\n# End of options\n\n");
574 
575     fclose(fp);
576 }
577 
SaveSectionToDisc(const std::string & presection,const std::string & filename)578 void GameOptions::SaveSectionToDisc(const std::string& presection, const std::string& filename) {
579     FILE *fp = OpenGameFile(filename, "wt");
580     if(fp == NULL) {
581 		errors << "GameOptions::SaveSectionToDisc(" << presection << "): cannot open " << filename << endl;
582         return;
583 	}
584 
585 	// TODO: remove this UTF8 information here
586 	fprintf(fp, "%c%c%c", 0xEF, 0xBB, 0xBF);  // mark that this file is UTF-8 encoded
587     fprintf(fp, "# OpenLieroX Section Options File\n");
588     fprintf(fp, "# Note: This file is automatically generated by ");
589 	fprintf(fp, "%s", GetFullGameName());
590 	fprintf(fp, "\n\n");
591 
592 	// Save variables registered with CGuiSkin
593 	std::string currentSection;
594 	for( CScriptableVars::const_iterator it = CScriptableVars::lower_bound(presection + ".");
595 		it != CScriptableVars::end(); it++ )
596 	{
597 		if( strCaseStartsWith(it->first, presection + ".") )
598 		{
599 			size_t dot1 = it->first.find(".");
600 			size_t dot2 = it->first.find( ".", dot1 + 1 );
601 			if(dot2 == std::string::npos) {
602 				errors << "section " << it->first << " is strange" << endl;
603 				continue;
604 			}
605 			std::string section = it->first.substr( dot1 + 1, dot2 - dot1 - 1 );	// Between two dots
606 			std::string key = it->first.substr( dot2 + 1 );	// After last dot
607 			if( currentSection != section )
608 			{
609 			    fprintf( fp, "\n[%s]\n", section.c_str() );
610 				currentSection = section;
611 			}
612 			fprintf( fp, "%s = %s\n", key.c_str(), it->second.var.toString().c_str() );
613 		}
614 		else
615 			break;
616 	}
617 
618 	fprintf(fp, "\n\n# This is for OLX to know what type of config file this is.\n");
619 	fprintf(fp, "[ConfigFileInfo]\n");
620 	fprintf(fp, "Type = SectionConfig\n");
621 	fprintf(fp, "Section = %s\n", presection.c_str());
622 	fprintf(fp, "SavedBy = %s\n", GetFullGameName());
623 	fprintf(fp, "Date = %s\n", GetDateTimeText().c_str());
624 
625 	fprintf(fp, "\n# End of options\n");
626     fclose(fp);
627 }
628 
629 
630 
Init()631 bool NetworkTexts::Init() {
632 	if(networkTexts) {
633 		warnings << "networktexts are already inited; ignoring ..." << endl;
634 		return true;
635 	}
636 
637 	networkTexts = new NetworkTexts;
638 	if(!networkTexts) {
639 		errors << "not enough mem for networktexts" << endl;
640 		return false;
641 	}
642 
643 	return networkTexts->LoadFromDisc();
644 }
645 
646 ////////////////////
647 // Loads the texts used by server
LoadFromDisc()648 bool NetworkTexts::LoadFromDisc()
649 {
650 	notes << "Loading network texts... ";
651 
652 	// TODO: use the general INI-parser here
653 	const std::string f = "cfg/network.txt";
654 	ReadString (f, "NetworkTexts", "HasConnected",    sHasConnected,	"<player> has connected");
655 	ReadString (f, "NetworkTexts", "HasLeft",	      sHasLeft,			"<player> has left");
656 	ReadString (f, "NetworkTexts", "HasTimedOut",     sHasTimedOut,		"<player> has timed out");
657 
658 	ReadString (f, "NetworkTexts", "HasBeenKicked",   sHasBeenKicked,	"<player> has been kicked out");
659 	ReadString (f, "NetworkTexts", "HasBeenKickedReason",	sHasBeenKickedReason,	"<player> has been kicked out because <reason>");
660 	ReadString (f, "NetworkTexts", "HasBeenBanned",   sHasBeenBanned,	"<player> has been banned");
661 	ReadString (f, "NetworkTexts", "HasBeenBannedReason",   sHasBeenBannedReason,	"<player> has been banned because <reason>");
662 	ReadString (f, "NetworkTexts", "HasBeenMuted",    sHasBeenMuted,	"<player> has been muted");
663 	ReadString (f, "NetworkTexts", "HasBeenUnmuted",  sHasBeenUnmuted,	"<player> has been unmuted");
664 	ReadString (f, "NetworkTexts", "IsSpectating",    sIsSpectating,	"<player> will only spectate this round");
665 	ReadString (f, "NetworkTexts", "IsPlaying",		  sIsPlaying,		"<player> will play this round!");
666 	ReadString (f, "NetworkTexts", "KickedYou",		  sKickedYou,		"You have been kicked");
667 	ReadString (f, "NetworkTexts", "KickedYouReason", sKickedYouReason, "You have been kicked because <reason>");
668 	ReadString (f, "NetworkTexts", "BannedYou",		  sBannedYou,		"You have been banned");
669 	ReadString (f, "NetworkTexts", "BannedYouReason", sBannedYouReason, "You have been banned because <reason>");
670 	ReadString (f, "NetworkTexts", "YouQuit",		  sYouQuit,			"You have quit");
671 	ReadString (f, "NetworkTexts", "YouTimed",		  sYouTimed,		"You timed out");
672 
673 	ReadString (f, "NetworkTexts", "Killed",	      sKilled,			"<killer> killed <victim>");
674 	ReadString (f, "NetworkTexts", "CommitedSuicide", sCommitedSuicide,	"<player> commited suicide");
675 	ReadString (f, "NetworkTexts", "FirstBlood",	  sFirstBlood,		"<player> drew first blood");
676 	ReadString (f, "NetworkTexts", "TeamKill",		  sTeamkill,		"<player> is an ugly teamkiller");
677 	ReadString (f, "NetworkTexts", "CTFScore",		  sHasScored,		"<player> has scored");
678 	ReadString (f, "NetworkTexts", "KilledAFK",	      sKilledAFK,		"<killer> typekilled <victim>");
679 
680 	ReadString (f, "NetworkTexts", "PlayerOut",		  sPlayerOut,		"<player> is out of the game");
681 	ReadString (f, "NetworkTexts", "TeamOut",		  sTeamOut,			"The <team> team is out of the game");
682 	ReadString (f, "NetworkTexts", "PlayerHasWon",	  sPlayerHasWon,	"<player> has won the match");
683 	ReadString (f, "NetworkTexts", "TeamHasWon",	  sTeamHasWon,		"The <team> team has won the match");
684 	ReadString (f, "NetworkTexts", "TimeLimit",		  sTimeLimit,		"Timelimit has been reached");
685 
686 	ReadString (f, "NetworkTexts", "WormIsIt",		  sWormIsIt,		"<player> is IT!");
687 
688 	ReadString (f, "NetworkTexts", "SeekerMessage",	  sSeekerMessage,	"You are a seeker, you have to find and catch the hiders. You have to catch the hiders before <time> seconds are up.");
689 	ReadString (f, "NetworkTexts", "HiderMessage",	  sHiderMessage,	"You are a hider, you have to run away from the seekers who are red. You have to hide for <time> seconds.");
690 	ReadString (f, "NetworkTexts", "CaughtMessage",	  sCaughtMessage,	"<seeker> caught <hider>!");
691 	ReadString (f, "NetworkTexts", "HiderVisible",	  sHiderVisible,	"You are visible to the seekers, run!");
692 	ReadString (f, "NetworkTexts", "SeekerVisible",	  sSeekerVisible,	"You are visible to the hiders");
693 	ReadString (f, "NetworkTexts", "VisibleMessage",  sVisibleMessage,	"<player> is visible!");
694 	ReadString (f, "NetworkTexts", "YouAreHidden",    sYouAreHidden,	"You are invisible again!");
695 	ReadString (f, "NetworkTexts", "HiddenMessage",   sHiddenMessage,	"<player> is hiding!");
696 
697 
698 	ReadString (f, "NetworkTexts", "Spree1",		  sSpree1,			"<player> is on a killing spree!");
699 	ReadString (f, "NetworkTexts", "Spree2",		  sSpree2,			"<player> is on a rampage!");
700 	ReadString (f, "NetworkTexts", "Spree3",		  sSpree3,			"<player> is dominating!");
701 	ReadString (f, "NetworkTexts", "Spree4",		  sSpree4,			"<player> is unstoppable!");
702 	ReadString (f, "NetworkTexts", "Spree5",		  sSpree5,			"<player> is GODLIKE!");
703 
704 	ReadString (f, "NetworkTexts", "DyingSpree1",	  sDSpree1,			"<player> is on a dying spree!");
705 	ReadString (f, "NetworkTexts", "DyingSpree2",	  sDSpree2,			"<player> is a target!");
706 	ReadString (f, "NetworkTexts", "DyingSpree3",	  sDSpree3,			"<player> is a sitting duck!");
707 	ReadString (f, "NetworkTexts", "DyingSpree4",	  sDSpree4,			"<player> is free kills!");
708 	ReadString (f, "NetworkTexts", "DyingSpree5",	  sDSpree5,			"<player> is WORTHLESS!");
709 
710 	ReadString (f, "NetworkTexts", "ServerFull",	  sServerFull,		"Server is full");
711 	ReadString (f, "NetworkTexts", "NoEmptySlots",	  sNoEmptySlots,	"The server has no emtpy slots");
712 	ReadString (f, "NetworkTexts", "WrongProtocol",	  sWrongProtocol,	"Wrong protocol version. Server protocol version is <version>.");
713 	ReadString (f, "NetworkTexts", "BadVerification", sBadVerification,	"Bad connection verification");
714 	ReadString (f, "NetworkTexts", "NoIpVerification",sNoIpVerification,"No verification for address");
715 	ReadString (f, "NetworkTexts", "GameInProgress",  sGameInProgress,	"Cannot join, the game is currently in progress");
716 	ReadString (f, "NetworkTexts", "YouAreBanned",	  sYouAreBanned,	"You are banned on this server");
717 	ReadString (f, "NetworkTexts", "BotsNotAllowed",  sBotsNotAllowed,	"Sorry, bots are not allowed on this server");
718 	ReadString (f, "NetworkTexts", "WantsJoin",		  sWantsJoin,		"<player> wants to join the server");
719 
720 	ReadString (f, "NetworkTexts", "KnownAs",		  sKnownAs,			"<oldname> is now known as <newname>");
721 
722 	notes << "DONE" << endl;
723 	return true;
724 }
725 
GameInfo()726 GameOptions::GameInfo::GameInfo() {
727 	// For most of the values, it doesn't matter if they are uninited.
728 	// In PrepareGame/UpdateGameLobby, we will get all the values (for cClient).
729 	// Anyway, we set some values because it could crash if these are invalid.
730 	fTimeLimit = -1;
731 	iLives = iKillLimit = iTagLimit = -1;
732 	iLoadingTime = iGeneralGameType = 0;
733 	iMaxPlayers = 8;
734 	gameMode = NULL;
735 }
736 
GameOptions()737 GameOptions::GameOptions() {
738 	// we need to set some initial values for these
739 	bLogTimestamps = false;
740 	iVerbosity = 0;
741 	cfgFilename = DefaultCfgFilename;
742 
743 	// TODO: don't hardcode the size here
744 	sPlayerControls.resize(2);	// Don't change array size or we'll get segfault when vector memory allocation changes
745 }
746