Give RMS access to the simulation templates. Fixes #1589. Patch by Itms.
This was SVN commit r15306.
This commit is contained in:
parent
27ca5fe19d
commit
567917bf41
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012 Wildfire Games.
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -91,6 +91,9 @@ bool CMapGeneratorWorker::Run()
|
||||
m_ScriptInterface->RegisterFunction<void, int, CMapGeneratorWorker::SetProgress>("SetProgress");
|
||||
m_ScriptInterface->RegisterFunction<void, CMapGeneratorWorker::MaybeGC>("MaybeGC");
|
||||
m_ScriptInterface->RegisterFunction<std::vector<std::string>, CMapGeneratorWorker::GetCivData>("GetCivData");
|
||||
m_ScriptInterface->RegisterFunction<CParamNode, std::string, CMapGeneratorWorker::GetTemplate>("GetTemplate");
|
||||
m_ScriptInterface->RegisterFunction<std::vector<std::string>, std::string, bool, CMapGeneratorWorker::FindTemplates>("FindTemplates");
|
||||
m_ScriptInterface->RegisterFunction<std::vector<std::string>, std::string, bool, CMapGeneratorWorker::FindActorTemplates>("FindActorTemplates");
|
||||
|
||||
// TODO: This code is a bit ugly because we have to ensure that CScriptValRooted gets destroyed before the ScriptInterface.
|
||||
// In the future we should work more with the standard JSAPI types for rooting on the stack, which should avoid such problems.
|
||||
@ -220,6 +223,31 @@ std::vector<std::string> CMapGeneratorWorker::GetCivData(ScriptInterface::CxPriv
|
||||
|
||||
}
|
||||
|
||||
CParamNode CMapGeneratorWorker::GetTemplate(ScriptInterface::CxPrivate* pCxPrivate, std::string templateName)
|
||||
{
|
||||
// TODO: Find a way to validate templates outside of the simulation.
|
||||
// This should be implemented in TemplateLoader though.
|
||||
|
||||
CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);
|
||||
const CParamNode& templateRoot = self->m_TemplateLoader.GetTemplateFileData(templateName).GetChild("Entity");
|
||||
if (!templateRoot.IsOk())
|
||||
LOGERROR(L"Invalid template found for '%hs'", templateName.c_str());
|
||||
|
||||
return templateRoot;
|
||||
}
|
||||
|
||||
std::vector<std::string> CMapGeneratorWorker::FindTemplates(ScriptInterface::CxPrivate* pCxPrivate, std::string path, bool includeSubdirectories)
|
||||
{
|
||||
CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);
|
||||
return self->m_TemplateLoader.FindTemplates(path, includeSubdirectories, SIMULATION_TEMPLATES);
|
||||
}
|
||||
|
||||
std::vector<std::string> CMapGeneratorWorker::FindActorTemplates(ScriptInterface::CxPrivate* pCxPrivate, std::string path, bool includeSubdirectories)
|
||||
{
|
||||
CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);
|
||||
return self->m_TemplateLoader.FindTemplates(path, includeSubdirectories, ACTOR_TEMPLATES);
|
||||
}
|
||||
|
||||
bool CMapGeneratorWorker::LoadScripts(const std::wstring& libraryName)
|
||||
{
|
||||
// Ignore libraries that are already loaded
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -20,6 +20,7 @@
|
||||
|
||||
#include "ps/FileIo.h"
|
||||
#include "ps/ThreadUtil.h"
|
||||
#include "ps/TemplateLoader.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
|
||||
#include <boost/random/linear_congruential.hpp>
|
||||
@ -125,6 +126,9 @@ private:
|
||||
static void SetProgress(ScriptInterface::CxPrivate* pCxPrivate, int progress);
|
||||
static void MaybeGC(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
static std::vector<std::string> GetCivData(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
static CParamNode GetTemplate(ScriptInterface::CxPrivate* pCxPrivate, std::string templateName);
|
||||
static std::vector<std::string> FindTemplates(ScriptInterface::CxPrivate* pCxPrivate, std::string path, bool includeSubdirectories);
|
||||
static std::vector<std::string> FindActorTemplates(ScriptInterface::CxPrivate* pCxPrivate, std::string path, bool includeSubdirectories);
|
||||
|
||||
std::set<std::wstring> m_LoadedLibraries;
|
||||
shared_ptr<ScriptInterface::StructuredClone> m_MapData;
|
||||
@ -133,6 +137,7 @@ private:
|
||||
ScriptInterface* m_ScriptInterface;
|
||||
VfsPath m_ScriptPath;
|
||||
std::string m_Settings;
|
||||
CTemplateLoader m_TemplateLoader;
|
||||
|
||||
// Thread
|
||||
static void* RunThread(void* data);
|
||||
|
409
source/ps/TemplateLoader.cpp
Normal file
409
source/ps/TemplateLoader.cpp
Normal file
@ -0,0 +1,409 @@
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "TemplateLoader.h"
|
||||
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/XML/Xeromyces.h"
|
||||
#include "lib/utf8.h"
|
||||
|
||||
static const wchar_t TEMPLATE_ROOT[] = L"simulation/templates/";
|
||||
static const wchar_t ACTOR_ROOT[] = L"art/actors/";
|
||||
|
||||
static CParamNode NULL_NODE(false);
|
||||
|
||||
|
||||
bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int depth)
|
||||
{
|
||||
// If this file was already loaded, we don't need to do anything
|
||||
if (m_TemplateFileData.find(templateName) != m_TemplateFileData.end())
|
||||
return true;
|
||||
|
||||
// Handle infinite loops more gracefully than running out of stack space and crashing
|
||||
if (depth > 100)
|
||||
{
|
||||
LOGERROR(L"Probable infinite inheritance loop in entity template '%hs'", templateName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle special case "actor|foo"
|
||||
if (templateName.find("actor|") == 0)
|
||||
{
|
||||
ConstructTemplateActor(templateName.substr(6), m_TemplateFileData[templateName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "preview|foo"
|
||||
if (templateName.find("preview|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(8);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "corpse|foo"
|
||||
if (templateName.find("corpse|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(7);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "foundation|foo"
|
||||
if (templateName.find("foundation|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(11);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyFoundationSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "construction|foo"
|
||||
if (templateName.find("construction|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(13);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyConstructionSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "resource|foo"
|
||||
if (templateName.find("resource|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(9);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyResourceSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Normal case: templateName is an XML file:
|
||||
|
||||
VfsPath path = VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(templateName + ".xml");
|
||||
CXeromyces xero;
|
||||
PSRETURN ok = xero.Load(g_VFS, path);
|
||||
if (ok != PSRETURN_OK)
|
||||
return false; // (Xeromyces already logged an error with the full filename)
|
||||
|
||||
int attr_parent = xero.GetAttributeID("parent");
|
||||
CStr parentName = xero.GetRoot().GetAttributes().GetNamedItem(attr_parent);
|
||||
if (!parentName.empty())
|
||||
{
|
||||
// To prevent needless complexity in template design, we don't allow |-separated strings as parents
|
||||
if (parentName.find('|') != parentName.npos)
|
||||
{
|
||||
LOGERROR(L"Invalid parent '%hs' in entity template '%hs'", parentName.c_str(), templateName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the parent is loaded
|
||||
if (!LoadTemplateFile(parentName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load parent '%hs' of entity template '%hs'", parentName.c_str(), templateName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
CParamNode& parentData = m_TemplateFileData[parentName];
|
||||
|
||||
// Initialise this template with its parent
|
||||
m_TemplateFileData[templateName] = parentData;
|
||||
}
|
||||
|
||||
// Load the new file into the template data (overriding parent values)
|
||||
CParamNode::LoadXML(m_TemplateFileData[templateName], xero, wstring_from_utf8(templateName).c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static Status AddToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData)
|
||||
{
|
||||
std::vector<std::string>& templates = *(std::vector<std::string>*)cbData;
|
||||
|
||||
// Strip the .xml extension
|
||||
VfsPath pathstem = pathname.ChangeExtension(L"");
|
||||
// Strip the root from the path
|
||||
std::wstring name = pathstem.string().substr(ARRAY_SIZE(TEMPLATE_ROOT)-1);
|
||||
|
||||
// We want to ignore template_*.xml templates, since they should never be built in the editor
|
||||
if (name.substr(0, 9) == L"template_")
|
||||
return INFO::OK;
|
||||
|
||||
templates.push_back(std::string(name.begin(), name.end()));
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
static Status AddActorToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData)
|
||||
{
|
||||
std::vector<std::string>& templates = *(std::vector<std::string>*)cbData;
|
||||
|
||||
// Strip the root from the path
|
||||
std::wstring name = pathname.string().substr(ARRAY_SIZE(ACTOR_ROOT)-1);
|
||||
|
||||
templates.push_back("actor|" + std::string(name.begin(), name.end()));
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
std::vector<std::string> CTemplateLoader::FindTemplates(const std::string& path, bool includeSubdirectories, ETemplatesType templatesType)
|
||||
{
|
||||
std::vector<std::string> templates;
|
||||
|
||||
Status ok;
|
||||
VfsPath templatePath;
|
||||
|
||||
if (templatesType == SIMULATION_TEMPLATES || templatesType == ALL_TEMPLATES)
|
||||
{
|
||||
templatePath = VfsPath(TEMPLATE_ROOT) / path;
|
||||
if (includeSubdirectories)
|
||||
ok = vfs::ForEachFile(g_VFS, templatePath, AddToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE);
|
||||
else
|
||||
ok = vfs::ForEachFile(g_VFS, templatePath, AddToTemplates, (uintptr_t)&templates, L"*.xml");
|
||||
WARN_IF_ERR(ok);
|
||||
}
|
||||
if (templatesType == ACTOR_TEMPLATES || templatesType == ALL_TEMPLATES)
|
||||
{
|
||||
templatePath = VfsPath(ACTOR_ROOT) / path;
|
||||
if (includeSubdirectories)
|
||||
ok = vfs::ForEachFile(g_VFS, templatePath, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE);
|
||||
else
|
||||
ok = vfs::ForEachFile(g_VFS, templatePath, AddActorToTemplates, (uintptr_t)&templates, L"*.xml");
|
||||
WARN_IF_ERR(ok);
|
||||
}
|
||||
|
||||
if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
|
||||
LOGERROR(L"Undefined template type (valid: all, simulation, actor)");
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
const CParamNode& CTemplateLoader::GetTemplateFileData(const std::string& templateName)
|
||||
{
|
||||
// Load the template if necessary
|
||||
if (!LoadTemplateFile(templateName, 0))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", templateName.c_str());
|
||||
return NULL_NODE;
|
||||
}
|
||||
|
||||
return m_TemplateFileData[templateName];
|
||||
}
|
||||
|
||||
void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CParamNode& out)
|
||||
{
|
||||
// Load the base actor template if necessary
|
||||
const char* templateName = "special/actor";
|
||||
if (!LoadTemplateFile(templateName, 0))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", templateName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the actor template
|
||||
out = m_TemplateFileData[templateName];
|
||||
|
||||
// Initialise the actor's name and make it an Atlas selectable entity.
|
||||
std::wstring actorNameW = wstring_from_utf8(actorName);
|
||||
std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(actorNameW));
|
||||
std::string xml = "<Entity>"
|
||||
"<VisualActor><Actor>" + name + "</Actor><ActorOnly/></VisualActor>"
|
||||
// arbitrary-sized Footprint definition to make actors' selection outlines show up in Atlas
|
||||
"<Footprint><Circle radius='2.0'/><Height>1.0</Height></Footprint>"
|
||||
"<Selectable>"
|
||||
"<EditorOnly/>"
|
||||
"<Overlay><Texture><MainTexture>actor.png</MainTexture><MainTextureMask>actor_mask.png</MainTextureMask></Texture></Overlay>"
|
||||
"</Selectable>"
|
||||
"</Entity>";
|
||||
|
||||
CParamNode::LoadXMLString(out, xml.c_str(), actorNameW.c_str());
|
||||
}
|
||||
|
||||
void CTemplateLoader::CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse)
|
||||
{
|
||||
// We only want to include components which are necessary (for the visual previewing of an entity)
|
||||
// and safe (i.e. won't do anything that affects the synchronised simulation state), so additions
|
||||
// to this list should be carefully considered
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Identity");
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
permittedComponentTypes.insert("Footprint");
|
||||
permittedComponentTypes.insert("Obstruction");
|
||||
permittedComponentTypes.insert("Decay");
|
||||
permittedComponentTypes.insert("BuildRestrictions");
|
||||
|
||||
// Need these for the Actor Viewer:
|
||||
permittedComponentTypes.insert("Attack");
|
||||
permittedComponentTypes.insert("UnitMotion");
|
||||
permittedComponentTypes.insert("Sound");
|
||||
|
||||
// (This set could be initialised once and reused, but it's not worth the effort)
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
|
||||
// Disable the Obstruction component (if there is one) so it doesn't affect pathfinding
|
||||
// (but can still be used for testing this entity for collisions against others)
|
||||
if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Obstruction><Active>false</Active></Obstruction></Entity>");
|
||||
|
||||
if (!corpse)
|
||||
{
|
||||
// Previews should not cast shadows
|
||||
if (out.GetChild("Entity").GetChild("VisualActor").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><VisualActor><DisableShadows/></VisualActor></Entity>");
|
||||
|
||||
// Previews should always be visible in fog-of-war/etc
|
||||
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range><RetainInFog>false</RetainInFog><AlwaysVisible>true</AlwaysVisible></Vision></Entity>");
|
||||
}
|
||||
|
||||
if (corpse)
|
||||
{
|
||||
// Corpses should include decay components and un-inactivate them
|
||||
if (out.GetChild("Entity").GetChild("Decay").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Decay><Inactive disable=''/></Decay></Entity>");
|
||||
|
||||
// Corpses shouldn't display silhouettes (especially since they're often half underground)
|
||||
if (out.GetChild("Entity").GetChild("VisualActor").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><VisualActor><SilhouetteDisplay>false</SilhouetteDisplay></VisualActor></Entity>");
|
||||
|
||||
// Corpses should remain visible in fog-of-war
|
||||
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range><RetainInFog>true</RetainInFog><AlwaysVisible>false</AlwaysVisible></Vision></Entity>");
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplateLoader::CopyFoundationSubset(CParamNode& out, const CParamNode& in)
|
||||
{
|
||||
// TODO: this is all kind of yucky and hard-coded; it'd be nice to have a more generic
|
||||
// extensible scriptable way to define these subsets
|
||||
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
permittedComponentTypes.insert("Identity");
|
||||
permittedComponentTypes.insert("BuildRestrictions");
|
||||
permittedComponentTypes.insert("Obstruction");
|
||||
permittedComponentTypes.insert("Selectable");
|
||||
permittedComponentTypes.insert("Footprint");
|
||||
permittedComponentTypes.insert("Armour");
|
||||
permittedComponentTypes.insert("Health");
|
||||
permittedComponentTypes.insert("StatusBars");
|
||||
permittedComponentTypes.insert("OverlayRenderer");
|
||||
permittedComponentTypes.insert("Decay");
|
||||
permittedComponentTypes.insert("Cost");
|
||||
permittedComponentTypes.insert("Sound");
|
||||
permittedComponentTypes.insert("Vision");
|
||||
permittedComponentTypes.insert("AIProxy");
|
||||
permittedComponentTypes.insert("RallyPoint");
|
||||
permittedComponentTypes.insert("RallyPointRenderer");
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
|
||||
// Switch the actor to foundation mode
|
||||
CParamNode::LoadXMLString(out, "<Entity><VisualActor><Foundation/></VisualActor></Entity>");
|
||||
|
||||
// Add the Foundation component, to deal with the construction process
|
||||
CParamNode::LoadXMLString(out, "<Entity><Foundation/></Entity>");
|
||||
|
||||
// Initialise health to 1
|
||||
CParamNode::LoadXMLString(out, "<Entity><Health><Initial>1</Initial></Health></Entity>");
|
||||
|
||||
// Foundations shouldn't initially block unit movement
|
||||
if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Obstruction><DisableBlockMovement>true</DisableBlockMovement><DisableBlockPathfinding>true</DisableBlockPathfinding></Obstruction></Entity>");
|
||||
|
||||
// Don't provide population bonuses yet (but still do take up population cost)
|
||||
if (out.GetChild("Entity").GetChild("Cost").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Cost><PopulationBonus>0</PopulationBonus></Cost></Entity>");
|
||||
|
||||
// Foundations should be visible themselves in fog-of-war if their base template is,
|
||||
// but shouldn't have any vision range
|
||||
if (out.GetChild("Entity").GetChild("Vision").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range></Vision></Entity>");
|
||||
}
|
||||
|
||||
void CTemplateLoader::CopyConstructionSubset(CParamNode& out, const CParamNode& in)
|
||||
{
|
||||
// Currently used for buildings rising during construction
|
||||
// Mostly serves to filter out components like Vision, UnitAI, etc.
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Footprint");
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
}
|
||||
|
||||
void CTemplateLoader::CopyResourceSubset(CParamNode& out, const CParamNode& in)
|
||||
{
|
||||
// Currently used for animals which die and leave a gatherable corpse.
|
||||
// Mostly serves to filter out components like Vision, UnitAI, etc.
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
permittedComponentTypes.insert("Identity");
|
||||
permittedComponentTypes.insert("Obstruction");
|
||||
permittedComponentTypes.insert("Minimap");
|
||||
permittedComponentTypes.insert("ResourceSupply");
|
||||
permittedComponentTypes.insert("Selectable");
|
||||
permittedComponentTypes.insert("Footprint");
|
||||
permittedComponentTypes.insert("StatusBars");
|
||||
permittedComponentTypes.insert("OverlayRenderer");
|
||||
permittedComponentTypes.insert("Sound");
|
||||
permittedComponentTypes.insert("AIProxy");
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
}
|
105
source/ps/TemplateLoader.h
Normal file
105
source/ps/TemplateLoader.h
Normal file
@ -0,0 +1,105 @@
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_TEMPLATELOADER
|
||||
#define INCLUDED_TEMPLATELOADER
|
||||
|
||||
#include "simulation2/system/ParamNode.h"
|
||||
|
||||
enum ETemplatesType
|
||||
{
|
||||
ALL_TEMPLATES,
|
||||
ACTOR_TEMPLATES,
|
||||
SIMULATION_TEMPLATES
|
||||
};
|
||||
|
||||
/**
|
||||
* Template loader: Handles the loading of entity template files for:
|
||||
* - the initialisation and deserialization of entity components in the
|
||||
* simulation (CmpTemplateManager).
|
||||
* - access to actor templates, obstruction data, etc. in RMS/RMGEN
|
||||
*
|
||||
* Template names are intentionally restricted to ASCII strings for storage/serialization
|
||||
* efficiency (we have a lot of strings so this is significant);
|
||||
* they correspond to filenames so they shouldn't contain non-ASCII anyway.
|
||||
*/
|
||||
class CTemplateLoader
|
||||
{
|
||||
public:
|
||||
CTemplateLoader()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the file data for requested template.
|
||||
*/
|
||||
const CParamNode &GetTemplateFileData(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).
|
||||
*/
|
||||
std::vector<std::string> FindTemplates(const std::string& path, bool includeSubdirectories, ETemplatesType templatesType);
|
||||
|
||||
private:
|
||||
/**
|
||||
* (Re)loads the given template, regardless of whether it exists already,
|
||||
* and saves into m_TemplateFileData. Also loads any parents that are not yet
|
||||
* loaded. Returns false on error.
|
||||
* @param templateName XML filename to load (not a |-separated string)
|
||||
*/
|
||||
bool LoadTemplateFile(const std::string& templateName, int depth);
|
||||
|
||||
/**
|
||||
* Constructs a standard static-decorative-object template for the given actor
|
||||
*/
|
||||
void ConstructTemplateActor(const std::string& actorName, CParamNode& out);
|
||||
|
||||
/**
|
||||
* Copy the non-interactive components of an entity template (position, actor, etc) into
|
||||
* a new entity template
|
||||
*/
|
||||
void CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse);
|
||||
|
||||
/**
|
||||
* Copy the components of an entity template necessary for a construction foundation
|
||||
* (position, actor, armour, health, etc) into a new entity template
|
||||
*/
|
||||
void CopyFoundationSubset(CParamNode& out, const CParamNode& in);
|
||||
|
||||
/**
|
||||
* Copy the components of an entity template necessary for a non-foundation construction entity
|
||||
* into a new entity template
|
||||
*/
|
||||
void CopyConstructionSubset(CParamNode& out, const CParamNode& in);
|
||||
|
||||
/**
|
||||
* Copy the components of an entity template necessary for a gatherable resource
|
||||
* into a new entity template
|
||||
*/
|
||||
void CopyResourceSubset(CParamNode& out, const CParamNode& in);
|
||||
|
||||
/**
|
||||
* Map from template name (XML filename or special |-separated string) to the most recently
|
||||
* loaded non-broken template data. This includes files that will fail schema validation.
|
||||
* (Failed loads won't remove existing entries under the same name, so we behave more nicely
|
||||
* when hotloading broken files)
|
||||
*/
|
||||
std::map<std::string, CParamNode> m_TemplateFileData;
|
||||
};
|
||||
|
||||
#endif // INCLUDED_TEMPLATELOADER
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -22,14 +22,11 @@
|
||||
|
||||
#include "simulation2/MessageTypes.h"
|
||||
|
||||
#include "ps/TemplateLoader.h"
|
||||
|
||||
#include "lib/utf8.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/XML/RelaxNG.h"
|
||||
#include "ps/XML/Xeromyces.h"
|
||||
|
||||
static const wchar_t TEMPLATE_ROOT[] = L"simulation/templates/";
|
||||
static const wchar_t ACTOR_ROOT[] = L"art/actors/";
|
||||
|
||||
class CCmpTemplateManager : public ICmpTemplateManager
|
||||
{
|
||||
@ -136,18 +133,15 @@ public:
|
||||
virtual std::vector<entity_id_t> GetEntitiesUsingTemplate(std::string templateName);
|
||||
|
||||
private:
|
||||
// Template loader
|
||||
CTemplateLoader m_templateLoader;
|
||||
|
||||
// Entity template XML validator
|
||||
RelaxNGValidator m_Validator;
|
||||
|
||||
// Disable validation, for test cases
|
||||
bool m_DisableValidation;
|
||||
|
||||
// Map from template name (XML filename or special |-separated string) to the most recently
|
||||
// loaded non-broken template data. This includes files that will fail schema validation.
|
||||
// (Failed loads won't remove existing entries under the same name, so we behave more nicely
|
||||
// when hotloading broken files)
|
||||
std::map<std::string, CParamNode> m_TemplateFileData;
|
||||
|
||||
// Map from template name to schema validation status.
|
||||
// (Some files, e.g. inherited parent templates, may not be valid themselves but we still need to load
|
||||
// them and use them; we only reject invalid templates that were requested directly by GetTemplate/etc)
|
||||
@ -157,31 +151,6 @@ private:
|
||||
// again for deserialization.
|
||||
// TODO: should store player ID etc.
|
||||
std::map<entity_id_t, std::string> m_LatestTemplates;
|
||||
|
||||
// (Re)loads the given template, regardless of whether it exists already,
|
||||
// and saves into m_TemplateFileData. Also loads any parents that are not yet
|
||||
// loaded. Returns false on error.
|
||||
// @param templateName XML filename to load (not a |-separated string)
|
||||
bool LoadTemplateFile(const std::string& templateName, int depth);
|
||||
|
||||
// Constructs a standard static-decorative-object template for the given actor
|
||||
void ConstructTemplateActor(const std::string& actorName, CParamNode& out);
|
||||
|
||||
// Copy the non-interactive components of an entity template (position, actor, etc) into
|
||||
// a new entity template
|
||||
void CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse);
|
||||
|
||||
// Copy the components of an entity necessary for a construction foundation
|
||||
// (position, actor, armour, health, etc) into a new entity template
|
||||
void CopyFoundationSubset(CParamNode& out, const CParamNode& in);
|
||||
|
||||
// Copy the components of an entity necessary for a non-foundation construction entity
|
||||
// into a new entity template
|
||||
void CopyConstructionSubset(CParamNode& out, const CParamNode& in);
|
||||
|
||||
// Copy the components of an entity necessary for a gatherable resource
|
||||
// into a new entity template
|
||||
void CopyResourceSubset(CParamNode& out, const CParamNode& in);
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_TYPE(TemplateManager)
|
||||
@ -201,19 +170,16 @@ const CParamNode* CCmpTemplateManager::LoadTemplate(entity_id_t ent, const std::
|
||||
|
||||
const CParamNode* CCmpTemplateManager::GetTemplate(std::string templateName)
|
||||
{
|
||||
// Load the template if necessary
|
||||
if (!LoadTemplateFile(templateName, 0))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", templateName.c_str());
|
||||
const CParamNode& fileData = m_templateLoader.GetTemplateFileData(templateName);
|
||||
if (!fileData.IsOk())
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!m_DisableValidation)
|
||||
{
|
||||
// Compute validity, if it's not computed before
|
||||
if (m_TemplateSchemaValidity.find(templateName) == m_TemplateSchemaValidity.end())
|
||||
{
|
||||
m_TemplateSchemaValidity[templateName] = m_Validator.Validate(wstring_from_utf8(templateName), m_TemplateFileData[templateName].ToXML());
|
||||
m_TemplateSchemaValidity[templateName] = m_Validator.Validate(wstring_from_utf8(templateName), fileData.ToXML());
|
||||
|
||||
// Show error on the first failure to validate the template
|
||||
if (!m_TemplateSchemaValidity[templateName])
|
||||
@ -224,7 +190,7 @@ const CParamNode* CCmpTemplateManager::GetTemplate(std::string templateName)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const CParamNode& templateRoot = m_TemplateFileData[templateName].GetChild("Entity");
|
||||
const CParamNode& templateRoot = fileData.GetChild("Entity");
|
||||
if (!templateRoot.IsOk())
|
||||
{
|
||||
// The validator should never let this happen
|
||||
@ -237,14 +203,7 @@ const CParamNode* CCmpTemplateManager::GetTemplate(std::string templateName)
|
||||
|
||||
const CParamNode* CCmpTemplateManager::GetTemplateWithoutValidation(std::string templateName)
|
||||
{
|
||||
// Load the template if necessary
|
||||
if (!LoadTemplateFile(templateName, 0))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", templateName.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const CParamNode& templateRoot = m_TemplateFileData[templateName].GetChild("Entity");
|
||||
const CParamNode& templateRoot = m_templateLoader.GetTemplateFileData(templateName).GetChild("Entity");
|
||||
if (!templateRoot.IsOk())
|
||||
return NULL;
|
||||
|
||||
@ -267,218 +226,10 @@ std::string CCmpTemplateManager::GetCurrentTemplateName(entity_id_t ent)
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool CCmpTemplateManager::LoadTemplateFile(const std::string& templateName, int depth)
|
||||
{
|
||||
// If this file was already loaded, we don't need to do anything
|
||||
if (m_TemplateFileData.find(templateName) != m_TemplateFileData.end())
|
||||
return true;
|
||||
|
||||
// Handle infinite loops more gracefully than running out of stack space and crashing
|
||||
if (depth > 100)
|
||||
{
|
||||
LOGERROR(L"Probable infinite inheritance loop in entity template '%hs'", templateName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle special case "actor|foo"
|
||||
if (templateName.find("actor|") == 0)
|
||||
{
|
||||
ConstructTemplateActor(templateName.substr(6), m_TemplateFileData[templateName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "preview|foo"
|
||||
if (templateName.find("preview|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(8);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "corpse|foo"
|
||||
if (templateName.find("corpse|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(7);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "foundation|foo"
|
||||
if (templateName.find("foundation|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(11);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyFoundationSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "construction|foo"
|
||||
if (templateName.find("construction|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(13);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyConstructionSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle special case "resource|foo"
|
||||
if (templateName.find("resource|") == 0)
|
||||
{
|
||||
// Load the base entity template, if it wasn't already loaded
|
||||
std::string baseName = templateName.substr(9);
|
||||
if (!LoadTemplateFile(baseName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
|
||||
return false;
|
||||
}
|
||||
// Copy a subset to the requested template
|
||||
CopyResourceSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Normal case: templateName is an XML file:
|
||||
|
||||
VfsPath path = VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(templateName + ".xml");
|
||||
CXeromyces xero;
|
||||
PSRETURN ok = xero.Load(g_VFS, path);
|
||||
if (ok != PSRETURN_OK)
|
||||
return false; // (Xeromyces already logged an error with the full filename)
|
||||
|
||||
int attr_parent = xero.GetAttributeID("parent");
|
||||
CStr parentName = xero.GetRoot().GetAttributes().GetNamedItem(attr_parent);
|
||||
if (!parentName.empty())
|
||||
{
|
||||
// To prevent needless complexity in template design, we don't allow |-separated strings as parents
|
||||
if (parentName.find('|') != parentName.npos)
|
||||
{
|
||||
LOGERROR(L"Invalid parent '%hs' in entity template '%hs'", parentName.c_str(), templateName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the parent is loaded
|
||||
if (!LoadTemplateFile(parentName, depth+1))
|
||||
{
|
||||
LOGERROR(L"Failed to load parent '%hs' of entity template '%hs'", parentName.c_str(), templateName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
CParamNode& parentData = m_TemplateFileData[parentName];
|
||||
|
||||
// Initialise this template with its parent
|
||||
m_TemplateFileData[templateName] = parentData;
|
||||
}
|
||||
|
||||
// Load the new file into the template data (overriding parent values)
|
||||
CParamNode::LoadXML(m_TemplateFileData[templateName], xero, wstring_from_utf8(templateName).c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CCmpTemplateManager::ConstructTemplateActor(const std::string& actorName, CParamNode& out)
|
||||
{
|
||||
// Load the base actor template if necessary
|
||||
const char* templateName = "special/actor";
|
||||
if (!LoadTemplateFile(templateName, 0))
|
||||
{
|
||||
LOGERROR(L"Failed to load entity template '%hs'", templateName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the actor template
|
||||
out = m_TemplateFileData[templateName];
|
||||
|
||||
// Initialise the actor's name and make it an Atlas selectable entity.
|
||||
std::wstring actorNameW = wstring_from_utf8(actorName);
|
||||
std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(actorNameW));
|
||||
std::string xml = "<Entity>"
|
||||
"<VisualActor><Actor>" + name + "</Actor><ActorOnly/></VisualActor>"
|
||||
// arbitrary-sized Footprint definition to make actors' selection outlines show up in Atlas
|
||||
"<Footprint><Circle radius='2.0'/><Height>1.0</Height></Footprint>"
|
||||
"<Selectable>"
|
||||
"<EditorOnly/>"
|
||||
"<Overlay><Texture><MainTexture>actor.png</MainTexture><MainTextureMask>actor_mask.png</MainTextureMask></Texture></Overlay>"
|
||||
"</Selectable>"
|
||||
"</Entity>";
|
||||
|
||||
CParamNode::LoadXMLString(out, xml.c_str(), actorNameW.c_str());
|
||||
}
|
||||
|
||||
static Status AddToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData)
|
||||
{
|
||||
std::vector<std::string>& templates = *(std::vector<std::string>*)cbData;
|
||||
|
||||
// Strip the .xml extension
|
||||
VfsPath pathstem = pathname.ChangeExtension(L"");
|
||||
// Strip the root from the path
|
||||
std::wstring name = pathstem.string().substr(ARRAY_SIZE(TEMPLATE_ROOT)-1);
|
||||
|
||||
// We want to ignore template_*.xml templates, since they should never be built in the editor
|
||||
if (name.substr(0, 9) == L"template_")
|
||||
return INFO::OK;
|
||||
|
||||
templates.push_back(std::string(name.begin(), name.end()));
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
static Status AddActorToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData)
|
||||
{
|
||||
std::vector<std::string>& templates = *(std::vector<std::string>*)cbData;
|
||||
|
||||
// Strip the root from the path
|
||||
std::wstring name = pathname.string().substr(ARRAY_SIZE(ACTOR_ROOT)-1);
|
||||
|
||||
templates.push_back("actor|" + std::string(name.begin(), name.end()));
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
std::vector<std::string> CCmpTemplateManager::FindAllTemplates(bool includeActors)
|
||||
{
|
||||
// TODO: eventually this should probably read all the template files and look for flags to
|
||||
// determine which should be displayed in the editor (and in what categories etc); for now we'll
|
||||
// just return all the files
|
||||
|
||||
std::vector<std::string> templates;
|
||||
|
||||
Status ok;
|
||||
|
||||
// Find all the normal entity templates first
|
||||
ok = vfs::ForEachFile(g_VFS, TEMPLATE_ROOT, AddToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE);
|
||||
WARN_IF_ERR(ok);
|
||||
|
||||
if (includeActors)
|
||||
{
|
||||
// Add all the actors too
|
||||
ok = vfs::ForEachFile(g_VFS, ACTOR_ROOT, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE);
|
||||
WARN_IF_ERR(ok);
|
||||
}
|
||||
|
||||
return templates;
|
||||
ETemplatesType templatesType = includeActors ? ALL_TEMPLATES : SIMULATION_TEMPLATES;
|
||||
return m_templateLoader.FindTemplates("", true, templatesType);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -494,148 +245,3 @@ std::vector<entity_id_t> CCmpTemplateManager::GetEntitiesUsingTemplate(std::stri
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
void CCmpTemplateManager::CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse)
|
||||
{
|
||||
// We only want to include components which are necessary (for the visual previewing of an entity)
|
||||
// and safe (i.e. won't do anything that affects the synchronised simulation state), so additions
|
||||
// to this list should be carefully considered
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Identity");
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
permittedComponentTypes.insert("Footprint");
|
||||
permittedComponentTypes.insert("Obstruction");
|
||||
permittedComponentTypes.insert("Decay");
|
||||
permittedComponentTypes.insert("BuildRestrictions");
|
||||
|
||||
// Need these for the Actor Viewer:
|
||||
permittedComponentTypes.insert("Attack");
|
||||
permittedComponentTypes.insert("UnitMotion");
|
||||
permittedComponentTypes.insert("Sound");
|
||||
|
||||
// (This set could be initialised once and reused, but it's not worth the effort)
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
|
||||
// Disable the Obstruction component (if there is one) so it doesn't affect pathfinding
|
||||
// (but can still be used for testing this entity for collisions against others)
|
||||
if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Obstruction><Active>false</Active></Obstruction></Entity>");
|
||||
|
||||
if (!corpse)
|
||||
{
|
||||
// Previews should not cast shadows
|
||||
if (out.GetChild("Entity").GetChild("VisualActor").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><VisualActor><DisableShadows/></VisualActor></Entity>");
|
||||
|
||||
// Previews should always be visible in fog-of-war/etc
|
||||
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range><RetainInFog>false</RetainInFog><AlwaysVisible>true</AlwaysVisible></Vision></Entity>");
|
||||
}
|
||||
|
||||
if (corpse)
|
||||
{
|
||||
// Corpses should include decay components and un-inactivate them
|
||||
if (out.GetChild("Entity").GetChild("Decay").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Decay><Inactive disable=''/></Decay></Entity>");
|
||||
|
||||
// Corpses shouldn't display silhouettes (especially since they're often half underground)
|
||||
if (out.GetChild("Entity").GetChild("VisualActor").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><VisualActor><SilhouetteDisplay>false</SilhouetteDisplay></VisualActor></Entity>");
|
||||
|
||||
// Corpses should remain visible in fog-of-war
|
||||
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range><RetainInFog>true</RetainInFog><AlwaysVisible>false</AlwaysVisible></Vision></Entity>");
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpTemplateManager::CopyFoundationSubset(CParamNode& out, const CParamNode& in)
|
||||
{
|
||||
// TODO: this is all kind of yucky and hard-coded; it'd be nice to have a more generic
|
||||
// extensible scriptable way to define these subsets
|
||||
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
permittedComponentTypes.insert("Identity");
|
||||
permittedComponentTypes.insert("BuildRestrictions");
|
||||
permittedComponentTypes.insert("Obstruction");
|
||||
permittedComponentTypes.insert("Selectable");
|
||||
permittedComponentTypes.insert("Footprint");
|
||||
permittedComponentTypes.insert("Armour");
|
||||
permittedComponentTypes.insert("Health");
|
||||
permittedComponentTypes.insert("StatusBars");
|
||||
permittedComponentTypes.insert("OverlayRenderer");
|
||||
permittedComponentTypes.insert("Decay");
|
||||
permittedComponentTypes.insert("Cost");
|
||||
permittedComponentTypes.insert("Sound");
|
||||
permittedComponentTypes.insert("Vision");
|
||||
permittedComponentTypes.insert("AIProxy");
|
||||
permittedComponentTypes.insert("RallyPoint");
|
||||
permittedComponentTypes.insert("RallyPointRenderer");
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
|
||||
// Switch the actor to foundation mode
|
||||
CParamNode::LoadXMLString(out, "<Entity><VisualActor><Foundation/></VisualActor></Entity>");
|
||||
|
||||
// Add the Foundation component, to deal with the construction process
|
||||
CParamNode::LoadXMLString(out, "<Entity><Foundation/></Entity>");
|
||||
|
||||
// Initialise health to 1
|
||||
CParamNode::LoadXMLString(out, "<Entity><Health><Initial>1</Initial></Health></Entity>");
|
||||
|
||||
// Foundations shouldn't initially block unit movement
|
||||
if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Obstruction><DisableBlockMovement>true</DisableBlockMovement><DisableBlockPathfinding>true</DisableBlockPathfinding></Obstruction></Entity>");
|
||||
|
||||
// Don't provide population bonuses yet (but still do take up population cost)
|
||||
if (out.GetChild("Entity").GetChild("Cost").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Cost><PopulationBonus>0</PopulationBonus></Cost></Entity>");
|
||||
|
||||
// Foundations should be visible themselves in fog-of-war if their base template is,
|
||||
// but shouldn't have any vision range
|
||||
if (out.GetChild("Entity").GetChild("Vision").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range></Vision></Entity>");
|
||||
}
|
||||
|
||||
void CCmpTemplateManager::CopyConstructionSubset(CParamNode& out, const CParamNode& in)
|
||||
{
|
||||
// Currently used for buildings rising during construction
|
||||
// Mostly serves to filter out components like Vision, UnitAI, etc.
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Footprint");
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
}
|
||||
|
||||
|
||||
void CCmpTemplateManager::CopyResourceSubset(CParamNode& out, const CParamNode& in)
|
||||
{
|
||||
// Currently used for animals which die and leave a gatherable corpse.
|
||||
// Mostly serves to filter out components like Vision, UnitAI, etc.
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
permittedComponentTypes.insert("Identity");
|
||||
permittedComponentTypes.insert("Obstruction");
|
||||
permittedComponentTypes.insert("Minimap");
|
||||
permittedComponentTypes.insert("ResourceSupply");
|
||||
permittedComponentTypes.insert("Selectable");
|
||||
permittedComponentTypes.insert("Footprint");
|
||||
permittedComponentTypes.insert("StatusBars");
|
||||
permittedComponentTypes.insert("OverlayRenderer");
|
||||
permittedComponentTypes.insert("Sound");
|
||||
permittedComponentTypes.insert("AIProxy");
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user