From 2fddb199e6009beb30e400489133aa3fd95bd58b Mon Sep 17 00:00:00 2001 From: danij Date: Tue, 12 Aug 2014 14:34:01 +0100 Subject: [PATCH] All Games|libcommon: Improved -episode and -warp to support episode IDs One can now specify an episode in textual, id form to both -episode and -warp from the command line. Note that both options still behave the same as vanilla if episode numbers are supplied instead. --- doomsday/plugins/common/include/common.h | 3 - doomsday/plugins/common/include/g_common.h | 5 +- doomsday/plugins/common/include/g_defs.h | 3 +- doomsday/plugins/common/src/common.c | 15 +-- doomsday/plugins/common/src/g_defs.cpp | 4 +- doomsday/plugins/common/src/g_game.cpp | 147 +++++++++++++++++++-- doomsday/plugins/doom/include/g_game.h | 6 + doomsday/plugins/doom/src/d_main.cpp | 121 +++++------------ doomsday/plugins/doom64/include/g_game.h | 7 + doomsday/plugins/doom64/src/d_main.cpp | 111 +++++----------- doomsday/plugins/heretic/include/g_game.h | 7 + doomsday/plugins/heretic/src/h_main.cpp | 119 +---------------- doomsday/plugins/hexen/include/g_game.h | 6 + doomsday/plugins/hexen/src/h2_main.cpp | 132 ++++++------------ 14 files changed, 283 insertions(+), 403 deletions(-) diff --git a/doomsday/plugins/common/include/common.h b/doomsday/plugins/common/include/common.h index a4d003e448..0ad33e6b88 100644 --- a/doomsday/plugins/common/include/common.h +++ b/doomsday/plugins/common/include/common.h @@ -51,9 +51,6 @@ # include #endif -DENG_EXTERN_C dd_bool sc_FileScripts; -DENG_EXTERN_C char const *sc_ScriptsDir; - #ifdef __cplusplus extern "C" { #endif diff --git a/doomsday/plugins/common/include/g_common.h b/doomsday/plugins/common/include/g_common.h index 2b9ac8a8b7..afc0e092e5 100644 --- a/doomsday/plugins/common/include/g_common.h +++ b/doomsday/plugins/common/include/g_common.h @@ -121,8 +121,9 @@ char const *G_InFineBriefing(de::Uri const *mapUri = 0); char const *G_InFineDebriefing(de::Uri const *mapUri = 0); /** - * @param mapUri Identifier of the episode to lookup the title of. Can be @c 0 in which - * case the title for the @em current episode will be returned (if set). + * @param episodeId Identifier of the episode to lookup the title of. Can be @c 0 in + * which case the title for the @em current episode will be returned + * (if set). */ de::String G_EpisodeTitle(de::String const *episodeId = 0); diff --git a/doomsday/plugins/common/include/g_defs.h b/doomsday/plugins/common/include/g_defs.h index cccbe199cd..26f10f588f 100644 --- a/doomsday/plugins/common/include/g_defs.h +++ b/doomsday/plugins/common/include/g_defs.h @@ -38,12 +38,13 @@ ded_t &Defs(); * better handling of map resources and their references. Instead, use the map URI * mechanism. * + * @param episode Episode identifier. * @param warpNumber Warp number to translate. * * @return The unique identifier of the map. If no game session is in progress or the * warp number is not found, the URI "Maps:" is returned. */ -de::Uri TranslateMapWarpNumber(uint warpNumber); +de::Uri TranslateMapWarpNumber(de::String const &episodeId, uint warpNumber); extern "C" { #endif diff --git a/doomsday/plugins/common/src/common.c b/doomsday/plugins/common/src/common.c index 6e7f32dbb8..f364eef869 100644 --- a/doomsday/plugins/common/src/common.c +++ b/doomsday/plugins/common/src/common.c @@ -1,9 +1,7 @@ -/** - * @file common.c - * Top-level libcommon routines. +/** @file common.c Top-level libcommon routines. * - * @authors Copyright © 2012-2013 Jaakko Keränen - * @authors Copyright © 2013 Daniel Swanson + * @authors Copyright © 2012-2013 Jaakko Keränen + * @authors Copyright © 2013-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -23,9 +21,6 @@ #include "common.h" #include "g_common.h" -dd_bool sc_FileScripts = false; -char const *sc_ScriptsDir = ""; - int Common_GetInteger(int id) { switch(id) @@ -35,9 +30,9 @@ int Common_GetInteger(int id) // manually save the game before, e.g., upgrading to a new version. return G_GameState() == GS_MAP; - default: - break; + default: break; } + return 0; } diff --git a/doomsday/plugins/common/src/g_defs.cpp b/doomsday/plugins/common/src/g_defs.cpp index ba78ff4c5a..9249369f36 100644 --- a/doomsday/plugins/common/src/g_defs.cpp +++ b/doomsday/plugins/common/src/g_defs.cpp @@ -58,9 +58,9 @@ void GetDefState(char const *def, int *val) if(*val < 0) *val = 0; } -de::Uri TranslateMapWarpNumber(uint warpNumber) +de::Uri TranslateMapWarpNumber(String const &episodeId, uint warpNumber) { - if(Record const *rec = COMMON_GAMESESSION->episodeDef()) + if(Record const *rec = Defs().episodes.tryFind("id", episodeId)) { defn::Episode episodeDef(*rec); if(Record const *mgNodeRec = episodeDef.tryFindMapGraphNodeByWarpNumber(warpNumber)) diff --git a/doomsday/plugins/common/src/g_game.cpp b/doomsday/plugins/common/src/g_game.cpp index cbeeafa6b2..b7756048b5 100644 --- a/doomsday/plugins/common/src/g_game.cpp +++ b/doomsday/plugins/common/src/g_game.cpp @@ -23,6 +23,17 @@ #include "common.h" #include "g_common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "am_map.h" #include "animdefs.h" #include "d_net.h" @@ -57,16 +68,6 @@ #include "saveslots.h" #include "x_hair.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - using namespace de; using namespace common; @@ -1026,6 +1027,132 @@ void G_CommonPostInit() DD_Execute(true, "listmaps"); } +void G_AutoStartOrBeginTitleLoop() +{ + CommandLine &cmdLine = DENG2_APP->commandLine(); + + String startEpisodeId; + de::Uri startMapUri; + + // A specific episode? + if(int arg = cmdLine.check("-episode", 1)) + { + String episodeId = cmdLine.at(arg + 1); + if(Record const *episodeDef = Defs().episodes.tryFind("id", episodeId)) + { + // Ensure this is a playable episode. + de::Uri startMap(episodeDef->gets("startMap"), RC_NULL); + if(P_MapExists(startMap.compose().toUtf8().constData())) + { + startEpisodeId = episodeId; + } + } + } + + // A specific map? + if(int arg = cmdLine.check("-warp", 1)) + { + bool haveEpisode = (arg + 2 < cmdLine.count() && !cmdLine.isOption(arg + 2)); + if(haveEpisode) + { + if(Record const *episodeDef = Defs().episodes.tryFind("id", cmdLine.at(arg + 1))) + { + // Ensure this is a playable episode. + de::Uri startMap(episodeDef->gets("startMap"), RC_NULL); + if(P_MapExists(startMap.compose().toUtf8().constData())) + { + startEpisodeId = episodeDef->gets("id"); + } + } + } + + // The map. + bool isNumber; + int oldMapNumber = cmdLine.at(arg + (haveEpisode? 2 : 1)).toInt(&isNumber); +#if !__JHEXEN__ + if(oldMapNumber > 0) oldMapNumber -= 1; // zero-based. +#endif + + if(!isNumber) + { + // It must be a URI, then. + Block rawMapUri = cmdLine.at(arg + (haveEpisode? 2 : 1)).toUtf8(); + char *args[1] = { const_cast(rawMapUri.constData()) }; + startMapUri = de::Uri::fromUserInput(args, 1); + if(startMapUri.scheme().isEmpty()) startMapUri.setScheme("Maps"); + } + else if(!startEpisodeId.isEmpty()) + { +#if __JHEXEN__ + // Map numbers must be translated in the context of an episode. + startMapUri = TranslateMapWarpNumber(startEpisodeId, oldMapNumber); +#else + int oldEpisodeNumber = startEpisodeId.toInt(&isNumber); + if(oldEpisodeNumber > 0) oldEpisodeNumber -= 1; // zero-based. + if(isNumber) + { + startMapUri = G_ComposeMapUri(oldEpisodeNumber, oldMapNumber); + } +#endif + } + else + { + startMapUri = G_ComposeMapUri(0, oldMapNumber); + } + } + + // Are we attempting an auto-start? + bool autoStart = (IS_NETGAME || !startEpisodeId.isEmpty() || !startMapUri.isEmpty()); + if(autoStart) + { + if(startEpisodeId.isEmpty()) + { + // Pick the first playable episode. + auto const &episodesById = Defs().episodes.lookup("id").elements(); + DENG2_FOR_EACH_CONST(DictionaryValue::Elements, i, episodesById) + { + Record const &episodeDef = *i->second->as().record(); + de::Uri startMap(episodeDef.gets("startMap"), RC_NULL); + if(P_MapExists(startMap.compose().toUtf8().constData())) + { + startEpisodeId = episodeDef.gets("id"); + break; + } + } + } + + // Ensure that the map exists. + if(!P_MapExists(startMapUri.compose().toUtf8().constData())) + { + startMapUri.clear(); + + // Pick the start map from the episode, if specified and playable. + if(Record const *episodeDef = Defs().episodes.tryFind("id", startEpisodeId)) + { + de::Uri startMap(episodeDef->gets("startMap"), RC_NULL); + if(P_MapExists(startMap.compose().toUtf8().constData())) + { + startMapUri = startMap; + } + } + } + } + + // Are we auto-starting? + if(!startEpisodeId.isEmpty() && !startMapUri.isEmpty()) + { + LOG_NOTE("Auto-starting episode '%s', map \"%s\", skill %i") + << startEpisodeId + << startMapUri + << ::defaultGameRules.skill; + G_SetGameActionNewSession(::defaultGameRules, startEpisodeId, startMapUri); + } + else + { + COMMON_GAMESESSION->endAndBeginTitle(); // Start up intro loop. + } +} + /** * Common game shutdown routine. * @note Game-specific actions should be placed in G_Shutdown rather than here. diff --git a/doomsday/plugins/doom/include/g_game.h b/doomsday/plugins/doom/include/g_game.h index 30f27aa011..ce840e4511 100644 --- a/doomsday/plugins/doom/include/g_game.h +++ b/doomsday/plugins/doom/include/g_game.h @@ -72,6 +72,12 @@ void G_CommonPreInit(void); */ void G_CommonPostInit(void); +/** + * To be called post-game initialization, to examine the command line to determine if + * a new game session should be started automatically, or, begin the title loop. + */ +void G_AutoStartOrBeginTitleLoop(void); + void G_CommonShutdown(void); void R_InitRefresh(void); diff --git a/doomsday/plugins/doom/src/d_main.cpp b/doomsday/plugins/doom/src/d_main.cpp index 0432e21eb3..e1ce55fcc4 100644 --- a/doomsday/plugins/doom/src/d_main.cpp +++ b/doomsday/plugins/doom/src/d_main.cpp @@ -22,6 +22,7 @@ #include "jdoom.h" +#include #include "d_netsv.h" #include "gamesession.h" #include "m_argv.h" @@ -387,8 +388,7 @@ void D_PreInit() void D_PostInit() { - bool autoStart = false; - de::Uri startMapUri; + CommandLine &cmdLine = DENG2_APP->commandLine(); /// @todo Kludge: Border background is different in DOOM2. /// @todo Do this properly! @@ -409,44 +409,53 @@ void D_PostInit() // Get skill / episode / map from parms. ::defaultGameRules.skill = /*startSkill =*/ SM_MEDIUM; - if(CommandLine_Check("-altdeath")) + if(cmdLine.check("-altdeath")) + { ::cfg.netDeathmatch = 2; - else if(CommandLine_Check("-deathmatch")) + } + else if(cmdLine.check("-deathmatch")) + { ::cfg.netDeathmatch = 1; + } // Apply these rules. - ::defaultGameRules.noMonsters = CommandLine_Check("-nomonsters")? true : false; - ::defaultGameRules.respawnMonsters = CommandLine_Check("-respawn")? true : false; - ::defaultGameRules.fast = CommandLine_Check("-fast")? true : false; + ::defaultGameRules.noMonsters = cmdLine.check("-nomonsters")? true : false; + ::defaultGameRules.respawnMonsters = cmdLine.check("-respawn") ? true : false; + ::defaultGameRules.fast = cmdLine.check("-fast") ? true : false; - int p = CommandLine_Check("-timer"); - if(p && p < myargc - 1 && ::defaultGameRules.deathmatch) + if(::defaultGameRules.deathmatch) { - int time = atoi(CommandLine_At(p + 1)); - App_Log(DE2_LOG_NOTE, "Maps will end after %d %s", time, time == 1? "minute" : "minutes"); + if(int arg = cmdLine.check("-timer", 1)) + { + bool isNumber; + int mins = cmdLine.at(arg + 1).toInt(&isNumber); + if(isNumber) + { + LOG_NOTE("Maps will end after %i %s") + << mins << (mins == 1? "minute" : "minutes"); + } + } } - // Turbo option. - p = CommandLine_Check("-turbo"); + // Change the turbo multiplier? ::turboMul = 1.0f; - if(p) + if(int arg = cmdLine.check("-turbo")) { int scale = 200; - if(p < myargc - 1) + if(arg + 1 < cmdLine.count() && !cmdLine.isOption(arg + 1)) { - scale = atoi(CommandLine_At(p + 1)); + scale = cmdLine.at(arg + 1).toInt(); } scale = de::clamp(10, scale, 400); - App_Log(DE2_MAP_NOTE, "Turbo scale: %i%%", scale); + LOG_NOTE("Turbo scale: %i%%") << scale; ::turboMul = scale / 100.f; } // Load a saved game? - p = CommandLine_Check("-loadgame"); - if(p && p < myargc - 1) + if(int arg = cmdLine.check("-loadgame", 1)) { - if(SaveSlot *sslot = G_SaveSlots().slotByUserInput(CommandLine_At(p + 1))) + if(SaveSlot *sslot = G_SaveSlots().slotByUserInput(cmdLine.at(arg + 1))) { if(sslot->isUserWritable() && G_SetGameActionLoadSession(sslot->id())) { @@ -456,78 +465,14 @@ void D_PostInit() } } - p = CommandLine_Check("-skill"); - if(p && p < myargc - 1) + // Change the default skill mode? + if(int arg = cmdLine.check("-skill", 1)) { - int skillNumber = atoi(CommandLine_At(p + 1)); + int skillNumber = cmdLine.at(arg + 1).toInt(); ::defaultGameRules.skill = (skillmode_t)(skillNumber > 0? skillNumber - 1 : skillNumber); - autoStart = true; } - p = CommandLine_Check("-episode"); - if(p && p < myargc - 1) - { - int episodeNumber = atoi(CommandLine_At(p + 1)); - - startMapUri = G_ComposeMapUri(episodeNumber > 0? episodeNumber - 1 : episodeNumber, 0); - autoStart = true; - } - - p = CommandLine_Check("-warp"); - if(p && p < myargc - 1) - { - autoStart = true; - - bool isNumber; - String(CommandLine_At(p + 1)).toInt(&isNumber); - if(!isNumber) - { - // It must be a URI, then. - char *args[1] = { const_cast(CommandLine_At(p + 1)) }; - startMapUri = de::Uri::fromUserInput(args, 1); - if(startMapUri.scheme().isEmpty()) - startMapUri.setScheme("Maps"); - } - else - { - if(gameModeBits & (GM_ANY_DOOM2|GM_DOOM_CHEX)) - { - int mapNumber = String(CommandLine_At(p + 1)).toInt(); - startMapUri = G_ComposeMapUri(0, mapNumber > 0? mapNumber - 1 : mapNumber); - } - else if(p < myargc - 2) - { - int episodeNumber = String(CommandLine_At(p + 1)).toInt(); - int mapNumber = String(CommandLine_At(p + 2)).toInt(); - - startMapUri = G_ComposeMapUri(episodeNumber > 0? episodeNumber - 1 : episodeNumber, - mapNumber > 0? mapNumber - 1 : mapNumber); - } - } - } - - if(startMapUri.path().isEmpty()) - { - startMapUri = G_ComposeMapUri(0, 0); - } - - // Are we autostarting? - if(autoStart) - { - App_Log(DE2_LOG_NOTE, "Autostart in Map %s, Skill %d", - startMapUri.asText().toUtf8().constData(), - ::defaultGameRules.skill); - } - - // Validate episode and map. - if((autoStart || IS_NETGAME) && P_MapExists(startMapUri.compose().toUtf8().constData())) - { - G_SetGameActionNewSession(startMapUri, 0/*default*/, ::defaultGameRules); - } - else - { - COMMON_GAMESESSION->endAndBeginTitle(); // Start up intro loop. - } + G_AutoStartOrBeginTitleLoop(); } void D_Shutdown() diff --git a/doomsday/plugins/doom64/include/g_game.h b/doomsday/plugins/doom64/include/g_game.h index ac99e2306f..3bd5c8b485 100644 --- a/doomsday/plugins/doom64/include/g_game.h +++ b/doomsday/plugins/doom64/include/g_game.h @@ -60,6 +60,13 @@ extern "C" { void G_Register(void); void G_CommonPreInit(void); void G_CommonPostInit(void); + +/** + * To be called post-game initialization, to examine the command line to determine if + * a new game session should be started automatically, or, begin the title loop. + */ +void G_AutoStartOrBeginTitleLoop(void); + void G_CommonShutdown(void); void R_InitRefresh(void); diff --git a/doomsday/plugins/doom64/src/d_main.cpp b/doomsday/plugins/doom64/src/d_main.cpp index b5e0500a4f..3fa13292e4 100644 --- a/doomsday/plugins/doom64/src/d_main.cpp +++ b/doomsday/plugins/doom64/src/d_main.cpp @@ -23,6 +23,8 @@ #include "jdoom64.h" +#include +#include #include "am_map.h" #include "d_netsv.h" #include "g_defs.h" @@ -31,7 +33,6 @@ #include "p_inventory.h" #include "p_map.h" #include "saveslots.h" -#include using namespace de; using namespace common; @@ -300,8 +301,7 @@ void D_PreInit() void D_PostInit() { - bool autoStart = false; - de::Uri startMapUri; + CommandLine &cmdLine = DENG2_APP->commandLine(); // Common post init routine. G_CommonPostInit(); @@ -318,47 +318,53 @@ void D_PostInit() // Get skill / episode / map from parms. ::defaultGameRules.skill = /*startSkill =*/ SM_MEDIUM; - // Game mode specific settings - // None. - - if(CommandLine_Check("-altdeath")) + if(cmdLine.check("-altdeath")) + { ::cfg.netDeathmatch = 2; - else if(CommandLine_Check("-deathmatch")) + } + else if(cmdLine.check("-deathmatch")) + { ::cfg.netDeathmatch = 1; + } // Apply these rules. - ::defaultGameRules.noMonsters = CommandLine_Check("-nomonsters")? true : false; - ::defaultGameRules.respawnMonsters = CommandLine_Check("-respawn")? true : false; - ::defaultGameRules.fast = CommandLine_Check("-fast")? true : false; + ::defaultGameRules.noMonsters = cmdLine.check("-nomonsters")? true : false; + ::defaultGameRules.respawnMonsters = cmdLine.check("-respawn") ? true : false; + ::defaultGameRules.fast = cmdLine.check("-fast") ? true : false; - int p = CommandLine_Check("-timer"); - if(p && p < myargc - 1 && ::defaultGameRules.deathmatch) + if(::defaultGameRules.deathmatch) { - int time = atoi(CommandLine_At(p + 1)); - App_Log(DE2_LOG_NOTE, "Maps will end after %d %s", time, time == 1? "minute" : "minutes"); + if(int arg = cmdLine.check("-timer", 1)) + { + bool isNumber; + int mins = cmdLine.at(arg + 1).toInt(&isNumber); + if(isNumber) + { + LOG_NOTE("Maps will end after %i %s") + << mins << (mins == 1? "minute" : "minutes"); + } + } } - // Turbo option. - p = CommandLine_Check("-turbo"); + // Change the turbo multiplier? ::turboMul = 1.0f; - if(p) + if(int arg = cmdLine.check("-turbo")) { int scale = 200; - if(p < myargc - 1) + if(arg + 1 < cmdLine.count() && !cmdLine.isOption(arg + 1)) { - scale = atoi(CommandLine_At(p + 1)); + scale = cmdLine.at(arg + 1).toInt(); } scale = de::clamp(10, scale, 400); - App_Log(DE2_MAP_NOTE, "Turbo scale: %i%%", scale); + LOG_NOTE("Turbo scale: %i%%") << scale; ::turboMul = scale / 100.f; } // Load a saved game? - p = CommandLine_Check("-loadgame"); - if(p && p < myargc - 1) + if(int arg = cmdLine.check("-loadgame", 1)) { - if(SaveSlot *sslot = G_SaveSlots().slotByUserInput(CommandLine_At(p + 1))) + if(SaveSlot *sslot = G_SaveSlots().slotByUserInput(cmdLine.at(arg + 1))) { if(sslot->isUserWritable() && G_SetGameActionLoadSession(sslot->id())) { @@ -368,61 +374,14 @@ void D_PostInit() } } - p = CommandLine_Check("-skill"); - if(p && p < myargc - 1) + // Change the default skill mode? + if(int arg = cmdLine.check("-skill", 1)) { - bool isNumber; - int skillNumber = String(CommandLine_At(p + 1)).toInt(&isNumber); - if(isNumber) - { - ::defaultGameRules.skill = (skillmode_t)(skillNumber > 0? skillNumber - 1 : skillNumber); - autoStart = true; - } + int skillNumber = cmdLine.at(arg + 1).toInt(); + ::defaultGameRules.skill = (skillmode_t)(skillNumber > 0? skillNumber - 1 : skillNumber); } - p = CommandLine_Check("-warp"); - if(p && p < myargc - 1) - { - autoStart = true; - - bool isNumber; - int mapNumber = String(CommandLine_At(p + 1)).toInt(&isNumber); - if(!isNumber) - { - // It must be a URI, then. - char *args[1] = { const_cast(CommandLine_At(p + 1)) }; - startMapUri = de::Uri::fromUserInput(args, 1); - if(startMapUri.scheme().isEmpty()) - startMapUri.setScheme("Maps"); - } - else - { - startMapUri = G_ComposeMapUri(0, mapNumber > 0? mapNumber - 1 : mapNumber); - } - } - - if(startMapUri.path().isEmpty()) - { - startMapUri = G_ComposeMapUri(0, 0); - } - - // Are we autostarting? - if(autoStart) - { - App_Log(DE2_LOG_NOTE, "Autostart in Map %s, Skill %d", - startMapUri.asText().toUtf8().constData(), - ::defaultGameRules.skill); - } - - // Validate episode and map. - if((autoStart || IS_NETGAME) && P_MapExists(startMapUri.compose().toUtf8().constData())) - { - G_SetGameActionNewSession(startMapUri, 0/*default*/, ::defaultGameRules); - } - else - { - COMMON_GAMESESSION->endAndBeginTitle(); // Start up intro loop. - } + G_AutoStartOrBeginTitleLoop(); } void D_Shutdown() diff --git a/doomsday/plugins/heretic/include/g_game.h b/doomsday/plugins/heretic/include/g_game.h index 6509684852..72e2c4a397 100644 --- a/doomsday/plugins/heretic/include/g_game.h +++ b/doomsday/plugins/heretic/include/g_game.h @@ -61,6 +61,13 @@ extern "C" { void G_Register(void); void G_CommonPreInit(void); void G_CommonPostInit(void); + +/** + * To be called post-game initialization, to examine the command line to determine if + * a new game session should be started automatically, or, begin the title loop. + */ +void G_AutoStartOrBeginTitleLoop(void); + void G_CommonShutdown(void); void R_InitRefresh(void); diff --git a/doomsday/plugins/heretic/src/h_main.cpp b/doomsday/plugins/heretic/src/h_main.cpp index 9000df4fea..bbbfc8bd53 100644 --- a/doomsday/plugins/heretic/src/h_main.cpp +++ b/doomsday/plugins/heretic/src/h_main.cpp @@ -24,7 +24,6 @@ #include #include -#include #include "am_map.h" #include "d_netsv.h" #include "g_defs.h" @@ -342,11 +341,6 @@ void H_PostInit() // Defaults for skill, episode and map. ::defaultGameRules.skill = /*startSkill =*/ SM_MEDIUM; - String startEpisodeId; - de::Uri startMapUri; - - // Game mode specific settings. - /* None */ if(cmdLine.check("-deathmatch")) { @@ -362,7 +356,7 @@ void H_PostInit() if(int arg = cmdLine.check("-turbo")) { int scale = 200; - if(arg + 1 < cmdLine.count() && cmdLine.isOption(arg + 1)) + if(arg + 1 < cmdLine.count() && !cmdLine.isOption(arg + 1)) { scale = cmdLine.at(arg + 1).toInt(); } @@ -392,116 +386,7 @@ void H_PostInit() ::defaultGameRules.skill = (skillmode_t)(skillNumber > 0? skillNumber - 1 : skillNumber); } - // Auto-start a specific episode? - if(int arg = cmdLine.check("-episode", 1)) - { - String episodeId = cmdLine.at(arg + 1); - if(Record const *episodeDef = Defs().episodes.tryFind("id", episodeId)) - { - // Ensure this is a playable episode. - de::Uri startMap(episodeDef->gets("startMap"), RC_NULL); - if(P_MapExists(startMap.compose().toUtf8().constData())) - { - startEpisodeId = episodeId; - } - } - } - - // Auto-start a specific map? - if(int arg = cmdLine.check("-warp", 1)) - { - bool haveEpisode = (arg + 2 < cmdLine.count() && !cmdLine.isOption(arg + 2)); - if(haveEpisode) - { - if(Record const *episodeDef = Defs().episodes.tryFind("id", cmdLine.at(arg + 1))) - { - // Ensure this is a playable episode. - de::Uri startMap(episodeDef->gets("startMap"), RC_NULL); - if(P_MapExists(startMap.compose().toUtf8().constData())) - { - startEpisodeId = episodeDef->gets("id"); - } - } - } - - // The map. - bool isNumber; - int oldMapNumber = cmdLine.at(arg + (haveEpisode? 2 : 1)).toInt(&isNumber); - if(oldMapNumber > 0) oldMapNumber -= 1; // zero-based. - - if(!isNumber) - { - // It must be a URI, then. - Block rawMapUri = cmdLine.at(arg + (haveEpisode? 2 : 1)).toUtf8(); - char *args[1] = { const_cast(rawMapUri.constData()) }; - startMapUri = de::Uri::fromUserInput(args, 1); - if(startMapUri.scheme().isEmpty()) startMapUri.setScheme("Maps"); - } - else if(!startEpisodeId.isEmpty()) - { - int oldEpisodeNumber = startEpisodeId.toInt(&isNumber); - if(oldEpisodeNumber > 0) oldEpisodeNumber -= 1; // zero-based. - if(isNumber) - { - startMapUri = G_ComposeMapUri(oldEpisodeNumber, oldMapNumber); - } - } - else - { - startMapUri = G_ComposeMapUri(0, oldMapNumber); - } - } - - // Are we attempting an auto-start? - bool autoStart = (IS_NETGAME || !startEpisodeId.isEmpty() || !startMapUri.isEmpty()); - if(autoStart) - { - if(startEpisodeId.isEmpty()) - { - // Pick the first playable episode. - auto const &episodesById = Defs().episodes.lookup("id").elements(); - DENG2_FOR_EACH_CONST(DictionaryValue::Elements, i, episodesById) - { - Record const &episodeDef = *i->second->as().record(); - de::Uri startMap(episodeDef.gets("startMap"), RC_NULL); - if(P_MapExists(startMap.compose().toUtf8().constData())) - { - startEpisodeId = episodeDef.gets("id"); - break; - } - } - } - - // Ensure that the map exists. - if(!P_MapExists(startMapUri.compose().toUtf8().constData())) - { - startMapUri.clear(); - - // Pick the first start map from the episode. - if(Record const *episodeDef = Defs().episodes.tryFind("id", startEpisodeId)) - { - de::Uri startMap(episodeDef->gets("startMap"), RC_NULL); - if(P_MapExists(startMap.compose().toUtf8().constData())) - { - startMapUri = startMap; - } - } - } - } - - // Are we autostarting? - if(!startEpisodeId.isEmpty() && !startMapUri.isEmpty()) - { - LOG_NOTE("Auto-starting episode '%s', map \"%s\", skill %i") - << startEpisodeId - << startMapUri - << ::defaultGameRules.skill; - G_SetGameActionNewSession(::defaultGameRules, startEpisodeId, startMapUri); - } - else - { - COMMON_GAMESESSION->endAndBeginTitle(); // Start up intro loop. - } + G_AutoStartOrBeginTitleLoop(); } void H_Shutdown() diff --git a/doomsday/plugins/hexen/include/g_game.h b/doomsday/plugins/hexen/include/g_game.h index 83cc825a10..f52153665b 100644 --- a/doomsday/plugins/hexen/include/g_game.h +++ b/doomsday/plugins/hexen/include/g_game.h @@ -61,6 +61,12 @@ void G_QuitGame(void); void G_CommonPreInit(void); void G_CommonPostInit(void); +/** + * To be called post-game initialization, to examine the command line to determine if + * a new game session should be started automatically, or, begin the title loop. + */ +void G_AutoStartOrBeginTitleLoop(void); + int G_GetInteger(int id); void *G_GetVariable(int id); diff --git a/doomsday/plugins/hexen/src/h2_main.cpp b/doomsday/plugins/hexen/src/h2_main.cpp index 0ddd4393f2..276adf98ec 100644 --- a/doomsday/plugins/hexen/src/h2_main.cpp +++ b/doomsday/plugins/hexen/src/h2_main.cpp @@ -22,6 +22,8 @@ #include "jhexen.h" +#include +#include #include "am_map.h" #include "d_netsv.h" #include "g_common.h" @@ -33,9 +35,7 @@ #include "player.h" #include "p_saveg.h" #include "p_sound.h" - #include "saveslots.h" -#include using namespace de; using namespace common; @@ -296,9 +296,7 @@ void X_PreInit() void X_PostInit() { - bool autoStart = false; - de::Uri startMapUri; - playerclass_t startPlayerClass = PCLASS_NONE; + CommandLine &cmdLine = DENG2_APP->commandLine(); // Do this early as other systems need to know. P_InitPlayerClassInfo(); @@ -309,54 +307,44 @@ void X_PostInit() // Initialize weapon info using definitions. P_InitWeaponInfo(); - // Game parameters. - /* None */ - // Defaults for skill, episode and map. ::defaultGameRules.skill = /*startSkill =*/ SM_MEDIUM; - // Game mode specific settings. - /* None */ - - ::cfg.netDeathmatch = CommandLine_Exists("-deathmatch"); + ::cfg.netDeathmatch = cmdLine.check("-deathmatch")? true : false; - ::defaultGameRules.noMonsters = CommandLine_Check("-nomonsters")? true : false; - ::defaultGameRules.randomClasses = CommandLine_Exists("-randclass")? true : false; + ::defaultGameRules.noMonsters = cmdLine.check("-nomonsters")? true : false; + ::defaultGameRules.randomClasses = cmdLine.check("-randclass") ? true : false; - // Turbo movement option. - int p = CommandLine_Check("-turbo"); + // Change the turbo multiplier? ::turboMul = 1.0f; - if(p) + if(int arg = cmdLine.check("-turbo")) { int scale = 200; - if(p < CommandLine_Count() - 1) + if(arg + 1 < cmdLine.count() && !cmdLine.isOption(arg + 1)) { - scale = atoi(CommandLine_At(p + 1)); + scale = cmdLine.at(arg + 1).toInt(); } - de::clamp(10, scale, 400); + scale = de::clamp(10, scale, 400); - App_Log(DE2_MAP_NOTE, "Turbo scale: %i%%", scale); + LOG_NOTE("Turbo scale: %i%%") << scale; ::turboMul = scale / 100.f; } - if((p = CommandLine_CheckWith("-scripts", 1)) != 0) - { - ::sc_FileScripts = true; - ::sc_ScriptsDir = CommandLine_At(p + 1); - } - // Process sound definitions. SndInfoParser(AutoStr_FromText("Lumps:SNDINFO")); // Process sound sequence scripts. - SndSeqParser(::sc_FileScripts? Str_Appendf(AutoStr_New(), "%sSNDSEQ.txt", ::sc_ScriptsDir) - : AutoStr_FromText("Lumps:SNDSEQ")); + String scriptPath("Lumps:SNDSEQ"); + if(int arg = cmdLine.check("-scripts", 1)) + { + scriptPath = cmdLine.at(arg + 1) + "SNDSEQ.txt"; + } + SndSeqParser(AutoStr_FromTextStd(scriptPath.toUtf8().constData())); // Load a saved game? - p = CommandLine_CheckWith("-loadgame", 1); - if(p != 0) + if(int arg = cmdLine.check("-loadgame", 1)) { - if(SaveSlot *sslot = G_SaveSlots().slotByUserInput(CommandLine_At(p + 1))) + if(SaveSlot *sslot = G_SaveSlots().slotByUserInput(cmdLine.at(arg + 1))) { if(sslot->isUserWritable() && G_SetGameActionLoadSession(sslot->id())) { @@ -366,82 +354,38 @@ void X_PostInit() } } - if((p = CommandLine_CheckWith("-skill", 1)) != 0) - { - int skillNumber = atoi(CommandLine_At(p + 1)); - ::defaultGameRules.skill = (skillmode_t)(skillNumber > 0? skillNumber - 1 : skillNumber); - autoStart = true; - } - - if((p = CommandLine_Check("-class")) != 0) - { - playerclass_t pClass = (playerclass_t)atoi(CommandLine_At(p + 1)); - if(!VALID_PLAYER_CLASS(pClass)) - { - App_Log(DE2_LOG_WARNING, "Invalid player class id=%d specified with -class", (int)pClass); - } - else if(!PCLASS_INFO(pClass)->userSelectable) - { - App_Log(DE2_LOG_WARNING, "Non-user-selectable player class id=%d specified with -class", (int)pClass); - } - else - { - startPlayerClass = pClass; - } - } - - if(startPlayerClass != PCLASS_NONE) + // Change the default skill mode? + if(int arg = cmdLine.check("-skill", 1)) { - App_Log(DE2_LOG_NOTE, "Player Class: '%s'", PCLASS_INFO(startPlayerClass)->niceName); - ::cfg.playerClass[CONSOLEPLAYER] = startPlayerClass; - autoStart = true; + int skillNumber = cmdLine.at(arg + 1).toInt(); + ::defaultGameRules.skill = skillmode_t( skillNumber > 0? skillNumber - 1 : skillNumber ); } - // Check for command line warping. - p = CommandLine_Check("-warp"); - if(p && p < CommandLine_Count() - 1) + // Change the default player class? + playerclass_t defPlayerClass = PCLASS_NONE; + if(int arg = cmdLine.check("-class", 1)) { - autoStart = true; - bool isNumber; - int mapNumber = String(CommandLine_At(p + 1)).toInt(&isNumber); - if(!isNumber) + playerclass_t pClass = playerclass_t( cmdLine.at(arg + 1).toInt(&isNumber) ); + if(isNumber && VALID_PLAYER_CLASS(pClass)) { - // It must be a URI, then. - char *args[1] = { const_cast(CommandLine_At(p + 1)) }; - startMapUri = de::Uri::fromUserInput(args, 1); - if(startMapUri.scheme().isEmpty()) - startMapUri.setScheme("Maps"); + if(!PCLASS_INFO(pClass)->userSelectable) + { + LOG_WARNING("Non-user-selectable player class '%i' specified with -class") << int( pClass ); + } } else { - startMapUri = TranslateMapWarpNumber(mapNumber); + LOG_WARNING("Invalid player class '%i' specified with -class") << int( pClass ); } } - - if(startMapUri.path().isEmpty()) - { - startMapUri = TranslateMapWarpNumber(1); - } - - // Are we autostarting? - if(autoStart) + if(defPlayerClass != PCLASS_NONE) { - App_Log(DE2_LOG_NOTE, "Autostart in Map %s, Skill %d", - startMapUri.asText().toUtf8().constData(), - ::defaultGameRules.skill); + ::cfg.playerClass[CONSOLEPLAYER] = defPlayerClass; + LOG_NOTE("Player Class: '%s'") << PCLASS_INFO(defPlayerClass)->niceName; } - // Validate episode and map. - if((autoStart || IS_NETGAME) && P_MapExists(startMapUri.compose().toUtf8().constData())) - { - G_SetGameActionNewSession(startMapUri, 0/*default*/, ::defaultGameRules); - } - else - { - // Start up intro loop. - COMMON_GAMESESSION->endAndBeginTitle(); - } + G_AutoStartOrBeginTitleLoop(); } void X_Shutdown()