Use XML files instead of hardcoded C++ code in the template manager to universally change template properties at load time.
Patch By: leper Differential Revision: https://code.wildfiregames.com/D215 Fixes #2951 This was SVN commit r19302.
This commit is contained in:
parent
59efb76966
commit
d093f714d7
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Entity filtered="">
|
||||||
|
<AIProxy merge=""/>
|
||||||
|
<Armour merge=""/>
|
||||||
|
<BuildRestrictions merge=""/>
|
||||||
|
<!-- Don't provide population bonuses yet (but still do take up population cost) -->
|
||||||
|
<Cost merge="">
|
||||||
|
<PopulationBonus>0</PopulationBonus>
|
||||||
|
</Cost>
|
||||||
|
<Decay merge=""/>
|
||||||
|
<Health>
|
||||||
|
<Initial>1</Initial>
|
||||||
|
</Health>
|
||||||
|
<Fogging merge=""/>
|
||||||
|
<Footprint merge=""/>
|
||||||
|
<!-- Add the Foundation component, to deal with the construction process -->
|
||||||
|
<Foundation replace=""/>
|
||||||
|
<Health merge=""/>
|
||||||
|
<Identity merge=""/>
|
||||||
|
<!-- Foundations shouldn't initially block unit movement -->
|
||||||
|
<Obstruction merge="">
|
||||||
|
<DisableBlockMovement>true</DisableBlockMovement>
|
||||||
|
<DisableBlockPathfinding>true</DisableBlockPathfinding>
|
||||||
|
</Obstruction>
|
||||||
|
<OverlayRenderer merge=""/>
|
||||||
|
<Ownership merge=""/>
|
||||||
|
<Position merge=""/>
|
||||||
|
<RallyPoint merge=""/>
|
||||||
|
<RallyPointRenderer merge=""/>
|
||||||
|
<Selectable merge=""/>
|
||||||
|
<Sound merge=""/>
|
||||||
|
<StatusBars merge=""/>
|
||||||
|
<Visibility merge=""/>
|
||||||
|
<!-- Foundations should be visible themselves in fog-of-war if their base template is,
|
||||||
|
but shouldn't have any vision range -->
|
||||||
|
<Vision merge="">
|
||||||
|
<Range>0</Range>
|
||||||
|
<RevealShore merge="">false</RevealShore>
|
||||||
|
</Vision>
|
||||||
|
<!-- Switch the actor to foundation mode -->
|
||||||
|
<VisualActor>
|
||||||
|
<Foundation/>
|
||||||
|
</VisualActor>
|
||||||
|
</Entity>
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Entity filtered="">
|
||||||
|
<Footprint merge=""/>
|
||||||
|
<Ownership merge=""/>
|
||||||
|
<Position merge=""/>
|
||||||
|
<VisualActor merge=""/>
|
||||||
|
</Entity>
|
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Entity filtered="">
|
||||||
|
<!-- 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 -->
|
||||||
|
<Attack merge=""/> <!-- Needed for the Actor Viewer -->
|
||||||
|
<BuildRestrictions merge=""/>
|
||||||
|
<!-- Corpses should include decay components and activate them -->
|
||||||
|
<Decay merge="">
|
||||||
|
<Active>true</Active>
|
||||||
|
</Decay>
|
||||||
|
<Footprint merge=""/>
|
||||||
|
<Identity merge=""/>
|
||||||
|
<!-- 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) -->
|
||||||
|
<Obstruction merge="">
|
||||||
|
<Active>false</Active>
|
||||||
|
</Obstruction>
|
||||||
|
<Ownership merge=""/>
|
||||||
|
<Position merge=""/>
|
||||||
|
<Sound merge=""/> <!-- Needed for the Actor Viewer -->
|
||||||
|
<UnitMotion merge=""/> <!-- Needed for the Actor Viewer -->
|
||||||
|
<!-- Corpses should remain visible in fog-of-war (for the owner only) -->
|
||||||
|
<Visibility>
|
||||||
|
<Corpse>true</Corpse>
|
||||||
|
</Visibility>
|
||||||
|
<!-- Corpses shouldn't display silhouettes (especially since they're often half underground) -->
|
||||||
|
<VisualActor merge="">
|
||||||
|
<SilhouetteDisplay>false</SilhouetteDisplay>
|
||||||
|
</VisualActor>
|
||||||
|
</Entity>
|
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Entity filtered="">
|
||||||
|
<AIProxy merge=""/>
|
||||||
|
<Armour merge=""/>
|
||||||
|
<BuildRestrictions merge=""/>
|
||||||
|
<!-- Don't provide population bonuses yet (but still do take up population cost) -->
|
||||||
|
<Cost merge="">
|
||||||
|
<PopulationBonus>0</PopulationBonus>
|
||||||
|
</Cost>
|
||||||
|
<Decay merge=""/>
|
||||||
|
<Fogging merge=""/>
|
||||||
|
<Footprint merge=""/>
|
||||||
|
<!-- Add the Foundation component, to deal with the construction process -->
|
||||||
|
<Foundation replace=""/>
|
||||||
|
<Health>
|
||||||
|
<Initial>1</Initial>
|
||||||
|
</Health>
|
||||||
|
<Identity merge=""/>
|
||||||
|
<Market merge=""/>
|
||||||
|
<!-- Foundations shouldn't initially block unit movement -->
|
||||||
|
<Obstruction merge="">
|
||||||
|
<DisableBlockMovement>true</DisableBlockMovement>
|
||||||
|
<DisableBlockPathfinding>true</DisableBlockPathfinding>
|
||||||
|
</Obstruction>
|
||||||
|
<OverlayRenderer merge=""/>
|
||||||
|
<Ownership merge=""/>
|
||||||
|
<Position merge=""/>
|
||||||
|
<RallyPoint merge=""/>
|
||||||
|
<RallyPointRenderer merge=""/>
|
||||||
|
<Selectable merge=""/>
|
||||||
|
<Sound merge=""/>
|
||||||
|
<StatusBars merge=""/>
|
||||||
|
<Visibility merge=""/>
|
||||||
|
<!-- Foundations should be visible themselves in fog-of-war if their base template is,
|
||||||
|
but shouldn't have any vision range -->
|
||||||
|
<Vision merge="">
|
||||||
|
<Range>0</Range>
|
||||||
|
<RevealShore>false</RevealShore>
|
||||||
|
</Vision>
|
||||||
|
<!-- Switch the actor to foundation mode -->
|
||||||
|
<VisualActor>
|
||||||
|
<Foundation/>
|
||||||
|
</VisualActor>
|
||||||
|
</Entity>
|
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Entity filtered="">
|
||||||
|
<Footprint merge=""/>
|
||||||
|
<Identity filtered="">
|
||||||
|
<Civ merge=""/>
|
||||||
|
<GenericName merge=""/>
|
||||||
|
<SpecificName merge=""/>
|
||||||
|
<Tooltip merge=""/>
|
||||||
|
<History merge=""/>
|
||||||
|
<Icon merge=""/>
|
||||||
|
</Identity>
|
||||||
|
<Minimap merge=""/>
|
||||||
|
<Mirage replace=""/>
|
||||||
|
<Obstruction merge="">
|
||||||
|
<BlockMovement>false</BlockMovement>
|
||||||
|
<BlockPathfinding>false</BlockPathfinding>
|
||||||
|
<BlockFoundation>false</BlockFoundation>
|
||||||
|
<BlockConstruction>false</BlockConstruction>
|
||||||
|
</Obstruction>
|
||||||
|
<Ownership merge=""/>
|
||||||
|
<OverlayRenderer merge=""/>
|
||||||
|
<Position merge=""/>
|
||||||
|
<Selectable merge=""/>
|
||||||
|
<StatusBars merge=""/>
|
||||||
|
<Visibility merge=""/>
|
||||||
|
<VisualActor merge=""/>
|
||||||
|
</Entity>
|
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Entity filtered="">
|
||||||
|
<!-- 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 -->
|
||||||
|
<Attack merge=""/> <!-- Needed for the Actor Viewer -->
|
||||||
|
<BuildRestrictions merge=""/>
|
||||||
|
<Decay merge=""/>
|
||||||
|
<Footprint merge=""/>
|
||||||
|
<Identity merge=""/>
|
||||||
|
<!-- 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) -->
|
||||||
|
<Obstruction merge="">
|
||||||
|
<Active>false</Active>
|
||||||
|
</Obstruction>
|
||||||
|
<Ownership merge=""/>
|
||||||
|
<Position merge=""/>
|
||||||
|
<Sound merge=""/> <!-- Needed for the Actor Viewer -->
|
||||||
|
<UnitMotion merge=""/> <!-- Needed for the Actor Viewer -->
|
||||||
|
<!-- Previews should always be visible in fog-of-war/etc -->
|
||||||
|
<Visibility>
|
||||||
|
<AlwaysVisible>true</AlwaysVisible>
|
||||||
|
<Preview>true</Preview>
|
||||||
|
</Visibility>
|
||||||
|
<!-- Previews should not cast shadows -->
|
||||||
|
<VisualActor merge="">
|
||||||
|
<DisableShadows/>
|
||||||
|
</VisualActor>
|
||||||
|
</Entity>
|
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Entity filtered="">
|
||||||
|
<AIProxy merge=""/>
|
||||||
|
<Footprint merge=""/>
|
||||||
|
<Identity merge=""/>
|
||||||
|
<Minimap merge=""/>
|
||||||
|
<!-- 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.
|
||||||
|
-->
|
||||||
|
<Obstruction replace="">
|
||||||
|
<Active>true</Active>
|
||||||
|
<BlockMovement>false</BlockMovement>
|
||||||
|
<BlockPathfinding>false</BlockPathfinding>
|
||||||
|
<BlockFoundation>false</BlockFoundation>
|
||||||
|
<BlockConstruction>false</BlockConstruction>
|
||||||
|
<DisableBlockMovement>false</DisableBlockMovement>
|
||||||
|
<DisableBlockPathfinding>false</DisableBlockPathfinding>
|
||||||
|
<Static width="2.0" depth="2.0"/>
|
||||||
|
</Obstruction>
|
||||||
|
<OverlayRenderer merge=""/>
|
||||||
|
<Ownership merge=""/>
|
||||||
|
<Position merge=""/>
|
||||||
|
<ResourceSupply merge=""/>
|
||||||
|
<Selectable merge=""/>
|
||||||
|
<StatusBars merge=""/>
|
||||||
|
<VisualActor merge=""/>
|
||||||
|
</Entity>
|
@ -49,93 +49,33 @@ bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int dept
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle special case "preview|foo"
|
// Handle special case "bar|foo"
|
||||||
if (templateName.find("preview|") == 0)
|
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 prefix = templateName.substr(0, pos);
|
||||||
std::string baseName = templateName.substr(8);
|
std::string baseName = templateName.substr(pos+1);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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))
|
if (!LoadTemplateFile(baseName, depth+1))
|
||||||
{
|
{
|
||||||
LOGERROR("Failed to load entity template '%s'", baseName.c_str());
|
LOGERROR("Failed to load entity template '%s'", baseName.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Copy a subset to the requested template
|
|
||||||
CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle special case "mirage|foo"
|
VfsPath path = VfsPath(TEMPLATE_ROOT) / L"special_filter" / wstring_from_utf8(prefix + ".xml");
|
||||||
if (templateName.find("mirage|") == 0)
|
if (!VfsFileExists(path))
|
||||||
{
|
|
||||||
// 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());
|
LOGERROR("Invalid subset '%s'", prefix.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Copy a subset to the requested template
|
|
||||||
CopyMirageSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle special case "foundation|foo"
|
CXeromyces xero;
|
||||||
if (templateName.find("foundation|") == 0)
|
PSRETURN ok = xero.Load(g_VFS, path);
|
||||||
{
|
if (ok != PSRETURN_OK)
|
||||||
// Load the base entity template, if it wasn't already loaded
|
return false; // (Xeromyces already logged an error with the full filename)
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle special case "construction|foo"
|
m_TemplateFileData[templateName] = m_TemplateFileData[baseName];
|
||||||
if (templateName.find("construction|") == 0)
|
CParamNode::LoadXML(m_TemplateFileData[templateName], xero, path.string().c_str());
|
||||||
{
|
|
||||||
// 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]);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,203 +288,3 @@ void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CPara
|
|||||||
|
|
||||||
CParamNode::LoadXMLString(out, xml.c_str(), actorNameW.c_str());
|
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("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, "<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><Visibility><AlwaysVisible>true</AlwaysVisible><Preview>true</Preview></Visibility></Entity>");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (corpse)
|
|
||||||
{
|
|
||||||
// Corpses should include decay components and activate them
|
|
||||||
if (out.GetChild("Entity").GetChild("Decay").IsOk())
|
|
||||||
CParamNode::LoadXMLString(out, "<Entity><Decay><Active>true</Active></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 (for the owner only)
|
|
||||||
CParamNode::LoadXMLString(out, "<Entity><Visibility><Corpse>true</Corpse></Visibility></Entity>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CTemplateLoader::CopyMirageSubset(CParamNode& out, const CParamNode& in)
|
|
||||||
{
|
|
||||||
// Currently used for mirage entities replacing real ones in fog-of-war
|
|
||||||
|
|
||||||
std::set<std::string> 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, "<Entity/>");
|
|
||||||
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<std::string> 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/>");
|
|
||||||
identity.CopyFilteredChildrenOfChild(in.GetChild("Entity"), "Identity", identitySubset);
|
|
||||||
CParamNode::LoadXMLString(out, ("<Entity>"+utf8_from_wstring(identity.ToXML())+"</Entity>").c_str());
|
|
||||||
|
|
||||||
// Mirages obstruction shouldn't block anything
|
|
||||||
if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
|
|
||||||
CParamNode::LoadXMLString(out, "<Entity><Obstruction><BlockMovement>false</BlockMovement><BlockPathfinding>false</BlockPathfinding><BlockFoundation>false</BlockFoundation><BlockConstruction>false</BlockConstruction></Obstruction></Entity>");
|
|
||||||
|
|
||||||
// Set the entity as mirage entity
|
|
||||||
CParamNode::LoadXMLString(out, "<Entity><Mirage/></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("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, "<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>");
|
|
||||||
// Foundations should not have special vision capabilities either
|
|
||||||
if (out.GetChild("Entity").GetChild("Vision").GetChild("RevealShore").IsOk())
|
|
||||||
CParamNode::LoadXMLString(out, "<Entity><Vision><RevealShore>false</RevealShore></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.
|
|
||||||
// Don't emit sound as our samples only apply to living animals.
|
|
||||||
std::set<std::string> 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, "<Entity/>");
|
|
||||||
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, "<Entity><Obstruction><Active>true</Active><BlockMovement>false</BlockMovement><BlockPathfinding>false</BlockPathfinding><BlockFoundation>false</BlockFoundation><BlockConstruction>false</BlockConstruction><DisableBlockMovement>false</DisableBlockMovement><DisableBlockPathfinding>false</DisableBlockPathfinding><Static width=\"2.0\" depth=\"2.0\"/></Obstruction></Entity>");
|
|
||||||
}
|
|
||||||
|
@ -81,36 +81,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
void ConstructTemplateActor(const std::string& actorName, CParamNode& out);
|
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
|
* 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.
|
* loaded non-broken template data. This includes files that will fail schema validation.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2015 Wildfire Games.
|
/* Copyright (C) 2017 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 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
|
// Look for special attributes
|
||||||
int at_disable = xmb.GetAttributeID("disable");
|
int at_disable = xmb.GetAttributeID("disable");
|
||||||
int at_replace = xmb.GetAttributeID("replace");
|
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_op = xmb.GetAttributeID("op");
|
||||||
int at_datatype = xmb.GetAttributeID("datatype");
|
int at_datatype = xmb.GetAttributeID("datatype");
|
||||||
enum op {
|
enum op {
|
||||||
@ -84,6 +86,8 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
|
|||||||
MUL
|
MUL
|
||||||
} op = INVALID;
|
} op = INVALID;
|
||||||
bool replacing = false;
|
bool replacing = false;
|
||||||
|
bool filtering = false;
|
||||||
|
bool merging = false;
|
||||||
{
|
{
|
||||||
XERO_ITER_ATTR(element, attr)
|
XERO_ITER_ATTR(element, attr)
|
||||||
{
|
{
|
||||||
@ -97,6 +101,16 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
|
|||||||
m_Childs.erase(name);
|
m_Childs.erase(name);
|
||||||
replacing = true;
|
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)
|
else if (attr.Name == at_op)
|
||||||
{
|
{
|
||||||
if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"add")
|
if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"add")
|
||||||
@ -157,6 +171,7 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
|
|||||||
// TODO: Support parsing of data types other than fixed; log warnings in other cases
|
// TODO: Support parsing of data types other than fixed; log warnings in other cases
|
||||||
fixed oldval = node.ToFixed();
|
fixed oldval = node.ToFixed();
|
||||||
fixed mod = fixed::FromString(CStrW(value));
|
fixed mod = fixed::FromString(CStrW(value));
|
||||||
|
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case ADD:
|
case ADD:
|
||||||
@ -168,24 +183,37 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
|
|||||||
}
|
}
|
||||||
hasSetValue = true;
|
hasSetValue = true;
|
||||||
}
|
}
|
||||||
if (!hasSetValue)
|
|
||||||
|
if (!hasSetValue && !merging)
|
||||||
node.m_Value = value;
|
node.m_Value = value;
|
||||||
|
|
||||||
// We also need to reset node's script val, even if it has no children
|
// We also need to reset node's script val, even if it has no children
|
||||||
// or if the attributes change.
|
// or if the attributes change.
|
||||||
node.ResetScriptVal();
|
node.ResetScriptVal();
|
||||||
|
|
||||||
|
// For the filtered case
|
||||||
|
ChildrenMap childs;
|
||||||
|
|
||||||
// Recurse through the element's children
|
// Recurse through the element's children
|
||||||
XERO_ITER_EL(element, child)
|
XERO_ITER_EL(element, child)
|
||||||
{
|
{
|
||||||
node.ApplyLayer(xmb, child, sourceIdentifier);
|
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 "@"
|
// Add the element's attributes, prefixing names with "@"
|
||||||
XERO_ITER_ATTR(element, attr)
|
XERO_ITER_ATTR(element, attr)
|
||||||
{
|
{
|
||||||
// Skip special attributes
|
// 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;
|
continue;
|
||||||
// Add any others
|
// Add any others
|
||||||
std::string attrName = xmb.GetAttributeString(attr.Name);
|
std::string attrName = xmb.GetAttributeString(attr.Name);
|
||||||
@ -193,21 +221,6 @@ void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CParamNode::CopyFilteredChildrenOfChild(const CParamNode& src, const char* name, const std::set<std::string>& 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
|
const CParamNode& CParamNode::GetChild(const char* name) const
|
||||||
{
|
{
|
||||||
ChildrenMap::const_iterator it = m_Childs.find(name);
|
ChildrenMap::const_iterator it = m_Childs.find(name);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2015 Wildfire Games.
|
/* Copyright (C) 2017 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@ -58,6 +58,15 @@ class XMBElement;
|
|||||||
* <Example4 datatype="tokens">
|
* <Example4 datatype="tokens">
|
||||||
* one two three
|
* one two three
|
||||||
* </Example4>
|
* </Example4>
|
||||||
|
* <Example5>
|
||||||
|
* <E/>
|
||||||
|
* <F>
|
||||||
|
* <I>test</I>
|
||||||
|
* </F>
|
||||||
|
* <H>
|
||||||
|
* <J>example</J>
|
||||||
|
* </H>
|
||||||
|
* </Example5>
|
||||||
* </Entity>
|
* </Entity>
|
||||||
* @endcode
|
* @endcode
|
||||||
* then a second like:
|
* then a second like:
|
||||||
@ -75,6 +84,15 @@ class XMBElement;
|
|||||||
* four <!-- add a token to the parent's set -->
|
* four <!-- add a token to the parent's set -->
|
||||||
* -two <!-- remove a token from the parent's set -->
|
* -two <!-- remove a token from the parent's set -->
|
||||||
* </Example4>
|
* </Example4>
|
||||||
|
* <Example5 filtered=""> <!-- drop all children of this node that are not in this file -->
|
||||||
|
* <F merge=""> <!-- only add this element if it is also present in the parent -->
|
||||||
|
* <K>example</K> <!-- if F is present merge its children normally -->
|
||||||
|
* </F>
|
||||||
|
* <G merge=""/> <!-- keep the G element of the parent if it exists -->
|
||||||
|
* <H>
|
||||||
|
* <J>text</J>
|
||||||
|
* </H>
|
||||||
|
* </Example5>
|
||||||
* </Entity>
|
* </Entity>
|
||||||
* @endcode
|
* @endcode
|
||||||
* is equivalent to loading a single file like:
|
* is equivalent to loading a single file like:
|
||||||
@ -90,6 +108,15 @@ class XMBElement;
|
|||||||
* <Example4>
|
* <Example4>
|
||||||
* one three four
|
* one three four
|
||||||
* </Example4>
|
* </Example4>
|
||||||
|
* <Example5>
|
||||||
|
* <F>
|
||||||
|
* <I>test</I>
|
||||||
|
* <K>example</K>
|
||||||
|
* </F>
|
||||||
|
* <H>
|
||||||
|
* <J>text</J>
|
||||||
|
* </H>
|
||||||
|
* </Example5>
|
||||||
* </Entity>
|
* </Entity>
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
@ -103,7 +130,16 @@ class XMBElement;
|
|||||||
* "Example3": {
|
* "Example3": {
|
||||||
* "D": "new"
|
* "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
|
* @endcode
|
||||||
@ -145,14 +181,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
static PSRETURN LoadXMLString(CParamNode& ret, const char* xml, const wchar_t* sourceIdentifier = NULL);
|
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<std::string>& permitted);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none.
|
* Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none.
|
||||||
*/
|
*/
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2010 Wildfire Games.
|
/* Copyright (C) 2017 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@ -135,6 +135,35 @@ public:
|
|||||||
TS_ASSERT_WSTR_EQUALS(node.ToXML(), L"<test><a datatype=\"tokens\">Y X</a></test>");
|
TS_ASSERT_WSTR_EQUALS(node.ToXML(), L"<test><a datatype=\"tokens\">Y X</a></test>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_overlay_filtered()
|
||||||
|
{
|
||||||
|
CParamNode node;
|
||||||
|
TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node, "<test> <a><b/></a> <c>toberemoved</c> <d><e/></d> </test>"), PSRETURN_OK);
|
||||||
|
TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node, "<test filtered=\"\"> <a/> <d><f/></d> <g/> </test>"), PSRETURN_OK);
|
||||||
|
TS_ASSERT_WSTR_EQUALS(node.ToXML(), L"<test><a><b></b></a><d><e></e><f></f></d><g></g></test>");
|
||||||
|
|
||||||
|
CParamNode node2;
|
||||||
|
TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node2, "<test> <a><b>b</b><c>c</c><d>d</d><e>e</e></a> <f/> </test>"), PSRETURN_OK);
|
||||||
|
TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node2, "<test filtered=\"\"> <a filtered=\"\"><b merge=\"\"/><c>c2</c><d/></a> </test>"), PSRETURN_OK);
|
||||||
|
TS_ASSERT_WSTR_EQUALS(node2.ToXML(), L"<test><a><b>b</b><c>c2</c><d></d></a></test>");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_overlay_merge()
|
||||||
|
{
|
||||||
|
CParamNode node;
|
||||||
|
TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node, "<test> <a><b>foo</b><c>bar</c></a> <x><y><z>foo</z></y></x> </test>"), PSRETURN_OK);
|
||||||
|
TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node, "<test> <a merge=\"\"><b>test</b><d>baz</d></a> <i merge=\"\"><j>willnotbeincluded</j></i> <x merge=\"\"><y merge=\"\"><v>text</v></y><w>more text</w></x> </test>"), PSRETURN_OK);
|
||||||
|
TS_ASSERT_WSTR_EQUALS(node.ToXML(), L"<test><a><b>test</b><c>bar</c><d>baz</d></a><x><w>more text</w><y><v>text</v><z>foo</z></y></x></test>");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_overlay_filtered_merge()
|
||||||
|
{
|
||||||
|
CParamNode node;
|
||||||
|
TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node, "<test> <a><b/></a> <c><x/></c> <Health><Max>1200</Max></Health> </test>"), PSRETURN_OK);
|
||||||
|
TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node, "<test filtered=\"\"> <c merge=\"\"/> <d>bar</d> <e merge=\"\"/> <Health><Initial>1</Initial></Health> </test>"), PSRETURN_OK);
|
||||||
|
TS_ASSERT_WSTR_EQUALS(node.ToXML(), L"<test><Health><Initial>1</Initial><Max>1200</Max></Health><c><x></x></c><d>bar</d></test>");
|
||||||
|
}
|
||||||
|
|
||||||
void test_types()
|
void test_types()
|
||||||
{
|
{
|
||||||
CParamNode node;
|
CParamNode node;
|
||||||
|
Loading…
Reference in New Issue
Block a user