diff --git a/binaries/data/mods/public/simulation/templates/special_filter/construction.xml b/binaries/data/mods/public/simulation/templates/special_filter/construction.xml
new file mode 100644
index 0000000..c7f7da7
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special_filter/construction.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special_filter/corpse.xml b/binaries/data/mods/public/simulation/templates/special_filter/corpse.xml
new file mode 100644
index 0000000..c1e20b5
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special_filter/corpse.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+ true
+
+
+
+ false
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special_filter/foundation.xml b/binaries/data/mods/public/simulation/templates/special_filter/foundation.xml
new file mode 100644
index 0000000..99b0e87
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special_filter/foundation.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ false
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special_filter/mirage.xml b/binaries/data/mods/public/simulation/templates/special_filter/mirage.xml
new file mode 100644
index 0000000..8f86013
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special_filter/mirage.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special_filter/preview.xml b/binaries/data/mods/public/simulation/templates/special_filter/preview.xml
new file mode 100644
index 0000000..0be3e4c
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special_filter/preview.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+ true
+ true
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/simulation/templates/special_filter/resource.xml b/binaries/data/mods/public/simulation/templates/special_filter/resource.xml
new file mode 100644
index 0000000..ee62748
--- /dev/null
+++ b/binaries/data/mods/public/simulation/templates/special_filter/resource.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
diff --git a/source/ps/TemplateLoader.cpp b/source/ps/TemplateLoader.cpp
index b9f7606..38edbe1 100644
--- a/source/ps/TemplateLoader.cpp
+++ b/source/ps/TemplateLoader.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2015 Wildfire Games.
+/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -29,7 +29,6 @@ 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
@@ -50,93 +49,33 @@ bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int dept
return true;
}
- // Handle special case "preview|foo"
- if (templateName.find("preview|") == 0)
+ // Handle special case "bar|foo"
+ size_t pos = templateName.find_first_of('|');
+ if (pos != std::string::npos)
{
- // Load the base entity template, if it wasn't already loaded
- std::string baseName = templateName.substr(8);
- if (!LoadTemplateFile(baseName, depth+1))
- {
- LOGERROR("Failed to load entity template '%s'", baseName.c_str());
- return false;
- }
- // Copy a subset to the requested template
- CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], false);
- return true;
- }
+ std::string prefix = templateName.substr(0, pos);
+ std::string baseName = templateName.substr(pos+1);
- // 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("Failed to load entity template '%s'", 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 "mirage|foo"
- if (templateName.find("mirage|") == 0)
- {
- // Load the base entity template, if it wasn't already loaded
- std::string baseName = templateName.substr(7);
- if (!LoadTemplateFile(baseName, depth+1))
+ VfsPath path = VfsPath(TEMPLATE_ROOT) / L"special_filter" / wstring_from_utf8(prefix + ".xml");
+ if (!VfsFileExists(path))
{
- LOGERROR("Failed to load entity template '%s'", baseName.c_str());
+ LOGERROR("Invalid subset '%s'", prefix.c_str());
return false;
}
- // Copy a subset to the requested template
- CopyMirageSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
- 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("Failed to load entity template '%s'", baseName.c_str());
- return false;
- }
- // Copy a subset to the requested template
- CopyFoundationSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
- return true;
- }
+ CXeromyces xero;
+ PSRETURN ok = xero.Load(g_VFS, path);
+ if (ok != PSRETURN_OK)
+ return false; // (Xeromyces already logged an error with the full filename)
- // 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("Failed to load entity template '%s'", 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("Failed to load entity template '%s'", baseName.c_str());
- return false;
- }
- // Copy a subset to the requested template
- CopyResourceSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
+ m_TemplateFileData[templateName] = m_TemplateFileData[baseName];
+ CParamNode::LoadXML(m_TemplateFileData[templateName], xero, path.string().c_str());
return true;
}
@@ -215,14 +154,19 @@ bool CTemplateLoader::TemplateExists(const std::string& templateName) const
std::vector CTemplateLoader::FindPlaceableTemplates(const std::string& path, bool includeSubdirectories, ETemplatesType templatesType, ScriptInterface& scriptInterface)
{
+ if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
+ {
+ LOGERROR("Undefined template type (valid: all, simulation, actor)");
+ return std::vector();
+ }
+
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
-
+
std::vector templates;
Status ok;
VfsPath templatePath;
-
if (templatesType == SIMULATION_TEMPLATES || templatesType == ALL_TEMPLATES)
{
JS::RootedValue placeablesFilter(cx);
@@ -259,11 +203,11 @@ std::vector CTemplateLoader::FindPlaceableTemplates(const std::stri
std::wstring fileFilter;
scriptInterface.GetProperty(val, "directory", directoryPath);
scriptInterface.GetProperty(val, "file", fileFilter);
-
+
VfsPaths filenames;
if (vfs::GetPathnames(g_VFS, templatePath / (directoryPath + "/"), fileFilter.c_str(), filenames) != INFO::OK)
continue;
-
+
for (const VfsPath& filename : filenames)
{
// Strip the .xml extension
@@ -273,9 +217,8 @@ std::vector CTemplateLoader::FindPlaceableTemplates(const std::stri
templates.emplace_back(name.begin(), name.end());
}
-
+
}
-
}
}
@@ -289,9 +232,6 @@ std::vector CTemplateLoader::FindPlaceableTemplates(const std::stri
WARN_IF_ERR(ok);
}
- if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
- LOGERROR("Undefined template type (valid: all, simulation, actor)");
-
return templates;
}
@@ -299,30 +239,19 @@ std::vector CTemplateLoader::FindTemplates(const std::string& path,
{
std::vector 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)
+ if (templatesType != SIMULATION_TEMPLATES && 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);
+ LOGERROR("Undefined template type (valid: all, simulation, actor)");
+ return templates;
}
- if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
- LOGERROR("Undefined template type (valid: all, simulation, actor)");
+ size_t flags = includeSubdirectories ? vfs::DIR_RECURSIVE : 0;
+
+ if (templatesType == SIMULATION_TEMPLATES || templatesType == ALL_TEMPLATES)
+ WARN_IF_ERR(vfs::ForEachFile(g_VFS, VfsPath(TEMPLATE_ROOT) / path, AddToTemplates, (uintptr_t)&templates, L"*.xml", flags));
+
+ if (templatesType == ACTOR_TEMPLATES || templatesType == ALL_TEMPLATES)
+ WARN_IF_ERR(vfs::ForEachFile(g_VFS, VfsPath(ACTOR_ROOT) / path, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", flags));
return templates;
}
@@ -367,203 +296,3 @@ void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CPara
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 permittedComponentTypes;
- permittedComponentTypes.insert("Identity");
- permittedComponentTypes.insert("Ownership");
- permittedComponentTypes.insert("Position");
- permittedComponentTypes.insert("Visibility");
- 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, "");
- 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, "false");
-
- if (!corpse)
- {
- // Previews should not cast shadows
- if (out.GetChild("Entity").GetChild("VisualActor").IsOk())
- CParamNode::LoadXMLString(out, "");
-
- // Previews should always be visible in fog-of-war/etc
- CParamNode::LoadXMLString(out, "truetrue");
- }
-
- if (corpse)
- {
- // Corpses should include decay components and activate them
- if (out.GetChild("Entity").GetChild("Decay").IsOk())
- CParamNode::LoadXMLString(out, "true");
-
- // Corpses shouldn't display silhouettes (especially since they're often half underground)
- if (out.GetChild("Entity").GetChild("VisualActor").IsOk())
- CParamNode::LoadXMLString(out, "false");
-
- // Corpses should remain visible in fog-of-war (for the owner only)
- CParamNode::LoadXMLString(out, "true");
- }
-}
-
-void CTemplateLoader::CopyMirageSubset(CParamNode& out, const CParamNode& in)
-{
- // Currently used for mirage entities replacing real ones in fog-of-war
-
- std::set permittedComponentTypes;
- permittedComponentTypes.insert("Footprint");
- permittedComponentTypes.insert("Minimap");
- permittedComponentTypes.insert("Obstruction");
- permittedComponentTypes.insert("Ownership");
- permittedComponentTypes.insert("OverlayRenderer");
- permittedComponentTypes.insert("Position");
- permittedComponentTypes.insert("Selectable");
- permittedComponentTypes.insert("StatusBars");
- permittedComponentTypes.insert("Visibility");
- permittedComponentTypes.insert("VisualActor");
-
- CParamNode::LoadXMLString(out, "");
- out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
-
- // Select a subset of identity data. We don't want to have, for example, a CC mirage
- // that has also the CC class and then prevents construction of other CCs
- std::set identitySubset;
- identitySubset.insert("Civ");
- identitySubset.insert("GenericName");
- identitySubset.insert("SpecificName");
- identitySubset.insert("Tooltip");
- identitySubset.insert("History");
- identitySubset.insert("Icon");
- CParamNode identity;
- CParamNode::LoadXMLString(identity, "");
- identity.CopyFilteredChildrenOfChild(in.GetChild("Entity"), "Identity", identitySubset);
- CParamNode::LoadXMLString(out, (""+utf8_from_wstring(identity.ToXML())+"").c_str());
-
- // Mirages obstruction shouldn't block anything
- if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
- CParamNode::LoadXMLString(out, "falsefalsefalsefalse");
-
- // Set the entity as mirage entity
- CParamNode::LoadXMLString(out, "");
-}
-
-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 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("Fogging");
- permittedComponentTypes.insert("Armour");
- permittedComponentTypes.insert("Health");
- permittedComponentTypes.insert("Market");
- permittedComponentTypes.insert("StatusBars");
- permittedComponentTypes.insert("OverlayRenderer");
- permittedComponentTypes.insert("Decay");
- permittedComponentTypes.insert("Cost");
- permittedComponentTypes.insert("Sound");
- permittedComponentTypes.insert("Visibility");
- permittedComponentTypes.insert("Vision");
- permittedComponentTypes.insert("AIProxy");
- permittedComponentTypes.insert("RallyPoint");
- permittedComponentTypes.insert("RallyPointRenderer");
-
- CParamNode::LoadXMLString(out, "");
- out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
-
- // Switch the actor to foundation mode
- CParamNode::LoadXMLString(out, "");
-
- // Add the Foundation component, to deal with the construction process
- CParamNode::LoadXMLString(out, "");
-
- // Initialise health to 1
- CParamNode::LoadXMLString(out, "1");
-
- // Foundations shouldn't initially block unit movement
- if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
- CParamNode::LoadXMLString(out, "truetrue");
-
- // Don't provide population bonuses yet (but still do take up population cost)
- if (out.GetChild("Entity").GetChild("Cost").IsOk())
- CParamNode::LoadXMLString(out, "0");
-
- // 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, "0");
- // Foundations should not have special vision capabilities either
- if (out.GetChild("Entity").GetChild("Vision").GetChild("RevealShore").IsOk())
- CParamNode::LoadXMLString(out, "false");
- }
-}
-
-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 permittedComponentTypes;
- permittedComponentTypes.insert("Footprint");
- permittedComponentTypes.insert("Ownership");
- permittedComponentTypes.insert("Position");
- permittedComponentTypes.insert("VisualActor");
-
- CParamNode::LoadXMLString(out, "");
- 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.
- // Don't emit sound as our samples only apply to living animals.
- std::set permittedComponentTypes;
- permittedComponentTypes.insert("Ownership");
- permittedComponentTypes.insert("Position");
- permittedComponentTypes.insert("VisualActor");
- permittedComponentTypes.insert("Identity");
- permittedComponentTypes.insert("Minimap");
- permittedComponentTypes.insert("ResourceSupply");
- permittedComponentTypes.insert("Selectable");
- permittedComponentTypes.insert("Footprint");
- permittedComponentTypes.insert("StatusBars");
- permittedComponentTypes.insert("OverlayRenderer");
- permittedComponentTypes.insert("AIProxy");
-
- CParamNode::LoadXMLString(out, "");
- out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
-
- // When dying, resources lose the unitMotion component
- // This causes them to have no clearance. Since unit obstructions no longer have a radius,
- // this makes them unreachable in some cases (see #3530).
- // Instead, create a static, unblocking (see #3530 for why) static obstruction.
- // TODO: this should probably be generalized as a parameter on entity death or something.
- CParamNode::LoadXMLString(out, "truefalsefalsefalsefalsefalsefalse");
-}
diff --git a/source/ps/TemplateLoader.h b/source/ps/TemplateLoader.h
index c34d303..d2a6514 100644
--- a/source/ps/TemplateLoader.h
+++ b/source/ps/TemplateLoader.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2015 Wildfire Games.
+/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -48,7 +48,7 @@ public:
CTemplateLoader()
{
}
-
+
/**
* Provides the file data for requested template.
*/
@@ -81,36 +81,6 @@ private:
*/
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 fogged "mirage"
- * entity (position, actor) into a new entity template
- */
- void CopyMirageSubset(CParamNode& out, const CParamNode& in);
-
- /**
- * 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.
diff --git a/source/simulation2/components/tests/test_scripts.h b/source/simulation2/components/tests/test_scripts.h
index 5cf8c2a..29014b4 100644
--- a/source/simulation2/components/tests/test_scripts.h
+++ b/source/simulation2/components/tests/test_scripts.h
@@ -59,10 +59,7 @@ public:
void test_scripts()
{
if (!VfsFileExists(L"simulation/components/tests/setup.js"))
- {
- debug_printf("WARNING: Skipping component scripts tests (can't find binaries/data/mods/public/simulation/components/tests/setup.js)\n");
return;
- }
VfsPaths paths;
TS_ASSERT_OK(vfs::GetPathnames(g_VFS, L"simulation/components/tests/", L"test_*.js", paths));
diff --git a/source/simulation2/system/ParamNode.cpp b/source/simulation2/system/ParamNode.cpp
index 173117b..89ba11d 100644
--- a/source/simulation2/system/ParamNode.cpp
+++ b/source/simulation2/system/ParamNode.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2015 Wildfire Games.
+/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -76,6 +76,8 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
// Look for special attributes
int at_disable = xmb.GetAttributeID("disable");
int at_replace = xmb.GetAttributeID("replace");
+ int at_filtered = xmb.GetAttributeID("filtered");
+ int at_merge = xmb.GetAttributeID("merge");
int at_op = xmb.GetAttributeID("op");
int at_datatype = xmb.GetAttributeID("datatype");
enum op {
@@ -84,6 +86,8 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
MUL
} op = INVALID;
bool replacing = false;
+ bool filtering = false;
+ bool merging = false;
{
XERO_ITER_ATTR(element, attr)
{
@@ -97,6 +101,16 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
m_Childs.erase(name);
replacing = true;
}
+ else if (attr.Name == at_filtered)
+ {
+ filtering = true;
+ }
+ else if (attr.Name == at_merge)
+ {
+ if (m_Childs.find(name) == m_Childs.end())
+ return;
+ merging = true;
+ }
else if (attr.Name == at_op)
{
if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"add")
@@ -168,20 +182,32 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
}
hasSetValue = true;
}
- if (!hasSetValue)
+ if (!hasSetValue && !merging)
node.m_Value = value;
+ // For the filtered case
+ ChildrenMap childs;
+
// Recurse through the element's children
XERO_ITER_EL(element, child)
{
node.ApplyLayer(xmb, child, sourceIdentifier);
+ if (filtering)
+ {
+ std::string childname = xmb.GetElementString(child.GetNodeName());
+ if (node.m_Childs.find(childname) != node.m_Childs.end())
+ childs[childname] = std::move(node.m_Childs[childname]);
+ }
}
+ if (filtering)
+ node.m_Childs.swap(childs);
+
// Add the element's attributes, prefixing names with "@"
XERO_ITER_ATTR(element, attr)
{
// Skip special attributes
- if (attr.Name == at_replace || attr.Name == at_op)
+ if (attr.Name == at_replace || attr.Name == at_op || attr.Name == at_merge || attr.Name == at_filtered)
continue;
// Add any others
std::string attrName = xmb.GetAttributeString(attr.Name);
@@ -189,21 +215,6 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
}
}
-void CParamNode::CopyFilteredChildrenOfChild(const CParamNode& src, const char* name, const std::set& permitted)
-{
- ResetScriptVal();
-
- ChildrenMap::iterator dstChild = m_Childs.find(name);
- ChildrenMap::const_iterator srcChild = src.m_Childs.find(name);
- if (dstChild == m_Childs.end() || srcChild == src.m_Childs.end())
- return; // error
-
- ChildrenMap::const_iterator it = srcChild->second.m_Childs.begin();
- for (; it != srcChild->second.m_Childs.end(); ++it)
- if (permitted.count(it->first))
- dstChild->second.m_Childs[it->first] = it->second;
-}
-
const CParamNode& CParamNode::GetChild(const char* name) const
{
ChildrenMap::const_iterator it = m_Childs.find(name);
diff --git a/source/simulation2/system/ParamNode.h b/source/simulation2/system/ParamNode.h
index 4fc911f..0b18e5e 100644
--- a/source/simulation2/system/ParamNode.h
+++ b/source/simulation2/system/ParamNode.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2015 Wildfire Games.
+/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -58,6 +58,15 @@ class XMBElement;
*
* one two three
*
+ *
+ *
+ *
+ * test
+ *
+ *
+ * example
+ *
+ *
*
* @endcode
* then a second like:
@@ -75,6 +84,15 @@ class XMBElement;
* four
* -two
*
+ *
+ *
+ * example
+ *
+ *
+ *
+ * text
+ *
+ *
*
* @endcode
* is equivalent to loading a single file like:
@@ -90,6 +108,15 @@ class XMBElement;
*
* one three four
*
+ *
+ *
+ * test
+ * example
+ *
+ *
+ * text
+ *
+ *
*
* @endcode
*
@@ -103,7 +130,16 @@ class XMBElement;
* "Example3": {
* "D": "new"
* },
- * "Example4": { "@datatype": "tokens", "_string": "one three four" }
+ * "Example4": { "@datatype": "tokens", "_string": "one three four" },
+ * "Example5": {
+ * "F": {
+ * "I": "test",
+ * "K": "example"
+ * },
+ * "H": {
+ * "J": "text"
+ * }
+ * }
* }
* }
* @endcode
@@ -146,14 +182,6 @@ public:
static PSRETURN LoadXMLString(CParamNode& ret, const char* xml, const wchar_t* sourceIdentifier = NULL);
/**
- * Finds the childs named @a name from @a src and from @a this, and copies the source child's children
- * which are in the @a permitted set into this node's child.
- * Intended for use as a filtered clone of XML files.
- * @a this and @a src must have childs named @a name.
- */
- void CopyFilteredChildrenOfChild(const CParamNode& src, const char* name, const std::set& permitted);
-
- /**
* Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none.
*/
const CParamNode& GetChild(const char* name) const;
diff --git a/source/simulation2/tests/test_CmpTemplateManager.h b/source/simulation2/tests/test_CmpTemplateManager.h
index 8103254..0fdc180 100644
--- a/source/simulation2/tests/test_CmpTemplateManager.h
+++ b/source/simulation2/tests/test_CmpTemplateManager.h
@@ -31,6 +31,8 @@
#include "ps/CLogger.h"
#include "ps/XML/Xeromyces.h"
+static const char SPECIAL_FILTER[] = "simulation/templates/special_filter/";
+
class TestCmpTemplateManager : public CxxTest::TestSuite
{
public:
@@ -39,6 +41,10 @@ public:
g_VFS = CreateVfs(20 * MiB);
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"_test.sim", VFS_MOUNT_MUST_EXIST));
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
+
+ // If the public mod exists, test the special_filter templates too
+ g_VFS->Mount(SPECIAL_FILTER, DataDir() / "mods" / "public" / SPECIAL_FILTER);
+
CXeromyces::Startup();
}
@@ -117,9 +123,23 @@ public:
ScriptInterface::ToJSVal(cx, &val, &actor->GetChild("VisualActor"));
TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({Actor:\"example1\", ActorOnly:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})");
- const CParamNode* foundation = tempMan->LoadTemplate(ent2, "foundation|actor|example1", -1);
- ScriptInterface::ToJSVal(cx, &val, &foundation->GetChild("VisualActor"));
- TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({Actor:\"example1\", ActorOnly:(void 0), Foundation:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})");
+ if (VfsDirectoryExists(SPECIAL_FILTER))
+ {
+ const CParamNode* foundation = tempMan->LoadTemplate(ent2, "foundation|actor|example1", -1);
+ ScriptInterface::ToJSVal(cx, &val, &foundation->GetChild("VisualActor"));
+
+ TS_ASSERT_STR_EQUALS(
+ man.GetScriptInterface().ToString(&val),
+ "({"
+ "Actor:\"example1\", "
+ "ActorOnly:(void 0), "
+ "Foundation:(void 0), "
+ "SilhouetteDisplay:\"false\", "
+ "SilhouetteOccluder:\"false\", "
+ "VisibleInAtlasOnly:\"false\""
+ "})"
+ );
+ }
}
void test_LoadTemplate_errors()
diff --git a/source/simulation2/tests/test_ParamNode.h b/source/simulation2/tests/test_ParamNode.h
index 043554b..83e11b9 100644
--- a/source/simulation2/tests/test_ParamNode.h
+++ b/source/simulation2/tests/test_ParamNode.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2010 Wildfire Games.
+/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -135,6 +135,164 @@ public:
TS_ASSERT_WSTR_EQUALS(node.ToXML(), L"Y X");
}
+ void test_overlay_filtered()
+ {
+ CParamNode node;
+ TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
+ ""
+ ""
+ ""
+ ""
+ "toberemoved"
+ ""
+ ""
+ ""
+ ""), PSRETURN_OK);
+
+ TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""), PSRETURN_OK);
+
+ TS_ASSERT_WSTR_EQUALS(node.ToXML(),
+ L""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "");
+ }
+
+ void test_overlay_filtered_and_merge()
+ {
+ CParamNode node;
+ TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
+ ""
+ ""
+ "b"
+ "c"
+ "d"
+ "e"
+ ""
+ ""
+ ""), PSRETURN_OK);
+
+ TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
+ ""
+ ""
+ ""
+ "c2"
+ ""
+ ""
+ ""), PSRETURN_OK);
+
+ TS_ASSERT_WSTR_EQUALS(node.ToXML(),
+ L""
+ ""
+ "b"
+ "c2"
+ ""
+ ""
+ "");
+ }
+
+ void test_overlay_merge()
+ {
+ CParamNode node;
+ TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
+ ""
+ ""
+ "foo"
+ "bar"
+ ""
+ ""
+ ""
+ "foo"
+ ""
+ ""
+ ""), PSRETURN_OK);
+
+ TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
+ ""
+ ""
+ "test"
+ "baz"
+ ""
+ ""
+ "willnotbeincluded"
+ ""
+ ""
+ ""
+ "text"
+ ""
+ "more text"
+ ""
+ ""), PSRETURN_OK);
+
+ TS_ASSERT_WSTR_EQUALS(node.ToXML(),
+ L""
+ ""
+ "test"
+ "bar"
+ "baz"
+ ""
+ ""
+ "more text"
+ ""
+ "text"
+ "foo"
+ ""
+ ""
+ "");
+ }
+
+ void test_overlay_filtered_merge()
+ {
+ CParamNode node;
+ TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "1200"
+ ""
+ ""), PSRETURN_OK);
+
+ TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
+ ""
+ ""
+ "bar"
+ ""
+ ""
+ "1"
+ ""
+ ""), PSRETURN_OK);
+
+ TS_ASSERT_WSTR_EQUALS(node.ToXML(),
+ L""
+ ""
+ "1"
+ "1200"
+ ""
+ ""
+ ""
+ ""
+ "bar"
+ "");
+ }
+
void test_types()
{
CParamNode node;