Ticket #2951: 2951_template_subsets_v2.diff

File 2951_template_subsets_v2.diff, 39.8 KB (added by elexis, 8 years ago)

merged github commits

  • new file inaries/data/mods/public/simulation/templates/special_filter/construction.xml

    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
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity filtered="">
     3  <Footprint merge=""/>
     4  <Ownership merge=""/>
     5  <Position merge=""/>
     6  <VisualActor merge=""/>
     7</Entity>
  • new file inaries/data/mods/public/simulation/templates/special_filter/corpse.xml

    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
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity filtered="">
     3  <!--
     4       We only want to include components which are necessary (for the visual previewing of an entity)
     5       and safe (i.e. won't do anything that affects the synchronised simulation state), so additions
     6       to this list should be carefully considered.
     7       The Attack, Sound and UnitMotion components are needed for the Actor Viewer.
     8  -->
     9  <Attack merge=""/>
     10  <BuildRestrictions merge=""/>
     11  <!-- Corpses should include decay components and activate them -->
     12  <Decay merge="">
     13    <Active>true</Active>
     14  </Decay>
     15  <Footprint merge=""/>
     16  <Identity merge=""/>
     17  <!-- Disable the Obstruction component (if there is one) so it doesn't affect pathfinding
     18       (but can still be used for testing this entity for collisions against others) -->
     19  <Obstruction merge="">
     20    <Active>false</Active>
     21  </Obstruction>
     22  <Ownership merge=""/>
     23  <Position merge=""/>
     24  <Sound merge=""/>
     25  <UnitMotion merge=""/>
     26  <!-- Corpses should remain visible in fog-of-war (for the owner only) -->
     27  <Visibility>
     28    <Corpse>true</Corpse>
     29  </Visibility>
     30  <!-- Corpses shouldn't display silhouettes (especially since they're often half underground) -->
     31  <VisualActor merge="">
     32    <SilhouetteDisplay>false</SilhouetteDisplay>
     33  </VisualActor>
     34</Entity>
  • new file inaries/data/mods/public/simulation/templates/special_filter/foundation.xml

    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
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity filtered="">
     3  <AIProxy merge=""/>
     4  <Armour merge=""/>
     5  <BuildRestrictions merge=""/>
     6  <!-- Don't provide population bonuses yet (but still do take up population cost) -->
     7  <Cost merge="">
     8    <PopulationBonus>0</PopulationBonus>
     9  </Cost>
     10  <Decay merge=""/>
     11  <Fogging merge=""/>
     12  <Footprint merge=""/>
     13  <!-- Add the Foundation component, to deal with the construction process -->
     14  <Foundation replace=""/>
     15  <Health>
     16    <Initial>1</Initial>
     17  </Health>
     18  <Identity merge=""/>
     19  <Market merge=""/>
     20  <!-- Foundations shouldn't initially block unit movement -->
     21  <Obstruction merge="">
     22    <DisableBlockMovement>true</DisableBlockMovement>
     23    <DisableBlockPathfinding>true</DisableBlockPathfinding>
     24  </Obstruction>
     25  <OverlayRenderer merge=""/>
     26  <Ownership merge=""/>
     27  <Position merge=""/>
     28  <RallyPoint merge=""/>
     29  <RallyPointRenderer merge=""/>
     30  <Selectable merge=""/>
     31  <Sound merge=""/>
     32  <StatusBars merge=""/>
     33  <Visibility merge=""/>
     34  <!-- Foundations should be visible themselves in fog-of-war if their base template is,
     35       but shouldn't have any vision range -->
     36  <Vision merge="">
     37    <Range>0</Range>
     38    <RevealShore>false</RevealShore>
     39  </Vision>
     40  <!-- Switch the actor to foundation mode -->
     41  <VisualActor>
     42    <Foundation/>
     43  </VisualActor>
     44</Entity>
  • new file inaries/data/mods/public/simulation/templates/special_filter/mirage.xml

    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
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity filtered="">
     3  <Footprint merge=""/>
     4  <Identity filtered="">
     5    <Civ merge=""/>
     6    <GenericName merge=""/>
     7    <SpecificName merge=""/>
     8    <Tooltip merge=""/>
     9    <History merge=""/>
     10    <Icon merge=""/>
     11  </Identity>
     12  <Minimap merge=""/>
     13  <Mirage replace=""/>
     14  <Obstruction merge="">
     15    <BlockMovement>false</BlockMovement>
     16    <BlockPathfinding>false</BlockPathfinding>
     17    <BlockFoundation>false</BlockFoundation>
     18    <BlockConstruction>false</BlockConstruction>
     19  </Obstruction>
     20  <Ownership merge=""/>
     21  <OverlayRenderer merge=""/>
     22  <Position merge=""/>
     23  <Selectable merge=""/>
     24  <StatusBars merge=""/>
     25  <Visibility merge=""/>
     26  <VisualActor merge=""/>
     27</Entity>
  • new file inaries/data/mods/public/simulation/templates/special_filter/preview.xml

    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
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity filtered="">
     3  <!--
     4       We only want to include components which are necessary (for the visual previewing of an entity)
     5       and safe (i.e. won't do anything that affects the synchronised simulation state), so additions
     6       to this list should be carefully considered.
     7       The Attack, Sound and UnitMotion components are needed for the Actor Viewer.
     8  -->
     9  <Attack merge=""/>
     10  <BuildRestrictions merge=""/>
     11  <Decay merge=""/>
     12  <Footprint merge=""/>
     13  <Identity merge=""/>
     14  <!-- Disable the Obstruction component (if there is one) so it doesn't affect pathfinding
     15       (but can still be used for testing this entity for collisions against others) -->
     16  <Obstruction merge="">
     17    <Active>false</Active>
     18  </Obstruction>
     19  <Ownership merge=""/>
     20  <Position merge=""/>
     21  <Sound merge=""/>
     22  <UnitMotion merge=""/>
     23  <!-- Previews should always be visible in fog-of-war/etc -->
     24  <Visibility>
     25    <AlwaysVisible>true</AlwaysVisible>
     26    <Preview>true</Preview>
     27  </Visibility>
     28  <!-- Previews should not cast shadows -->
     29  <VisualActor merge="">
     30    <DisableShadows/>
     31  </VisualActor>
     32</Entity>
  • new file inaries/data/mods/public/simulation/templates/special_filter/resource.xml

    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
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity filtered="">
     3  <AIProxy merge=""/>
     4  <Footprint merge=""/>
     5  <Identity merge=""/>
     6  <Minimap merge=""/>
     7  <!-- When dying, resources lose the unitMotion component, this causes them to have no clearance.
     8       Since unit obstructions no longer have a radius, this makes them unreachable in some cases (see #3530).
     9       Instead, create a static, unblocking (see #3530 for why) static obstruction.
     10       TODO: this should probably be generalized as a parameter on entity death or something.
     11  -->
     12  <Obstruction replace="">
     13    <Active>true</Active>
     14    <BlockMovement>false</BlockMovement>
     15    <BlockPathfinding>false</BlockPathfinding>
     16    <BlockFoundation>false</BlockFoundation>
     17    <BlockConstruction>false</BlockConstruction>
     18    <DisableBlockMovement>false</DisableBlockMovement>
     19    <DisableBlockPathfinding>false</DisableBlockPathfinding>
     20    <Static width="2.0" depth="2.0"/>
     21  </Obstruction>
     22  <OverlayRenderer merge=""/>
     23  <Ownership merge=""/>
     24  <Position merge=""/>
     25  <ResourceSupply merge=""/>
     26  <Selectable merge=""/>
     27  <StatusBars merge=""/>
     28  <VisualActor merge=""/>
     29</Entity>
  • source/ps/TemplateLoader.cpp

    diff --git a/source/ps/TemplateLoader.cpp b/source/ps/TemplateLoader.cpp
    index b9f7606..38edbe1 100644
    a b  
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    static const wchar_t ACTOR_ROOT[] = L"art/actors/";  
    2929
    3030static CParamNode NULL_NODE(false);
    3131
    32 
    3332bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int depth)
    3433{
    3534    // If this file was already loaded, we don't need to do anything
    bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int dept  
    5049        return true;
    5150    }
    5251
    53     // Handle special case "preview|foo"
    54     if (templateName.find("preview|") == 0)
     52    // Handle special case "bar|foo"
     53    size_t pos = templateName.find_first_of('|');
     54    if (pos != std::string::npos)
    5555    {
    56         // Load the base entity template, if it wasn't already loaded
    57         std::string baseName = templateName.substr(8);
    58         if (!LoadTemplateFile(baseName, depth+1))
    59         {
    60             LOGERROR("Failed to load entity template '%s'", baseName.c_str());
    61             return false;
    62         }
    63         // Copy a subset to the requested template
    64         CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], false);
    65         return true;
    66     }
     56        std::string prefix = templateName.substr(0, pos);
     57        std::string baseName = templateName.substr(pos+1);
    6758
    68     // Handle special case "corpse|foo"
    69     if (templateName.find("corpse|") == 0)
    70     {
    71         // Load the base entity template, if it wasn't already loaded
    72         std::string baseName = templateName.substr(7);
    7359        if (!LoadTemplateFile(baseName, depth+1))
    7460        {
    7561            LOGERROR("Failed to load entity template '%s'", baseName.c_str());
    7662            return false;
    7763        }
    78         // Copy a subset to the requested template
    79         CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], true);
    80         return true;
    81     }
    8264
    83     // Handle special case "mirage|foo"
    84     if (templateName.find("mirage|") == 0)
    85     {
    86         // Load the base entity template, if it wasn't already loaded
    87         std::string baseName = templateName.substr(7);
    88         if (!LoadTemplateFile(baseName, depth+1))
     65        VfsPath path = VfsPath(TEMPLATE_ROOT) / L"special_filter" / wstring_from_utf8(prefix + ".xml");
     66        if (!VfsFileExists(path))
    8967        {
    90             LOGERROR("Failed to load entity template '%s'", baseName.c_str());
     68            LOGERROR("Invalid subset '%s'", prefix.c_str());
    9169            return false;
    9270        }
    93         // Copy a subset to the requested template
    94         CopyMirageSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
    95         return true;
    96     }
    9771
    98     // Handle special case "foundation|foo"
    99     if (templateName.find("foundation|") == 0)
    100     {
    101         // Load the base entity template, if it wasn't already loaded
    102         std::string baseName = templateName.substr(11);
    103         if (!LoadTemplateFile(baseName, depth+1))
    104         {
    105             LOGERROR("Failed to load entity template '%s'", baseName.c_str());
    106             return false;
    107         }
    108         // Copy a subset to the requested template
    109         CopyFoundationSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
    110         return true;
    111     }
     72        CXeromyces xero;
     73        PSRETURN ok = xero.Load(g_VFS, path);
     74        if (ok != PSRETURN_OK)
     75            return false; // (Xeromyces already logged an error with the full filename)
    11276
    113     // Handle special case "construction|foo"
    114     if (templateName.find("construction|") == 0)
    115     {
    116         // Load the base entity template, if it wasn't already loaded
    117         std::string baseName = templateName.substr(13);
    118         if (!LoadTemplateFile(baseName, depth+1))
    119         {
    120             LOGERROR("Failed to load entity template '%s'", baseName.c_str());
    121             return false;
    122         }
    123         // Copy a subset to the requested template
    124         CopyConstructionSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
    125         return true;
    126     }
    127 
    128     // Handle special case "resource|foo"
    129     if (templateName.find("resource|") == 0)
    130     {
    131         // Load the base entity template, if it wasn't already loaded
    132         std::string baseName = templateName.substr(9);
    133         if (!LoadTemplateFile(baseName, depth+1))
    134         {
    135             LOGERROR("Failed to load entity template '%s'", baseName.c_str());
    136             return false;
    137         }
    138         // Copy a subset to the requested template
    139         CopyResourceSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
     77        m_TemplateFileData[templateName] = m_TemplateFileData[baseName];
     78        CParamNode::LoadXML(m_TemplateFileData[templateName], xero, path.string().c_str());
    14079        return true;
    14180    }
    14281
    bool CTemplateLoader::TemplateExists(const std::string& templateName) const  
    215154
    216155std::vector<std::string> CTemplateLoader::FindPlaceableTemplates(const std::string& path, bool includeSubdirectories, ETemplatesType templatesType, ScriptInterface& scriptInterface)
    217156{
     157    if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
     158    {
     159        LOGERROR("Undefined template type (valid: all, simulation, actor)");
     160        return std::vector<std::string>();
     161    }
     162
    218163    JSContext* cx = scriptInterface.GetContext();
    219164    JSAutoRequest rq(cx);
    220    
     165
    221166    std::vector<std::string> templates;
    222167    Status ok;
    223168    VfsPath templatePath;
    224169
    225 
    226170    if (templatesType == SIMULATION_TEMPLATES || templatesType == ALL_TEMPLATES)
    227171    {
    228172        JS::RootedValue placeablesFilter(cx);
    std::vector<std::string> CTemplateLoader::FindPlaceableTemplates(const std::stri  
    259203                std::wstring fileFilter;
    260204                scriptInterface.GetProperty(val, "directory", directoryPath);
    261205                scriptInterface.GetProperty(val, "file", fileFilter);
    262                
     206
    263207                VfsPaths filenames;
    264208                if (vfs::GetPathnames(g_VFS, templatePath / (directoryPath + "/"), fileFilter.c_str(), filenames) != INFO::OK)
    265209                    continue;
    266                
     210
    267211                for (const VfsPath& filename : filenames)
    268212                {
    269213                    // Strip the .xml extension
    std::vector<std::string> CTemplateLoader::FindPlaceableTemplates(const std::stri  
    273217
    274218                    templates.emplace_back(name.begin(), name.end());
    275219                }
    276                
     220
    277221            }
    278            
    279222        }
    280223    }
    281224
    std::vector<std::string> CTemplateLoader::FindPlaceableTemplates(const std::stri  
    289232        WARN_IF_ERR(ok);
    290233    }
    291234
    292     if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
    293         LOGERROR("Undefined template type (valid: all, simulation, actor)");
    294 
    295235    return templates;
    296236}
    297237
    std::vector<std::string> CTemplateLoader::FindTemplates(const std::string& path,  
    299239{
    300240    std::vector<std::string> templates;
    301241
    302     Status ok;
    303     VfsPath templatePath;
    304 
    305     if (templatesType == SIMULATION_TEMPLATES || templatesType == ALL_TEMPLATES)
    306     {
    307         templatePath = VfsPath(TEMPLATE_ROOT) / path;
    308         if (includeSubdirectories)
    309             ok = vfs::ForEachFile(g_VFS, templatePath, AddToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE);
    310         else
    311             ok = vfs::ForEachFile(g_VFS, templatePath, AddToTemplates, (uintptr_t)&templates, L"*.xml");
    312         WARN_IF_ERR(ok);
    313     }
    314     if (templatesType == ACTOR_TEMPLATES || templatesType == ALL_TEMPLATES)
     242    if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
    315243    {
    316         templatePath = VfsPath(ACTOR_ROOT) / path;
    317         if (includeSubdirectories)
    318             ok = vfs::ForEachFile(g_VFS, templatePath, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE);
    319         else
    320             ok = vfs::ForEachFile(g_VFS, templatePath, AddActorToTemplates, (uintptr_t)&templates, L"*.xml");
    321         WARN_IF_ERR(ok);
     244        LOGERROR("Undefined template type (valid: all, simulation, actor)");
     245        return templates;
    322246    }
    323247
    324     if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
    325         LOGERROR("Undefined template type (valid: all, simulation, actor)");
     248    size_t flags = includeSubdirectories ? vfs::DIR_RECURSIVE : 0;
     249
     250    if (templatesType == SIMULATION_TEMPLATES || templatesType == ALL_TEMPLATES)
     251        WARN_IF_ERR(vfs::ForEachFile(g_VFS, VfsPath(TEMPLATE_ROOT) / path, AddToTemplates, (uintptr_t)&templates, L"*.xml", flags));
     252
     253    if (templatesType == ACTOR_TEMPLATES || templatesType == ALL_TEMPLATES)
     254        WARN_IF_ERR(vfs::ForEachFile(g_VFS, VfsPath(ACTOR_ROOT) / path, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", flags));
    326255
    327256    return templates;
    328257}
    void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CPara  
    367296
    368297    CParamNode::LoadXMLString(out, xml.c_str(), actorNameW.c_str());
    369298}
    370 
    371 void CTemplateLoader::CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse)
    372 {
    373     // We only want to include components which are necessary (for the visual previewing of an entity)
    374     // and safe (i.e. won't do anything that affects the synchronised simulation state), so additions
    375     // to this list should be carefully considered
    376     std::set<std::string> permittedComponentTypes;
    377     permittedComponentTypes.insert("Identity");
    378     permittedComponentTypes.insert("Ownership");
    379     permittedComponentTypes.insert("Position");
    380     permittedComponentTypes.insert("Visibility");
    381     permittedComponentTypes.insert("VisualActor");
    382     permittedComponentTypes.insert("Footprint");
    383     permittedComponentTypes.insert("Obstruction");
    384     permittedComponentTypes.insert("Decay");
    385     permittedComponentTypes.insert("BuildRestrictions");
    386 
    387     // Need these for the Actor Viewer:
    388     permittedComponentTypes.insert("Attack");
    389     permittedComponentTypes.insert("UnitMotion");
    390     permittedComponentTypes.insert("Sound");
    391 
    392     // (This set could be initialised once and reused, but it's not worth the effort)
    393 
    394     CParamNode::LoadXMLString(out, "<Entity/>");
    395     out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
    396 
    397     // Disable the Obstruction component (if there is one) so it doesn't affect pathfinding
    398     // (but can still be used for testing this entity for collisions against others)
    399     if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
    400         CParamNode::LoadXMLString(out, "<Entity><Obstruction><Active>false</Active></Obstruction></Entity>");
    401 
    402     if (!corpse)
    403     {
    404         // Previews should not cast shadows
    405         if (out.GetChild("Entity").GetChild("VisualActor").IsOk())
    406             CParamNode::LoadXMLString(out, "<Entity><VisualActor><DisableShadows/></VisualActor></Entity>");
    407 
    408         // Previews should always be visible in fog-of-war/etc
    409         CParamNode::LoadXMLString(out, "<Entity><Visibility><AlwaysVisible>true</AlwaysVisible><Preview>true</Preview></Visibility></Entity>");
    410     }
    411 
    412     if (corpse)
    413     {
    414         // Corpses should include decay components and activate them
    415         if (out.GetChild("Entity").GetChild("Decay").IsOk())
    416             CParamNode::LoadXMLString(out, "<Entity><Decay><Active>true</Active></Decay></Entity>");
    417 
    418         // Corpses shouldn't display silhouettes (especially since they're often half underground)
    419         if (out.GetChild("Entity").GetChild("VisualActor").IsOk())
    420             CParamNode::LoadXMLString(out, "<Entity><VisualActor><SilhouetteDisplay>false</SilhouetteDisplay></VisualActor></Entity>");
    421 
    422         // Corpses should remain visible in fog-of-war (for the owner only)
    423         CParamNode::LoadXMLString(out, "<Entity><Visibility><Corpse>true</Corpse></Visibility></Entity>");
    424     }
    425 }
    426 
    427 void CTemplateLoader::CopyMirageSubset(CParamNode& out, const CParamNode& in)
    428 {
    429     // Currently used for mirage entities replacing real ones in fog-of-war
    430 
    431     std::set<std::string> permittedComponentTypes;
    432     permittedComponentTypes.insert("Footprint");
    433     permittedComponentTypes.insert("Minimap");
    434     permittedComponentTypes.insert("Obstruction");
    435     permittedComponentTypes.insert("Ownership");
    436     permittedComponentTypes.insert("OverlayRenderer");
    437     permittedComponentTypes.insert("Position");
    438     permittedComponentTypes.insert("Selectable");
    439     permittedComponentTypes.insert("StatusBars");
    440     permittedComponentTypes.insert("Visibility");
    441     permittedComponentTypes.insert("VisualActor");
    442 
    443     CParamNode::LoadXMLString(out, "<Entity/>");
    444     out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
    445 
    446     // Select a subset of identity data. We don't want to have, for example, a CC mirage
    447     // that has also the CC class and then prevents construction of other CCs
    448     std::set<std::string> identitySubset;
    449     identitySubset.insert("Civ");
    450     identitySubset.insert("GenericName");
    451     identitySubset.insert("SpecificName");
    452     identitySubset.insert("Tooltip");
    453     identitySubset.insert("History");
    454     identitySubset.insert("Icon");
    455     CParamNode identity;
    456     CParamNode::LoadXMLString(identity, "<Identity/>");
    457     identity.CopyFilteredChildrenOfChild(in.GetChild("Entity"), "Identity", identitySubset);
    458     CParamNode::LoadXMLString(out, ("<Entity>"+utf8_from_wstring(identity.ToXML())+"</Entity>").c_str());
    459 
    460     // Mirages obstruction shouldn't block anything
    461     if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
    462         CParamNode::LoadXMLString(out, "<Entity><Obstruction><BlockMovement>false</BlockMovement><BlockPathfinding>false</BlockPathfinding><BlockFoundation>false</BlockFoundation><BlockConstruction>false</BlockConstruction></Obstruction></Entity>");
    463 
    464     // Set the entity as mirage entity
    465     CParamNode::LoadXMLString(out, "<Entity><Mirage/></Entity>");
    466 }
    467 
    468 void CTemplateLoader::CopyFoundationSubset(CParamNode& out, const CParamNode& in)
    469 {
    470     // TODO: this is all kind of yucky and hard-coded; it'd be nice to have a more generic
    471     // extensible scriptable way to define these subsets
    472 
    473     std::set<std::string> permittedComponentTypes;
    474     permittedComponentTypes.insert("Ownership");
    475     permittedComponentTypes.insert("Position");
    476     permittedComponentTypes.insert("VisualActor");
    477     permittedComponentTypes.insert("Identity");
    478     permittedComponentTypes.insert("BuildRestrictions");
    479     permittedComponentTypes.insert("Obstruction");
    480     permittedComponentTypes.insert("Selectable");
    481     permittedComponentTypes.insert("Footprint");
    482     permittedComponentTypes.insert("Fogging");
    483     permittedComponentTypes.insert("Armour");
    484     permittedComponentTypes.insert("Health");
    485     permittedComponentTypes.insert("Market");
    486     permittedComponentTypes.insert("StatusBars");
    487     permittedComponentTypes.insert("OverlayRenderer");
    488     permittedComponentTypes.insert("Decay");
    489     permittedComponentTypes.insert("Cost");
    490     permittedComponentTypes.insert("Sound");
    491     permittedComponentTypes.insert("Visibility");
    492     permittedComponentTypes.insert("Vision");
    493     permittedComponentTypes.insert("AIProxy");
    494     permittedComponentTypes.insert("RallyPoint");
    495     permittedComponentTypes.insert("RallyPointRenderer");
    496 
    497     CParamNode::LoadXMLString(out, "<Entity/>");
    498     out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
    499 
    500     // Switch the actor to foundation mode
    501     CParamNode::LoadXMLString(out, "<Entity><VisualActor><Foundation/></VisualActor></Entity>");
    502 
    503     // Add the Foundation component, to deal with the construction process
    504     CParamNode::LoadXMLString(out, "<Entity><Foundation/></Entity>");
    505 
    506     // Initialise health to 1
    507     CParamNode::LoadXMLString(out, "<Entity><Health><Initial>1</Initial></Health></Entity>");
    508 
    509     // Foundations shouldn't initially block unit movement
    510     if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
    511         CParamNode::LoadXMLString(out, "<Entity><Obstruction><DisableBlockMovement>true</DisableBlockMovement><DisableBlockPathfinding>true</DisableBlockPathfinding></Obstruction></Entity>");
    512 
    513     // Don't provide population bonuses yet (but still do take up population cost)
    514     if (out.GetChild("Entity").GetChild("Cost").IsOk())
    515         CParamNode::LoadXMLString(out, "<Entity><Cost><PopulationBonus>0</PopulationBonus></Cost></Entity>");
    516 
    517     // Foundations should be visible themselves in fog-of-war if their base template is,
    518     // but shouldn't have any vision range
    519     if (out.GetChild("Entity").GetChild("Vision").IsOk())
    520     {
    521         CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range></Vision></Entity>");
    522         // Foundations should not have special vision capabilities either
    523         if (out.GetChild("Entity").GetChild("Vision").GetChild("RevealShore").IsOk())
    524             CParamNode::LoadXMLString(out, "<Entity><Vision><RevealShore>false</RevealShore></Vision></Entity>");
    525     }
    526 }
    527 
    528 void CTemplateLoader::CopyConstructionSubset(CParamNode& out, const CParamNode& in)
    529 {
    530     // Currently used for buildings rising during construction
    531     // Mostly serves to filter out components like Vision, UnitAI, etc.
    532     std::set<std::string> permittedComponentTypes;
    533     permittedComponentTypes.insert("Footprint");
    534     permittedComponentTypes.insert("Ownership");
    535     permittedComponentTypes.insert("Position");
    536     permittedComponentTypes.insert("VisualActor");
    537 
    538     CParamNode::LoadXMLString(out, "<Entity/>");
    539     out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
    540 }
    541 
    542 void CTemplateLoader::CopyResourceSubset(CParamNode& out, const CParamNode& in)
    543 {
    544     // Currently used for animals which die and leave a gatherable corpse.
    545     // Mostly serves to filter out components like Vision, UnitAI, etc.
    546     // Don't emit sound as our samples only apply to living animals.
    547     std::set<std::string> permittedComponentTypes;
    548     permittedComponentTypes.insert("Ownership");
    549     permittedComponentTypes.insert("Position");
    550     permittedComponentTypes.insert("VisualActor");
    551     permittedComponentTypes.insert("Identity");
    552     permittedComponentTypes.insert("Minimap");
    553     permittedComponentTypes.insert("ResourceSupply");
    554     permittedComponentTypes.insert("Selectable");
    555     permittedComponentTypes.insert("Footprint");
    556     permittedComponentTypes.insert("StatusBars");
    557     permittedComponentTypes.insert("OverlayRenderer");
    558     permittedComponentTypes.insert("AIProxy");
    559 
    560     CParamNode::LoadXMLString(out, "<Entity/>");
    561     out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
    562    
    563     // When dying, resources lose the unitMotion component
    564     // This causes them to have no clearance. Since unit obstructions no longer have a radius,
    565     // this makes them unreachable in some cases (see #3530).
    566     // Instead, create a static, unblocking (see #3530 for why) static obstruction.
    567     // TODO: this should probably be generalized as a parameter on entity death or something.
    568     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>");
    569 }
  • source/ps/TemplateLoader.h

    diff --git a/source/ps/TemplateLoader.h b/source/ps/TemplateLoader.h
    index c34d303..d2a6514 100644
    a b  
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    public:  
    4848    CTemplateLoader()
    4949    {
    5050    }
    51    
     51
    5252    /**
    5353     * Provides the file data for requested template.
    5454     */
    private:  
    8181     */
    8282    void ConstructTemplateActor(const std::string& actorName, CParamNode& out);
    8383
    84     /**
    85      * Copy the non-interactive components of an entity template (position, actor, etc) into
    86      * a new entity template
    87      */
    88     void CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse);
    89 
    90     /**
    91      * Copy the components of an entity template necessary for a fogged "mirage"
    92      * entity (position, actor) into a new entity template
    93      */
    94     void CopyMirageSubset(CParamNode& out, const CParamNode& in);
    95 
    96     /**
    97      * Copy the components of an entity template necessary for a construction foundation
    98      * (position, actor, armour, health, etc) into a new entity template
    99      */
    100     void CopyFoundationSubset(CParamNode& out, const CParamNode& in);
    101 
    102     /**
    103      * Copy the components of an entity template necessary for a non-foundation construction entity
    104      * into a new entity template
    105      */
    106     void CopyConstructionSubset(CParamNode& out, const CParamNode& in);
    107 
    108     /**
    109      * Copy the components of an entity template necessary for a gatherable resource
    110      * into a new entity template
    111      */
    112     void CopyResourceSubset(CParamNode& out, const CParamNode& in);
    113 
    11484    /**
    11585     * Map from template name (XML filename or special |-separated string) to the most recently
    11686     * loaded non-broken template data. This includes files that will fail schema validation.
  • source/simulation2/components/tests/test_scripts.h

    diff --git a/source/simulation2/components/tests/test_scripts.h b/source/simulation2/components/tests/test_scripts.h
    index 5cf8c2a..29014b4 100644
    a b public:  
    5959    void test_scripts()
    6060    {
    6161        if (!VfsFileExists(L"simulation/components/tests/setup.js"))
    62         {
    63             debug_printf("WARNING: Skipping component scripts tests (can't find binaries/data/mods/public/simulation/components/tests/setup.js)\n");
    6462            return;
    65         }
    6663
    6764        VfsPaths paths;
    6865        TS_ASSERT_OK(vfs::GetPathnames(g_VFS, L"simulation/components/tests/", L"test_*.js", paths));
  • source/simulation2/system/ParamNode.cpp

    diff --git a/source/simulation2/system/ParamNode.cpp b/source/simulation2/system/ParamNode.cpp
    index 173117b..89ba11d 100644
    a b  
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const  
    7676    // Look for special attributes
    7777    int at_disable = xmb.GetAttributeID("disable");
    7878    int at_replace = xmb.GetAttributeID("replace");
     79    int at_filtered = xmb.GetAttributeID("filtered");
     80    int at_merge = xmb.GetAttributeID("merge");
    7981    int at_op = xmb.GetAttributeID("op");
    8082    int at_datatype = xmb.GetAttributeID("datatype");
    8183    enum op {
    void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const  
    8486        MUL
    8587    } op = INVALID;
    8688    bool replacing = false;
     89    bool filtering = false;
     90    bool merging = false;
    8791    {
    8892        XERO_ITER_ATTR(element, attr)
    8993        {
    void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const  
    97101                m_Childs.erase(name);
    98102                replacing = true;
    99103            }
     104            else if (attr.Name == at_filtered)
     105            {
     106                filtering = true;
     107            }
     108            else if (attr.Name == at_merge)
     109            {
     110                if (m_Childs.find(name) == m_Childs.end())
     111                    return;
     112                merging = true;
     113            }
    100114            else if (attr.Name == at_op)
    101115            {
    102116                if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"add")
    void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const  
    168182        }
    169183        hasSetValue = true;
    170184    }
    171     if (!hasSetValue)
     185    if (!hasSetValue && !merging)
    172186        node.m_Value = value;
    173187
     188    // For the filtered case
     189    ChildrenMap childs;
     190
    174191    // Recurse through the element's children
    175192    XERO_ITER_EL(element, child)
    176193    {
    177194        node.ApplyLayer(xmb, child, sourceIdentifier);
     195        if (filtering)
     196        {
     197            std::string childname = xmb.GetElementString(child.GetNodeName());
     198            if (node.m_Childs.find(childname) != node.m_Childs.end())
     199                childs[childname] = std::move(node.m_Childs[childname]);
     200        }
    178201    }
    179202
     203    if (filtering)
     204        node.m_Childs.swap(childs);
     205
    180206    // Add the element's attributes, prefixing names with "@"
    181207    XERO_ITER_ATTR(element, attr)
    182208    {
    183209        // Skip special attributes
    184         if (attr.Name == at_replace || attr.Name == at_op)
     210        if (attr.Name == at_replace || attr.Name == at_op || attr.Name == at_merge || attr.Name == at_filtered)
    185211            continue;
    186212        // Add any others
    187213        std::string attrName = xmb.GetAttributeString(attr.Name);
    void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const  
    189215    }
    190216}
    191217
    192 void CParamNode::CopyFilteredChildrenOfChild(const CParamNode& src, const char* name, const std::set<std::string>& permitted)
    193 {
    194     ResetScriptVal();
    195 
    196     ChildrenMap::iterator dstChild = m_Childs.find(name);
    197     ChildrenMap::const_iterator srcChild = src.m_Childs.find(name);
    198     if (dstChild == m_Childs.end() || srcChild == src.m_Childs.end())
    199         return; // error
    200 
    201     ChildrenMap::const_iterator it = srcChild->second.m_Childs.begin();
    202     for (; it != srcChild->second.m_Childs.end(); ++it)
    203         if (permitted.count(it->first))
    204             dstChild->second.m_Childs[it->first] = it->second;
    205 }
    206 
    207218const CParamNode& CParamNode::GetChild(const char* name) const
    208219{
    209220    ChildrenMap::const_iterator it = m_Childs.find(name);
  • source/simulation2/system/ParamNode.h

    diff --git a/source/simulation2/system/ParamNode.h b/source/simulation2/system/ParamNode.h
    index 4fc911f..0b18e5e 100644
    a b  
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    class XMBElement;  
    5858 *   <Example4 datatype="tokens">
    5959 *     one two three
    6060 *   </Example4>
     61 *   <Example5>
     62 *     <E/>
     63 *     <F>
     64 *       <I>test</I>
     65 *     </F>
     66 *     <H>
     67 *       <J>example</J>
     68 *     </H>
     69 *   </Example5>
    6170 * </Entity>
    6271 * @endcode
    6372 * then a second like:
    class XMBElement;  
    7584 *     four             <!-- add a token to the parent's set -->
    7685 *     -two             <!-- remove a token from the parent's set -->
    7786 *   </Example4>
     87 *   <Example5 filtered=""> <!-- drop all children of this node that are not in this file -->
     88 *     <F merge="">  <!-- only add this element if it is also present in the parent -->
     89 *       <K>example</K> <!-- if F is present merge its children normally -->
     90 *     </F>
     91 *     <G merge=""/>  <!-- keep the G element of the parent if it exists -->
     92 *     <H>
     93 *       <J>text</J>
     94 *     </H>
     95 *   </Example5>
    7896 * </Entity>
    7997 * @endcode
    8098 * is equivalent to loading a single file like:
    class XMBElement;  
    90108 *   <Example4>
    91109 *     one three four
    92110 *   </Example4>
     111 *   <Example5>
     112 *     <F>
     113 *       <I>test</I>
     114 *       <K>example</K>
     115 *     </F>
     116 *     <H>
     117 *       <J>text</J>
     118 *     </H>
     119 *   </Example5>
    93120 * </Entity>
    94121 * @endcode
    95122 *
    class XMBElement;  
    103130 *     "Example3": {
    104131 *       "D": "new"
    105132 *     },
    106  *     "Example4": { "@datatype": "tokens", "_string": "one three four" }
     133 *     "Example4": { "@datatype": "tokens", "_string": "one three four" },
     134 *     "Example5": {
     135 *       "F": {
     136 *         "I": "test",
     137 *         "K": "example"
     138 *       },
     139 *       "H": {
     140 *         "J": "text"
     141 *       }
     142 *     }
    107143 *   }
    108144 * }
    109145 * @endcode
    public:  
    146182    static PSRETURN LoadXMLString(CParamNode& ret, const char* xml, const wchar_t* sourceIdentifier = NULL);
    147183
    148184    /**
    149      * Finds the childs named @a name from @a src and from @a this, and copies the source child's children
    150      * which are in the @a permitted set into this node's child.
    151      * Intended for use as a filtered clone of XML files.
    152      * @a this and @a src must have childs named @a name.
    153      */
    154     void CopyFilteredChildrenOfChild(const CParamNode& src, const char* name, const std::set<std::string>& permitted);
    155 
    156     /**
    157185     * Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none.
    158186     */
    159187    const CParamNode& GetChild(const char* name) const;
  • source/simulation2/tests/test_CmpTemplateManager.h

    diff --git a/source/simulation2/tests/test_CmpTemplateManager.h b/source/simulation2/tests/test_CmpTemplateManager.h
    index 8103254..0fdc180 100644
    a b  
    3131#include "ps/CLogger.h"
    3232#include "ps/XML/Xeromyces.h"
    3333
     34static const char SPECIAL_FILTER[] = "simulation/templates/special_filter/";
     35
    3436class TestCmpTemplateManager : public CxxTest::TestSuite
    3537{
    3638public:
    public:  
    3941        g_VFS = CreateVfs(20 * MiB);
    4042        TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"_test.sim", VFS_MOUNT_MUST_EXIST));
    4143        TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
     44
     45        // If the public mod exists, test the special_filter templates too
     46        g_VFS->Mount(SPECIAL_FILTER, DataDir() / "mods" / "public" / SPECIAL_FILTER);
     47
    4248        CXeromyces::Startup();
    4349    }
    4450
    public:  
    117123        ScriptInterface::ToJSVal(cx, &val, &actor->GetChild("VisualActor"));
    118124        TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({Actor:\"example1\", ActorOnly:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})");
    119125
    120         const CParamNode* foundation = tempMan->LoadTemplate(ent2, "foundation|actor|example1", -1);
    121         ScriptInterface::ToJSVal(cx, &val, &foundation->GetChild("VisualActor"));
    122         TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({Actor:\"example1\", ActorOnly:(void 0), Foundation:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})");
     126        if (VfsDirectoryExists(SPECIAL_FILTER))
     127        {
     128            const CParamNode* foundation = tempMan->LoadTemplate(ent2, "foundation|actor|example1", -1);
     129            ScriptInterface::ToJSVal(cx, &val, &foundation->GetChild("VisualActor"));
     130
     131            TS_ASSERT_STR_EQUALS(
     132                man.GetScriptInterface().ToString(&val),
     133                "({"
     134                    "Actor:\"example1\", "
     135                    "ActorOnly:(void 0), "
     136                    "Foundation:(void 0), "
     137                    "SilhouetteDisplay:\"false\", "
     138                    "SilhouetteOccluder:\"false\", "
     139                    "VisibleInAtlasOnly:\"false\""
     140                "})"
     141            );
     142        }
    123143    }
    124144
    125145    void test_LoadTemplate_errors()
  • source/simulation2/tests/test_ParamNode.h

    diff --git a/source/simulation2/tests/test_ParamNode.h b/source/simulation2/tests/test_ParamNode.h
    index 043554b..83e11b9 100644
    a b  
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    public:  
    135135        TS_ASSERT_WSTR_EQUALS(node.ToXML(), L"<test><a datatype=\"tokens\">Y X</a></test>");
    136136    }
    137137
     138    void test_overlay_filtered()
     139    {
     140        CParamNode node;
     141        TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
     142            "<test>"
     143                "<a>"
     144                    "<b/>"
     145                "</a>"
     146                "<c>toberemoved</c>"
     147                "<d>"
     148                    "<e/>"
     149                "</d>"
     150            "</test>"), PSRETURN_OK);
     151
     152        TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
     153            "<test filtered=\"\">"
     154                "<a/>"
     155                "<d>"
     156                    "<f/>"
     157                "</d>"
     158                "<g/>"
     159            "</test>"), PSRETURN_OK);
     160
     161        TS_ASSERT_WSTR_EQUALS(node.ToXML(),
     162            L"<test>"
     163                "<a>"
     164                    "<b></b>"
     165                "</a>"
     166                "<d>"
     167                    "<e></e>"
     168                    "<f></f>"
     169                "</d>"
     170                "<g></g>"
     171            "</test>");
     172    }
     173
     174    void test_overlay_filtered_and_merge()
     175    {
     176        CParamNode node;
     177        TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
     178            "<test>"
     179                "<a>"
     180                    "<b>b</b>"
     181                    "<c>c</c>"
     182                    "<d>d</d>"
     183                    "<e>e</e>"
     184                "</a>"
     185                "<f/>"
     186            "</test>"), PSRETURN_OK);
     187
     188        TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
     189            "<test filtered=\"\">"
     190                "<a filtered=\"\">"
     191                    "<b merge=\"\"/>"
     192                    "<c>c2</c>"
     193                    "<d/>"
     194                "</a>"
     195            "</test>"), PSRETURN_OK);
     196
     197        TS_ASSERT_WSTR_EQUALS(node.ToXML(),
     198            L"<test>"
     199                "<a>"
     200                    "<b>b</b>"
     201                    "<c>c2</c>"
     202                    "<d></d>"
     203                "</a>"
     204            "</test>");
     205    }
     206
     207    void test_overlay_merge()
     208    {
     209        CParamNode node;
     210        TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
     211            "<test>"
     212                "<a>"
     213                    "<b>foo</b>"
     214                    "<c>bar</c>"
     215                "</a>"
     216                "<x>"
     217                    "<y>"
     218                        "<z>foo</z>"
     219                    "</y>"
     220                "</x>"
     221            "</test>"), PSRETURN_OK);
     222
     223        TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
     224            "<test>"
     225                "<a merge=\"\">"
     226                    "<b>test</b>"
     227                    "<d>baz</d>"
     228                "</a>"
     229                "<i merge=\"\">"
     230                    "<j>willnotbeincluded</j>"
     231                "</i>"
     232                "<x merge=\"\">"
     233                    "<y merge=\"\">"
     234                        "<v>text</v>"
     235                    "</y>"
     236                    "<w>more text</w>"
     237                "</x>"
     238            "</test>"), PSRETURN_OK);
     239
     240        TS_ASSERT_WSTR_EQUALS(node.ToXML(),
     241            L"<test>"
     242                "<a>"
     243                    "<b>test</b>"
     244                    "<c>bar</c>"
     245                    "<d>baz</d>"
     246                "</a>"
     247                "<x>"
     248                    "<w>more text</w>"
     249                    "<y>"
     250                        "<v>text</v>"
     251                        "<z>foo</z>"
     252                    "</y>"
     253                "</x>"
     254            "</test>");
     255    }
     256
     257    void test_overlay_filtered_merge()
     258    {
     259        CParamNode node;
     260        TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
     261            "<test>"
     262                "<a>"
     263                    "<b/>"
     264                "</a>"
     265                "<c>"
     266                    "<x/>"
     267                "</c>"
     268                "<Health>"
     269                    "<Max>1200</Max>"
     270                "</Health>"
     271            "</test>"), PSRETURN_OK);
     272
     273        TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node,
     274            "<test filtered=\"\">"
     275                "<c merge=\"\"/>"
     276                "<d>bar</d>"
     277                "<e merge=\"\"/>"
     278                "<Health>"
     279                    "<Initial>1</Initial>"
     280                "</Health>"
     281            "</test>"), PSRETURN_OK);
     282
     283        TS_ASSERT_WSTR_EQUALS(node.ToXML(),
     284                L"<test>"
     285                "<Health>"
     286                    "<Initial>1</Initial>"
     287                    "<Max>1200</Max>"
     288                "</Health>"
     289                "<c>"
     290                    "<x></x>"
     291                "</c>"
     292                "<d>bar</d>"
     293            "</test>");
     294    }
     295
    138296    void test_types()
    139297    {
    140298        CParamNode node;