diff --git a/binaries/data/mods/public/simulation/components/PlayerManager.js b/binaries/data/mods/public/simulation/components/PlayerManager.js index 0a706040a0..0c05cc43bf 100644 --- a/binaries/data/mods/public/simulation/components/PlayerManager.js +++ b/binaries/data/mods/public/simulation/components/PlayerManager.js @@ -31,8 +31,7 @@ PlayerManager.prototype.AddPlayer = function(ent) /** * To avoid possible problems with cached quantities (as in TechnologyManager), * we first remove all entities from this player, and add them back after the replacement. - * Futhermore, because of the Engine.FlushDestroyedComponents, it should only be called during setup/init - * and not during the game + * Note: This should only be called during setup/init and not during the game */ PlayerManager.prototype.ReplacePlayer = function(id, ent) { diff --git a/binaries/data/mods/public/simulation/helpers/InitGame.js b/binaries/data/mods/public/simulation/helpers/InitGame.js index 7a1e70b799..e80f2bd9e8 100644 --- a/binaries/data/mods/public/simulation/helpers/InitGame.js +++ b/binaries/data/mods/public/simulation/helpers/InitGame.js @@ -24,21 +24,21 @@ function InitGame(settings) let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); if (settings.ExploreMap) - for (var i = 0; i < settings.PlayerData.length; i++) - cmpRangeManager.ExploreAllTiles(i+1); + for (let i = 1; i < settings.PlayerData.length; ++i) + cmpRangeManager.ExploreAllTiles(i); else // Explore the map only inside the players' territory borders cmpRangeManager.ExploreTerritories(); let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); let cmpAIManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIManager); - for (let i = 0; i < settings.PlayerData.length; ++i) + for (let i = 1; i < settings.PlayerData.length; ++i) { - let cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(i+1), IID_Player); + let cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(i), IID_Player); cmpPlayer.SetCheatsEnabled(!!settings.CheatsEnabled); if (settings.PlayerData[i] && settings.PlayerData[i].AI && settings.PlayerData[i].AI != "") { - cmpAIManager.AddPlayer(settings.PlayerData[i].AI, i+1, +settings.PlayerData[i].AIDiff); + cmpAIManager.AddPlayer(settings.PlayerData[i].AI, i, +settings.PlayerData[i].AIDiff); cmpPlayer.SetAI(true); // Sandbox: 50%, very easy: 50%, easy: 66%, Medium: 100%, hard: 133%, very hard: 166% cmpPlayer.SetGatherRateMultiplier(Math.max(0.5,(+settings.PlayerData[i].AIDiff)/3.0)); diff --git a/binaries/data/mods/public/simulation/helpers/Player.js b/binaries/data/mods/public/simulation/helpers/Player.js index 2a6077885c..f57ab8bfde 100644 --- a/binaries/data/mods/public/simulation/helpers/Player.js +++ b/binaries/data/mods/public/simulation/helpers/Player.js @@ -19,43 +19,35 @@ function LoadPlayerSettings(settings, newPlayers) if (!(rawData && rawData.PlayerData)) throw("Player.js: Error reading player_defaults.json"); + // Add gaia to simplify iteration + if (settings.PlayerData) + settings.PlayerData.unshift({}); + var playerDefaults = rawData.PlayerData; + var playerData = settings.PlayerData; // Get player manager var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); var numPlayers = cmpPlayerManager.GetNumPlayers(); var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); - var templatesList = cmpTemplateManager.FindAllTemplates(false); - var previousPlayers = numPlayers; // Remove existing players or add new ones if (newPlayers) { var settingsNumPlayers = 9; // default 8 players + gaia - if (settings.PlayerData) - settingsNumPlayers = settings.PlayerData.length + 1; // including gaia + if (playerData) + settingsNumPlayers = playerData.length; // includes gaia (see above) else warn("Player.js: Setup has no player data - using defaults"); - previousPlayers = Math.min(numPlayers, settingsNumPlayers); - while (settingsNumPlayers > numPlayers) { // Add player entity to engine - var playerTemplate = "special/player"; - if (numPlayers > 0) - { - var pDefs = playerDefaults ? playerDefaults[numPlayers] : {}; - var pData = settings.PlayerData ? settings.PlayerData[numPlayers-1] : {}; - var civ = getSetting(pData, pDefs, "Civ"); - } - else - var civ = "gaia"; - if (templatesList.indexOf("special/"+civ+"_player") !== -1) - playerTemplate = "special/"+civ+"_player"; - var entID = Engine.AddEntity(playerTemplate); + var civ = getSetting(playerData, playerDefaults, i, "Civ"); + var template = cmpTemplateManager.TemplateExists("special/"+civ+"_player") ? "special/"+civ+"_player" : "special/player"; + var entID = Engine.AddEntity(template); var cmpPlayer = Engine.QueryInterface(entID, IID_Player); if (!cmpPlayer) throw("Player.js: Error creating player entity " + numPlayers); @@ -72,19 +64,15 @@ function LoadPlayerSettings(settings, newPlayers) } // Even when no new player, we must check the template compatibility as player template may be civ dependent - for (var i = 1; i < previousPlayers; ++i) + for (var i = 0; i < numPlayers; ++i) { - var pDefs = playerDefaults ? playerDefaults[i] : {}; - var pData = settings.PlayerData ? settings.PlayerData[i-1] : {}; - var civ = getSetting(pData, pDefs, "Civ"); - var neededTemplate = "special/player"; - if (templatesList.indexOf("special/"+civ+"_player") !== -1) - neededTemplate = "special/"+civ+"_player"; + var civ = getSetting(playerData, playerDefaults, i, "Civ"); + var template = cmpTemplateManager.TemplateExists("special/"+civ+"_player") ? "special/"+civ+"_player" : "special/player"; var entID = cmpPlayerManager.GetPlayerByID(i); - if (cmpTemplateManager.GetCurrentTemplateName(entID) === neededTemplate) + if (cmpTemplateManager.GetCurrentTemplateName(entID) === template) continue; // We need to recreate this player to have the right template - entID = Engine.AddEntity(neededTemplate); + entID = Engine.AddEntity(template); cmpPlayerManager.ReplacePlayer(i, entID); } @@ -92,79 +80,70 @@ function LoadPlayerSettings(settings, newPlayers) for (var i = 0; i < numPlayers; ++i) { var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(i), IID_Player); - var pDefs = playerDefaults ? playerDefaults[i] : {}; + cmpPlayer.SetName(getSetting(playerData, playerDefaults, i, "Name")); + cmpPlayer.SetCiv(getSetting(playerData, playerDefaults, i, "Civ")); + var colour = getSetting(playerData, playerDefaults, i, "Colour"); + cmpPlayer.SetColour(colour.r, colour.g, colour.b); - // Skip gaia - if (i > 0) + // Special case for gaia + if (i == 0) { - var pData = settings.PlayerData ? settings.PlayerData[i-1] : {}; - - cmpPlayer.SetName(getSetting(pData, pDefs, "Name")); - cmpPlayer.SetCiv(getSetting(pData, pDefs, "Civ")); - var colour = getSetting(pData, pDefs, "Colour"); - cmpPlayer.SetColour(colour.r, colour.g, colour.b); - - // Note: this is not yet implemented but I leave it commented to highlight it's easy - // If anyone ever adds handicap. - //if (getSetting(pData, pDefs, "GatherRateMultiplier") !== undefined) - // cmpPlayer.SetGatherRateMultiplier(getSetting(pData, pDefs, "GatherRateMultiplier")); - - if (getSetting(pData, pDefs, "PopulationLimit") !== undefined) - cmpPlayer.SetMaxPopulation(getSetting(pData, pDefs, "PopulationLimit")); - - if (getSetting(pData, pDefs, "Resources") !== undefined) - cmpPlayer.SetResourceCounts(getSetting(pData, pDefs, "Resources")); - - // If diplomacy explicitly defined, use that; otherwise use teams - if (getSetting(pData, pDefs, "Diplomacy") !== undefined) - cmpPlayer.SetDiplomacy(getSetting(pData, pDefs, "Diplomacy")); - else - { - // Init diplomacy - var myTeam = getSetting(pData, pDefs, "Team"); - - // Set all but self as enemies as SetTeam takes care of allies - for (var j = 0; j < numPlayers; ++j) - { - if (i == j) - cmpPlayer.SetAlly(j); - else - cmpPlayer.SetEnemy(j); - } - cmpPlayer.SetTeam((myTeam !== undefined) ? myTeam : -1); - } - - // If formations explicitly defined, use that; otherwise use civ defaults - var formations = getSetting(pData, pDefs, "Formations"); - if (formations !== undefined) - cmpPlayer.SetFormations(formations); - else - { - var rawFormations = Engine.ReadCivJSONFile(cmpPlayer.GetCiv()+".json"); - if (!(rawFormations && rawFormations.Formations)) - throw("Player.js: Error reading "+cmpPlayer.GetCiv()+".json"); - - cmpPlayer.SetFormations(rawFormations.Formations); - } - - var startCam = getSetting(pData, pDefs, "StartingCamera"); - if (startCam !== undefined) - cmpPlayer.SetStartingCamera(startCam.Position, startCam.Rotation); - } - else - { - // Copy gaia data from defaults - cmpPlayer.SetName(pDefs.Name); - cmpPlayer.SetCiv(pDefs.Civ); - cmpPlayer.SetColour(pDefs.Colour.r, pDefs.Colour.g, pDefs.Colour.b); - // Gaia should be its own ally. cmpPlayer.SetAlly(0); // Gaia is everyone's enemy for (var j = 1; j < numPlayers; ++j) cmpPlayer.SetEnemy(j); + + continue; } + + // Note: this is not yet implemented but I leave it commented to highlight it's easy + // If anyone ever adds handicap. + //if (getSetting(playerData, playerDefaults, i, "GatherRateMultiplier") !== undefined) + // cmpPlayer.SetGatherRateMultiplier(getSetting(playerData, playerDefaults, i, "GatherRateMultiplier")); + + if (getSetting(playerData, playerDefaults, i, "PopulationLimit") !== undefined) + cmpPlayer.SetMaxPopulation(getSetting(playerData, playerDefaults, i, "PopulationLimit")); + + if (getSetting(playerData, playerDefaults, i, "Resources") !== undefined) + cmpPlayer.SetResourceCounts(getSetting(playerData, playerDefaults, i, "Resources")); + + // If diplomacy explicitly defined, use that; otherwise use teams + if (getSetting(playerData, playerDefaults, i, "Diplomacy") !== undefined) + cmpPlayer.SetDiplomacy(getSetting(playerData, playerDefaults, i, "Diplomacy")); + else + { + // Init diplomacy + var myTeam = getSetting(playerData, playerDefaults, i, "Team"); + + // Set all but self as enemies as SetTeam takes care of allies + for (var j = 0; j < numPlayers; ++j) + { + if (i == j) + cmpPlayer.SetAlly(j); + else + cmpPlayer.SetEnemy(j); + } + cmpPlayer.SetTeam((myTeam !== undefined) ? myTeam : -1); + } + + // If formations explicitly defined, use that; otherwise use civ defaults + var formations = getSetting(playerData, playerDefaults, i, "Formations"); + if (formations !== undefined) + cmpPlayer.SetFormations(formations); + else + { + var rawFormations = Engine.ReadCivJSONFile(cmpPlayer.GetCiv()+".json"); + if (!(rawFormations && rawFormations.Formations)) + throw("Player.js: Error reading "+cmpPlayer.GetCiv()+".json"); + + cmpPlayer.SetFormations(rawFormations.Formations); + } + + var startCam = getSetting(playerData, playerDefaults, i, "StartingCamera"); + if (startCam !== undefined) + cmpPlayer.SetStartingCamera(startCam.Position, startCam.Rotation); } // NOTE: We need to do the team locking here, as otherwise @@ -178,14 +157,14 @@ function LoadPlayerSettings(settings, newPlayers) } // Get a setting if it exists or return default -function getSetting(settings, defaults, property) +function getSetting(settings, defaults, idx, property) { - if (settings && (property in settings)) - return settings[property]; + if (settings && settings[idx] && (property in settings[idx])) + return settings[idx][property]; // Use defaults - if (defaults && (property in defaults)) - return defaults[property]; + if (defaults && defaults[idx] && (property in defaults[idx])) + return defaults[idx][property]; return undefined; } diff --git a/source/ps/TemplateLoader.cpp b/source/ps/TemplateLoader.cpp index d692f6799d..d2e8345849 100644 --- a/source/ps/TemplateLoader.cpp +++ b/source/ps/TemplateLoader.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -206,6 +206,13 @@ static Status AddActorToTemplates(const VfsPath& pathname, const CFileInfo& UNUS return INFO::OK; } +bool CTemplateLoader::TemplateExists(const std::string& templateName) +{ + size_t pos = templateName.rfind('|'); + std::string baseName(pos != std::string::npos ? templateName.substr(pos+1) : templateName); + return VfsFileExists(VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(baseName + ".xml")); +} + std::vector CTemplateLoader::FindPlaceableTemplates(const std::string& path, bool includeSubdirectories, ETemplatesType templatesType, ScriptInterface& scriptInterface) { JSContext* cx = scriptInterface.GetContext(); diff --git a/source/ps/TemplateLoader.h b/source/ps/TemplateLoader.h index 7f471aaa6b..816c4f1964 100644 --- a/source/ps/TemplateLoader.h +++ b/source/ps/TemplateLoader.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -54,6 +54,11 @@ public: */ const CParamNode& GetTemplateFileData(const std::string& templateName); + /** + * Check if the template XML file exits, without trying to load it. + */ + bool TemplateExists(const std::string& templateName); + /** * Returns a list of strings that could be validly passed as @c templateName to LoadTemplateFile. * (This includes "actor|foo" etc names). diff --git a/source/simulation2/components/CCmpTemplateManager.cpp b/source/simulation2/components/CCmpTemplateManager.cpp index dcf721ce39..ffb3cd82ce 100644 --- a/source/simulation2/components/CCmpTemplateManager.cpp +++ b/source/simulation2/components/CCmpTemplateManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -124,6 +124,8 @@ public: virtual const CParamNode* GetTemplateWithoutValidation(std::string templateName); + virtual bool TemplateExists(std::string templateName); + virtual const CParamNode* LoadLatestTemplate(entity_id_t ent); virtual std::string GetCurrentTemplateName(entity_id_t ent); @@ -212,6 +214,11 @@ const CParamNode* CCmpTemplateManager::GetTemplateWithoutValidation(std::string return &templateRoot; } +bool CCmpTemplateManager::TemplateExists(std::string templateName) +{ + return m_templateLoader.TemplateExists(templateName); +} + const CParamNode* CCmpTemplateManager::LoadLatestTemplate(entity_id_t ent) { std::map::const_iterator it = m_LatestTemplates.find(ent); diff --git a/source/simulation2/components/ICmpTemplateManager.cpp b/source/simulation2/components/ICmpTemplateManager.cpp index c0af33ac8f..b2b343cf3e 100644 --- a/source/simulation2/components/ICmpTemplateManager.cpp +++ b/source/simulation2/components/ICmpTemplateManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2010 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -24,6 +24,7 @@ BEGIN_INTERFACE_WRAPPER(TemplateManager) DEFINE_INTERFACE_METHOD_1("GetTemplate", const CParamNode*, ICmpTemplateManager, GetTemplate, std::string) DEFINE_INTERFACE_METHOD_1("GetTemplateWithoutValidation", const CParamNode*, ICmpTemplateManager, GetTemplateWithoutValidation, std::string) +DEFINE_INTERFACE_METHOD_1("TemplateExists", bool, ICmpTemplateManager, TemplateExists, std::string) DEFINE_INTERFACE_METHOD_1("GetCurrentTemplateName", std::string, ICmpTemplateManager, GetCurrentTemplateName, entity_id_t) DEFINE_INTERFACE_METHOD_1("FindAllTemplates", std::vector, ICmpTemplateManager, FindAllTemplates, bool) DEFINE_INTERFACE_METHOD_1("GetEntitiesUsingTemplate", std::vector, ICmpTemplateManager, GetEntitiesUsingTemplate, std::string) diff --git a/source/simulation2/components/ICmpTemplateManager.h b/source/simulation2/components/ICmpTemplateManager.h index 8830adbfb8..999fb272bb 100644 --- a/source/simulation2/components/ICmpTemplateManager.h +++ b/source/simulation2/components/ICmpTemplateManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -73,6 +73,11 @@ public: */ virtual const CParamNode* GetTemplateWithoutValidation(std::string templateName) = 0; + /** + * Check if the template XML file exists, without trying to load it. + */ + virtual bool TemplateExists(std::string templateName) = 0; + /** * Returns the template most recently specified for the entity 'ent'. * Used during deserialization. diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp index 1d9aa52f46..e3599545ed 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -668,7 +668,7 @@ void PlayerSettingsControl::ReadFromEngine() m_NumPlayers = MAX_NUM_PLAYERS; } else - m_NumPlayers = player.count(); + m_NumPlayers = player.count() - 1; // skip gaia wxASSERT(m_NumPlayers <= MAX_NUM_PLAYERS && m_NumPlayers != 0);