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