forked from mirrors/0ad
Allow the CLI to load skirmish maps. From now on, the directory must be included in the map path to load any maps (see the readme). Patch by Teiresias. Fixes #2572
This was SVN commit r15257.
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
COMMAND LINE OPTIONS
|
||||
|
||||
Basic gameplay:
|
||||
-autostart load a map instead of showing main menu (see below)
|
||||
-autostart=... load a map instead of showing main menu (see below)
|
||||
-editor launch the Atlas scenario editor
|
||||
-mod NAME start the game using NAME mod
|
||||
-quickstart load faster (disables audio and some system info logging)
|
||||
|
||||
Autostart:
|
||||
-autostart=NAME map NAME for scenario, or rms name for random map
|
||||
-autostart=TYPEDIR/MAPNAME enables autostart, selects map in subdirectory of maps/
|
||||
-autostart-ai=PLAYER:AI adds named AI to the given PLAYER (e.g. 2:testbot)
|
||||
-autostart-aidiff=PLAYER:level changes the difficulty setting for AI PLAYER (0: easy, 3: very hard)
|
||||
Multiplayer:
|
||||
@@ -17,7 +17,6 @@ Multiplayer:
|
||||
-autostart-client multiplayer client mode
|
||||
-autostart-ip=IP multiplayer client: connect to this host IP
|
||||
Random maps only:
|
||||
-autostart-random random map
|
||||
-autostart-random=SEED random map with SEED value (default 0, use -1 for random)
|
||||
-autostart-size=TILES random map SIZE in tiles (default 192)
|
||||
-autostart-players=NUMBER NUMBER of players on random map
|
||||
|
||||
@@ -1059,6 +1059,54 @@ void RenderCursor(bool RenderingState)
|
||||
g_DoRenderCursor = RenderingState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily loads a scenario map and retrieves the "ScriptSettings" JSON
|
||||
* data from it.
|
||||
* The scenario map format is used for scenario and skirmish map types (random
|
||||
* games do not use a "map" (format) but a small JavaScript program which
|
||||
* creates a map on the fly). It contains a section to initialize the game
|
||||
* setup screen.
|
||||
* @param mapPath Absolute path (from VSF root) to the map file to peek in.
|
||||
* @return ScriptSettings in JSON format extracted from the map.
|
||||
*/
|
||||
CStr8 LoadSettingsOfScenarioMap(const VfsPath &mapPath)
|
||||
{
|
||||
CXeromyces mapFile;
|
||||
const char *pathToSettings[] =
|
||||
{
|
||||
"Scenario", "ScriptSettings", "" // Path to JSON data in map
|
||||
};
|
||||
|
||||
Status loadResult = mapFile.Load(g_VFS, mapPath);
|
||||
|
||||
if (INFO::OK != loadResult)
|
||||
{
|
||||
LOGERROR(L"Unable to load map file - maybe path typo?");
|
||||
throw PSERROR_Game_World_MapLoadFailed("Unable to load map file - maybe path typo?");
|
||||
}
|
||||
XMBElement mapElement = mapFile.GetRoot();
|
||||
|
||||
// Select the ScriptSettings node in the map file...
|
||||
for (int i = 0; pathToSettings[i][0]; i++)
|
||||
{
|
||||
int childId = mapFile.GetElementID(pathToSettings[i]);
|
||||
|
||||
XMBElementList children = mapElement.GetChildNodes();
|
||||
for (int childIndex = 0; childIndex < children.Count; childIndex++)
|
||||
{
|
||||
XMBElement child = children.Item(childIndex);
|
||||
if (child.GetNodeName() == childId)
|
||||
{
|
||||
mapElement = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ... they contain a JSON document to initialize the game setup
|
||||
// screen
|
||||
return mapElement.GetText();
|
||||
}
|
||||
|
||||
bool Autostart(const CmdLineArgs& args)
|
||||
{
|
||||
/*
|
||||
@@ -1103,13 +1151,22 @@ bool Autostart(const CmdLineArgs& args)
|
||||
CScriptVal playerData;
|
||||
scriptInterface.Eval("([])", playerData);
|
||||
|
||||
// Set different attributes for random or scenario game
|
||||
if (args.Has("autostart-random"))
|
||||
{
|
||||
CStr seedArg = args.Get("autostart-random");
|
||||
// The directory in front of the actual map name indicates which type
|
||||
// of map is being loaded. Drawback of this approach is the association
|
||||
// of map types and folders is hard-coded, but benefits are:
|
||||
// - No need to pass the map type via command line separately (as was
|
||||
// done by -autostart-random)
|
||||
// - Prevents mixing up of scenarios and skirmish maps to some degree
|
||||
Path mapPath = Path(autoStartName);
|
||||
std::wstring mapDirectory = mapPath.Parent().Filename().string();
|
||||
std::string mapType;
|
||||
|
||||
if (mapDirectory == L"random")
|
||||
{
|
||||
// Default seed is 0
|
||||
u32 seed = 0;
|
||||
CStr seedArg = args.Get("autostart-random");
|
||||
|
||||
if (!seedArg.empty())
|
||||
{
|
||||
if (seedArg.compare("-1") == 0)
|
||||
@@ -1123,7 +1180,7 @@ bool Autostart(const CmdLineArgs& args)
|
||||
}
|
||||
|
||||
// Random map definition will be loaded from JSON file, so we need to parse it
|
||||
std::wstring scriptPath = L"maps/random/" + autoStartName.FromUTF8() + L".json";
|
||||
std::wstring scriptPath = L"maps/" + autoStartName.FromUTF8() + L".json";
|
||||
CScriptValRooted scriptData = scriptInterface.ReadJSONFile(scriptPath);
|
||||
if (!scriptData.undefined() && scriptInterface.GetProperty(scriptData.get(), "settings", settings))
|
||||
{
|
||||
@@ -1147,8 +1204,6 @@ bool Autostart(const CmdLineArgs& args)
|
||||
mapSize = size.ToUInt();
|
||||
}
|
||||
|
||||
scriptInterface.SetProperty(attrs.get(), "map", std::string(autoStartName));
|
||||
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("random"));
|
||||
scriptInterface.SetProperty(settings.get(), "Seed", seed); // Random seed
|
||||
scriptInterface.SetProperty(settings.get(), "Size", mapSize); // Random map size (in patches)
|
||||
|
||||
@@ -1171,14 +1226,48 @@ bool Autostart(const CmdLineArgs& args)
|
||||
scriptInterface.SetProperty(player.get(), "Civ", std::string("athen"));
|
||||
scriptInterface.SetPropertyInt(playerData.get(), i, player);
|
||||
}
|
||||
mapType = "random";
|
||||
}
|
||||
else
|
||||
if (mapDirectory == L"scenarios")
|
||||
{
|
||||
// TODO: support akirmish maps
|
||||
std::string mapFile = "maps/scenarios/" + autoStartName;
|
||||
scriptInterface.SetProperty(attrs.get(), "map", mapFile);
|
||||
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario"));
|
||||
// Initialize general settings from the map data so some values
|
||||
// (e.g. name of map) are always present, even when autostart is
|
||||
// less-than-completely configured
|
||||
// (Omitting this may cause the loading screen to display "Loading (undefined)",
|
||||
// for example...)
|
||||
CStr8 mapSettingsJSON = LoadSettingsOfScenarioMap("maps/" + autoStartName + ".xml");
|
||||
CScriptValRooted mapSettings = scriptInterface.ParseJSON(mapSettingsJSON);
|
||||
|
||||
settings = mapSettings.get();
|
||||
mapType = "scenario";
|
||||
}
|
||||
if (mapDirectory == L"skirmishes")
|
||||
{
|
||||
// In skirmish mode, the player initialization data is taken from the
|
||||
// game-setup settings (see CGame::RegisterInit(...)). If some player
|
||||
// is not initialized (PlayerData[] holding fewer/more entries than
|
||||
// defined in map), there's a crash.
|
||||
// To prevent this, we mimic the behavior of the game setup screen by
|
||||
// retrieving the map settings from the actual map xml...
|
||||
CStr8 mapSettingsJSON = LoadSettingsOfScenarioMap("maps/" + autoStartName + ".xml");
|
||||
CScriptValRooted mapSettings = scriptInterface.ParseJSON(mapSettingsJSON);
|
||||
|
||||
settings = mapSettings.get();
|
||||
// ...and initialize the playerData array being edited by
|
||||
// autostart-civ et.al. with the real map data, so sensible values
|
||||
// are always present:
|
||||
scriptInterface.GetProperty(settings.get(), "PlayerData", playerData);
|
||||
mapType = "skirmish";
|
||||
}
|
||||
if (mapType.empty())
|
||||
{
|
||||
LOGERROR(L"Unrecognized map type '%ls' detected", mapType.c_str());
|
||||
throw PSERROR_Game_World_MapLoadFailed("Unrecognized map type.\nConsult GameSetup.cpp for the currently supported types.");
|
||||
}
|
||||
scriptInterface.SetProperty(attrs.get(), "mapType", mapType);
|
||||
scriptInterface.SetProperty(attrs.get(), "map", std::string("maps/" + autoStartName));
|
||||
|
||||
scriptInterface.SetProperty(settings.get(), "mapType", mapType);
|
||||
|
||||
// Set player data for AIs
|
||||
// attrs.settings = { PlayerData: [ { AI: ... }, ... ] }:
|
||||
|
||||
Reference in New Issue
Block a user