Ticket #2951: template_subsets.diff

File template_subsets.diff, 36.5 KB (added by leper, 8 years ago)

Done.

  • new file inaries/data/mods/_test.sim/simulation/templates/special_filter/foundation.xml

    From 2d06008d6b32499a28a547abd39946ff1b9fd83f Mon Sep 17 00:00:00 2001
    From: leper <leper@wildfiregames.com>
    Date: Thu, 25 Feb 2016 15:47:31 +0100
    Subject: [PATCH] Make special templates (foo|templatename) (apart from actor|)
     more moddable. Fixes #2951.
    
    This moves template additions to xml files.
    Permitted components are now done via filtered and merge.
    For adding new nodes while ignoring possible previous values use replace.
    
    Some explanation on how to use the new filter templates:
     * Previously permitted component types should be replaced by
       <Foo merge=""/>, which does not add the <Foo> node if it wasn't present.
     * To actually remove the other components the encompassing node should
       specify filtered="", which will remove all nodes which are present in the
       original, but not in the filter.
     * Additions that should be performed while ignoring a possible original
       node should specify replace="".
     * To add something to an existing node (or add that node if it does not exist)
       just use <Foo>...</Foo>.
    ---
     .../templates/special_filter/foundation.xml        |  45 +++
     .../templates/special_filter/construction.xml      |   7 +
     .../simulation/templates/special_filter/corpse.xml |  31 ++
     .../templates/special_filter/foundation.xml        |  45 +++
     .../simulation/templates/special_filter/mirage.xml |  21 ++
     .../templates/special_filter/preview.xml           |  29 ++
     .../templates/special_filter/resource.xml          |  29 ++
     source/ps/TemplateLoader.cpp                       | 317 ++-------------------
     source/ps/TemplateLoader.h                         |  34 +--
     source/simulation2/system/ParamNode.cpp            |  27 +-
     source/simulation2/system/ParamNode.h              |  40 ++-
     source/simulation2/tests/test_ParamNode.h          |  18 +-
     12 files changed, 320 insertions(+), 323 deletions(-)
     create mode 100644 binaries/data/mods/_test.sim/simulation/templates/special_filter/foundation.xml
     create mode 100644 binaries/data/mods/public/simulation/templates/special_filter/construction.xml
     create mode 100644 binaries/data/mods/public/simulation/templates/special_filter/corpse.xml
     create mode 100644 binaries/data/mods/public/simulation/templates/special_filter/foundation.xml
     create mode 100644 binaries/data/mods/public/simulation/templates/special_filter/mirage.xml
     create mode 100644 binaries/data/mods/public/simulation/templates/special_filter/preview.xml
     create mode 100644 binaries/data/mods/public/simulation/templates/special_filter/resource.xml
    
    diff --git a/binaries/data/mods/_test.sim/simulation/templates/special_filter/foundation.xml b/binaries/data/mods/_test.sim/simulation/templates/special_filter/foundation.xml
    new file mode 100644
    index 0000000..e294934
    - +  
     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  <!-- Initialise health to 1 -->
     12  <Health>
     13    <Initial>1</Initial>
     14  </Health>
     15  <Fogging merge=""/>
     16  <Footprint merge=""/>
     17  <!-- Add the Foundation component, to deal with the construction process -->
     18  <Foundation replace=""/>
     19  <Health merge=""/>
     20  <Identity merge=""/>
     21  <!-- Foundations shouldn't initially block unit movement -->
     22  <Obstruction merge="">
     23    <DisableBlockMovement>true</DisableBlockMovement>
     24    <DisableBlockPathfinding>true</DisableBlockPathfinding>
     25  </Obstruction>
     26  <OverlayRenderer merge=""/>
     27  <Ownership merge=""/>
     28  <Position merge=""/>
     29  <RallyPoint merge=""/>
     30  <RallyPointRenderer merge=""/>
     31  <Selectable merge=""/>
     32  <Sound merge=""/>
     33  <StatusBars merge=""/>
     34  <Visibility merge=""/>
     35  <!-- Foundations should be visible themselves in fog-of-war if their base template is,
     36       but shouldn't have any vision range -->
     37  <Vision merge="">
     38    <Range>0</Range>
     39    <RevealShore merge="">false</RevealShore>
     40  </Vision>
     41  <!-- Switch the actor to foundation mode -->
     42  <VisualActor>
     43    <Foundation/>
     44  </VisualActor>
     45</Entity>
  • 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..fe75d3f
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity filtered="">
     3  <!-- We only want to include components which are necessary (for the visual previewing of an entity)
     4       and safe (i.e. won't do anything that affects the synchronised simulation state), so additions
     5       to this list should be carefully considered -->
     6  <Attack merge=""/> <!-- Needed for the Actor Viewer -->
     7  <BuildRestrictions merge=""/>
     8  <!-- Corpses should include decay components and activate them -->
     9  <Decay merge="">
     10    <Active>true</Active>
     11  </Decay>
     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=""/> <!-- Needed for the Actor Viewer -->
     22  <UnitMotion merge=""/> <!-- Needed for the Actor Viewer -->
     23  <!-- Corpses should remain visible in fog-of-war (for the owner only) -->
     24  <Visibility>
     25    <Corpse>true</Corpse>
     26  </Visibility>
     27  <!-- Corpses shouldn't display silhouettes (especially since they're often half underground) -->
     28  <VisualActor merge="">
     29    <SilhouetteDisplay>false</SilhouetteDisplay>
     30  </VisualActor>
     31</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..e294934
    - +  
     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  <!-- Initialise health to 1 -->
     12  <Health>
     13    <Initial>1</Initial>
     14  </Health>
     15  <Fogging merge=""/>
     16  <Footprint merge=""/>
     17  <!-- Add the Foundation component, to deal with the construction process -->
     18  <Foundation replace=""/>
     19  <Health merge=""/>
     20  <Identity merge=""/>
     21  <!-- Foundations shouldn't initially block unit movement -->
     22  <Obstruction merge="">
     23    <DisableBlockMovement>true</DisableBlockMovement>
     24    <DisableBlockPathfinding>true</DisableBlockPathfinding>
     25  </Obstruction>
     26  <OverlayRenderer merge=""/>
     27  <Ownership merge=""/>
     28  <Position merge=""/>
     29  <RallyPoint merge=""/>
     30  <RallyPointRenderer merge=""/>
     31  <Selectable merge=""/>
     32  <Sound merge=""/>
     33  <StatusBars merge=""/>
     34  <Visibility merge=""/>
     35  <!-- Foundations should be visible themselves in fog-of-war if their base template is,
     36       but shouldn't have any vision range -->
     37  <Vision merge="">
     38    <Range>0</Range>
     39    <RevealShore merge="">false</RevealShore>
     40  </Vision>
     41  <!-- Switch the actor to foundation mode -->
     42  <VisualActor>
     43    <Foundation/>
     44  </VisualActor>
     45</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..565456d
    - +  
     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  <Ownership merge=""/>
     15  <OverlayRenderer merge=""/>
     16  <Position merge=""/>
     17  <Selectable merge=""/>
     18  <StatusBars merge=""/>
     19  <Visibility merge=""/>
     20  <VisualActor merge=""/>
     21</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..9ae9cf9
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity filtered="">
     3  <!-- We only want to include components which are necessary (for the visual previewing of an entity)
     4       and safe (i.e. won't do anything that affects the synchronised simulation state), so additions
     5       to this list should be carefully considered -->
     6  <Attack merge=""/> <!-- Needed for the Actor Viewer -->
     7  <BuildRestrictions merge=""/>
     8  <Decay merge=""/>
     9  <Footprint merge=""/>
     10  <Identity merge=""/>
     11  <!-- Disable the Obstruction component (if there is one) so it doesn't affect pathfinding
     12       (but can still be used for testing this entity for collisions against others) -->
     13  <Obstruction merge="">
     14    <Active>false</Active>
     15  </Obstruction>
     16  <Ownership merge=""/>
     17  <Position merge=""/>
     18  <Sound merge=""/> <!-- Needed for the Actor Viewer -->
     19  <UnitMotion merge=""/> <!-- Needed for the Actor Viewer -->
     20  <!-- Previews should always be visible in fog-of-war/etc -->
     21  <Visibility>
     22    <AlwaysVisible>true</AlwaysVisible>
     23    <Preview>true</Preview>
     24  </Visibility>
     25  <!-- Previews should not cast shadows -->
     26  <VisualActor merge="">
     27    <DisableShadows/>
     28  </VisualActor>
     29</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 a9d067b..88701ac 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);
     56        std::string prefix = templateName.substr(0, pos);
     57        std::string baseName = templateName.substr(pos+1);
     58       
    5859        if (!LoadTemplateFile(baseName, depth+1))
    5960        {
    6061            LOGERROR("Failed to load entity template '%s'", baseName.c_str());
    6162            return false;
    6263        }
    63         // Copy a subset to the requested template
    64         CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], false);
    65         return true;
    66     }
    6764
    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);
    73         if (!LoadTemplateFile(baseName, depth+1))
     65        VfsPath path = VfsPath(TEMPLATE_ROOT) / L"special_filter" / wstring_from_utf8(prefix + ".xml");
     66        if (!VfsFileExists(path))
    7467        {
    75             LOGERROR("Failed to load entity template '%s'", baseName.c_str());
     68            LOGERROR("Invalid subset '%s'", prefix.c_str());
    7669            return false;
    7770        }
    78         // Copy a subset to the requested template
    79         CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], true);
    80         return true;
    81     }
    8271
    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))
    89         {
    90             LOGERROR("Failed to load entity template '%s'", baseName.c_str());
    91             return false;
    92         }
    93         // Copy a subset to the requested template
    94         CopyMirageSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
    95         return true;
    96     }
     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)
    9776
    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     }
    112 
    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);
    220165   
    std::vector<std::string> CTemplateLoader::FindPlaceableTemplates(const std::stri  
    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  
    289233        WARN_IF_ERR(ok);
    290234    }
    291235
    292     if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
    293         LOGERROR("Undefined template type (valid: all, simulation, actor)");
    294 
    295236    return templates;
    296237}
    297238
    std::vector<std::string> CTemplateLoader::FindTemplates(const std::string& path,  
    299240{
    300241    std::vector<std::string> templates;
    301242
     243    if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
     244    {
     245        LOGERROR("Undefined template type (valid: all, simulation, actor)");
     246        return templates;
     247    }
     248
    302249    Status ok;
    303250    VfsPath templatePath;
     251    size_t flags = includeSubdirectories ? vfs::DIR_RECURSIVE : 0;
    304252
    305253    if (templatesType == SIMULATION_TEMPLATES || templatesType == ALL_TEMPLATES)
    306254    {
    307255        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");
     256        ok = vfs::ForEachFile(g_VFS, templatePath, AddToTemplates, (uintptr_t)&templates, L"*.xml", flags);
    312257        WARN_IF_ERR(ok);
    313258    }
    314259    if (templatesType == ACTOR_TEMPLATES || templatesType == ALL_TEMPLATES)
    315260    {
    316261        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");
     262        ok = vfs::ForEachFile(g_VFS, templatePath, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", flags);
    321263        WARN_IF_ERR(ok);
    322264    }
    323265
    324     if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES)
    325         LOGERROR("Undefined template type (valid: all, simulation, actor)");
    326 
    327266    return templates;
    328267}
    329268
    void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CPara  
    367306
    368307    CParamNode::LoadXMLString(out, xml.c_str(), actorNameW.c_str());
    369308}
    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("Ownership");
    435     permittedComponentTypes.insert("OverlayRenderer");
    436     permittedComponentTypes.insert("Position");
    437     permittedComponentTypes.insert("Selectable");
    438     permittedComponentTypes.insert("StatusBars");
    439     permittedComponentTypes.insert("Visibility");
    440     permittedComponentTypes.insert("VisualActor");
    441 
    442     CParamNode::LoadXMLString(out, "<Entity/>");
    443     out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
    444 
    445     // Select a subset of identity data. We don't want to have, for example, a CC mirage
    446     // that has also the CC class and then prevents construction of other CCs
    447     std::set<std::string> identitySubset;
    448     identitySubset.insert("Civ");
    449     identitySubset.insert("GenericName");
    450     identitySubset.insert("SpecificName");
    451     identitySubset.insert("Tooltip");
    452     identitySubset.insert("History");
    453     identitySubset.insert("Icon");
    454     CParamNode identity;
    455     CParamNode::LoadXMLString(identity, "<Identity/>");
    456     identity.CopyFilteredChildrenOfChild(in.GetChild("Entity"), "Identity", identitySubset);
    457     CParamNode::LoadXMLString(out, ("<Entity>"+utf8_from_wstring(identity.ToXML())+"</Entity>").c_str());
    458 
    459     // Set the entity as mirage entity
    460     CParamNode::LoadXMLString(out, "<Entity><Mirage/></Entity>");
    461 }
    462 
    463 void CTemplateLoader::CopyFoundationSubset(CParamNode& out, const CParamNode& in)
    464 {
    465     // TODO: this is all kind of yucky and hard-coded; it'd be nice to have a more generic
    466     // extensible scriptable way to define these subsets
    467 
    468     std::set<std::string> permittedComponentTypes;
    469     permittedComponentTypes.insert("Ownership");
    470     permittedComponentTypes.insert("Position");
    471     permittedComponentTypes.insert("VisualActor");
    472     permittedComponentTypes.insert("Identity");
    473     permittedComponentTypes.insert("BuildRestrictions");
    474     permittedComponentTypes.insert("Obstruction");
    475     permittedComponentTypes.insert("Selectable");
    476     permittedComponentTypes.insert("Footprint");
    477     permittedComponentTypes.insert("Fogging");
    478     permittedComponentTypes.insert("Armour");
    479     permittedComponentTypes.insert("Health");
    480     permittedComponentTypes.insert("StatusBars");
    481     permittedComponentTypes.insert("OverlayRenderer");
    482     permittedComponentTypes.insert("Decay");
    483     permittedComponentTypes.insert("Cost");
    484     permittedComponentTypes.insert("Sound");
    485     permittedComponentTypes.insert("Visibility");
    486     permittedComponentTypes.insert("Vision");
    487     permittedComponentTypes.insert("AIProxy");
    488     permittedComponentTypes.insert("RallyPoint");
    489     permittedComponentTypes.insert("RallyPointRenderer");
    490 
    491     CParamNode::LoadXMLString(out, "<Entity/>");
    492     out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
    493 
    494     // Switch the actor to foundation mode
    495     CParamNode::LoadXMLString(out, "<Entity><VisualActor><Foundation/></VisualActor></Entity>");
    496 
    497     // Add the Foundation component, to deal with the construction process
    498     CParamNode::LoadXMLString(out, "<Entity><Foundation/></Entity>");
    499 
    500     // Initialise health to 1
    501     CParamNode::LoadXMLString(out, "<Entity><Health><Initial>1</Initial></Health></Entity>");
    502 
    503     // Foundations shouldn't initially block unit movement
    504     if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
    505         CParamNode::LoadXMLString(out, "<Entity><Obstruction><DisableBlockMovement>true</DisableBlockMovement><DisableBlockPathfinding>true</DisableBlockPathfinding></Obstruction></Entity>");
    506 
    507     // Don't provide population bonuses yet (but still do take up population cost)
    508     if (out.GetChild("Entity").GetChild("Cost").IsOk())
    509         CParamNode::LoadXMLString(out, "<Entity><Cost><PopulationBonus>0</PopulationBonus></Cost></Entity>");
    510 
    511     // Foundations should be visible themselves in fog-of-war if their base template is,
    512     // but shouldn't have any vision range
    513     if (out.GetChild("Entity").GetChild("Vision").IsOk())
    514     {
    515         CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range></Vision></Entity>");
    516         // Foundations should not have special vision capabilities either
    517         if (out.GetChild("Entity").GetChild("Vision").GetChild("RevealShore").IsOk())
    518             CParamNode::LoadXMLString(out, "<Entity><Vision><RevealShore>false</RevealShore></Vision></Entity>");
    519     }
    520 }
    521 
    522 void CTemplateLoader::CopyConstructionSubset(CParamNode& out, const CParamNode& in)
    523 {
    524     // Currently used for buildings rising during construction
    525     // Mostly serves to filter out components like Vision, UnitAI, etc.
    526     std::set<std::string> permittedComponentTypes;
    527     permittedComponentTypes.insert("Footprint");
    528     permittedComponentTypes.insert("Ownership");
    529     permittedComponentTypes.insert("Position");
    530     permittedComponentTypes.insert("VisualActor");
    531 
    532     CParamNode::LoadXMLString(out, "<Entity/>");
    533     out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
    534 }
    535 
    536 void CTemplateLoader::CopyResourceSubset(CParamNode& out, const CParamNode& in)
    537 {
    538     // Currently used for animals which die and leave a gatherable corpse.
    539     // Mostly serves to filter out components like Vision, UnitAI, etc.
    540     // Don't emit sound as our samples only apply to living animals.
    541     std::set<std::string> permittedComponentTypes;
    542     permittedComponentTypes.insert("Ownership");
    543     permittedComponentTypes.insert("Position");
    544     permittedComponentTypes.insert("VisualActor");
    545     permittedComponentTypes.insert("Identity");
    546     permittedComponentTypes.insert("Minimap");
    547     permittedComponentTypes.insert("ResourceSupply");
    548     permittedComponentTypes.insert("Selectable");
    549     permittedComponentTypes.insert("Footprint");
    550     permittedComponentTypes.insert("StatusBars");
    551     permittedComponentTypes.insert("OverlayRenderer");
    552     permittedComponentTypes.insert("AIProxy");
    553 
    554     CParamNode::LoadXMLString(out, "<Entity/>");
    555     out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
    556    
    557     // When dying, resources lose the unitMotion component
    558     // This causes them to have no clearance. Since unit obstructions no longer have a radius,
    559     // this makes them unreachable in some cases (see #3530).
    560     // Instead, create a static, unblocking (see #3530 for why) static obstruction.
    561     // TODO: this should probably be generalized as a parameter on entity death or something.
    562     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>");
    563 }
  • 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/system/ParamNode.cpp

    diff --git a/source/simulation2/system/ParamNode.cpp b/source/simulation2/system/ParamNode.cpp
    index 173117b..74ca203 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;
    8790    {
    8891        XERO_ITER_ATTR(element, attr)
    8992        {
    void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const  
    97100                m_Childs.erase(name);
    98101                replacing = true;
    99102            }
     103            else if (attr.Name == at_filtered)
     104            {
     105                filtering = true;
     106            }
     107            else if (attr.Name == at_merge)
     108            {
     109                if (m_Childs.find(name) == m_Childs.end())
     110                    return;
     111            }
    100112            else if (attr.Name == at_op)
    101113            {
    102114                if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"add")
    void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const  
    150162        }
    151163    }
    152164
     165    // For the filtered case
     166    ChildrenMap childs;
     167
    153168    // Add this element as a child node
    154169    CParamNode& node = m_Childs[name];
    155170    if (op != INVALID)
    void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const  
    175190    XERO_ITER_EL(element, child)
    176191    {
    177192        node.ApplyLayer(xmb, child, sourceIdentifier);
     193        if (filtering)
     194        {
     195            std::string childname = xmb.GetElementString(child.GetNodeName()); // TODO: is GetElementString inefficient? (especially if we call it twice)
     196            childs[childname] = std::move(node.m_Childs[childname]);
     197        }
    178198    }
    179199
     200    if (filtering)
     201        node.m_Childs.swap(childs);
     202
    180203    // Add the element's attributes, prefixing names with "@"
    181204    XERO_ITER_ATTR(element, attr)
    182205    {
    183206        // Skip special attributes
    184         if (attr.Name == at_replace || attr.Name == at_op)
     207        if (attr.Name == at_replace || attr.Name == at_op || attr.Name == at_merge || attr.Name == at_filtered)
    185208            continue;
    186209        // Add any others
    187210        std::string attrName = xmb.GetAttributeString(attr.Name);
  • source/simulation2/system/ParamNode.h

    diff --git a/source/simulation2/system/ParamNode.h b/source/simulation2/system/ParamNode.h
    index 4fc911f..9ce530e 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
  • source/simulation2/tests/test_ParamNode.h

    diff --git a/source/simulation2/tests/test_ParamNode.h b/source/simulation2/tests/test_ParamNode.h
    index 043554b..d329424 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, "<test> <a><b/></a> <c>toberemoved</c> <d><e/></d> </test>"), PSRETURN_OK);
     142        TS_ASSERT_EQUALS(CParamNode::LoadXMLString(node, "<test filtered=\"\"> <a/> <d><f/></d> <g/> </test>"), PSRETURN_OK);
     143        TS_ASSERT_WSTR_EQUALS(node.ToXML(), L"<test><a><b></b></a><d><e></e><f></f></d><g></g></test>");
     144    }
     145
     146    void test_overlay_merge()
     147    {
     148        CParamNode node;
     149        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);
     150        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);
     151        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>");
     152    }
     153
    138154    void test_types()
    139155    {
    140156        CParamNode node;