Index: ps/trunk/binaries/data/mods/public/simulation/components/Visibility.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Visibility.js (revision 16918) +++ ps/trunk/binaries/data/mods/public/simulation/components/Visibility.js (revision 16919) @@ -1,83 +1,97 @@ const VIS_HIDDEN = 0; const VIS_FOGGED = 1; const VIS_VISIBLE = 2; function Visibility() {} Visibility.prototype.Schema = "" + "" + "" + "" + "" + "" + + "" + + "" + + "" + "" + "" + ""; Visibility.prototype.Init = function() { this.retainInFog = this.template.RetainInFog == "true"; this.alwaysVisible = this.template.AlwaysVisible == "true"; + this.corpse = this.template.Corpse == "true"; this.preview = this.template.Preview == "true"; this.activated = false; - if (this.preview) + if (this.preview || this.corpse) this.SetActivated(true); }; /** * Sets the range manager scriptedVisibility flag for this entity. */ Visibility.prototype.SetActivated = function(status) { let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); cmpRangeManager.ActivateScriptedVisibility(this.entity, status); this.activated = status; }; /** * This function is a fallback for some entities whose visibility status * cannot be cached by the range manager (especially local entities like previews). * Calling the scripts is expensive, so only call it if really needed. */ Visibility.prototype.IsActivated = function() { return this.activated; }; /** * This function is called if the range manager scriptedVisibility flag is set to true for this entity. * If so, the return value supersedes the visibility computed by the range manager. * isVisible: true if the entity is in the vision range of a unit, false otherwise * isExplored: true if the entity is in explored territory, false otherwise */ Visibility.prototype.GetVisibility = function(player, isVisible, isExplored) { if (this.preview) { - // For the owner only, mock the "RetainInFog" behaviour - + // For the owner only, mock the "RetainInFog" behavior let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); if (cmpOwnership && cmpOwnership.GetOwner() == player && isExplored) return isVisible ? VIS_VISIBLE : VIS_FOGGED; + // For others, hide the preview return VIS_HIDDEN; } + else if (this.corpse) + { + // For the owner only, mock the "RetainInFog" behavior + let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); + if (cmpOwnership && cmpOwnership.GetOwner() == player && isExplored) + return isVisible ? VIS_VISIBLE : VIS_FOGGED; + + // For others, regular displaying + return isVisible ? VIS_VISIBLE : VIS_HIDDEN; + } return VIS_VISIBLE; }; Visibility.prototype.GetRetainInFog = function() { return this.retainInFog; }; Visibility.prototype.GetAlwaysVisible = function() { return this.alwaysVisible; }; Engine.RegisterComponentType(IID_Visibility, "Visibility", Visibility); Index: ps/trunk/binaries/data/mods/public/simulation/templates/special/actor.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/special/actor.xml (revision 16918) +++ ps/trunk/binaries/data/mods/public/simulation/templates/special/actor.xml (revision 16919) @@ -1,23 +1,24 @@ 0 upright false 6.0 true false + false false 0 (should be overridden) false false false Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_gaia.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_gaia.xml (revision 16918) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_gaia.xml (revision 16919) @@ -1,36 +1,37 @@ gaia Gaia true true true true true false false 2.0 0.333 5.0 true false + false false 0 false true false Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_rubble.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_rubble.xml (revision 16918) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_rubble.xml (revision 16919) @@ -1,31 +1,32 @@ true false 15.0 0.2 0 0 upright false 6.0 true false + false false 0 structures/rubble_stone_3x3.xml false false false Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml (revision 16918) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml (revision 16919) @@ -1,123 +1,124 @@ 1 1 1 1 1 1 0 0 Ranged Infantry land own 1000 0 5 0 0 10 0 0 0 0 false false 0.0 3.0 9.8 corpse 0 true true Structure Structure ConquestCritical structure true true true true true false false special/rallypoint art/textures/misc/rallypoint_line.png art/textures/misc/rallypoint_line_mask.png 0.2 square round default outline_border.png outline_border_mask.png 0.4 interface/complete/building/complete_universal.xml attack/destruction/building_collapse_large.xml interface/alarm/alarm_attackplayer.xml attack/weapon/arrowfly.xml attack/impact/arrow_metal.xml 6.0 0.6 12.0 5 true false + false false 40 false true false Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_gaia_settlement.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_gaia_settlement.xml (revision 16918) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_gaia_settlement.xml (revision 16919) @@ -1,37 +1,38 @@ 8.0 gaia Settlement Settlement Build a Civic Center at this location to expand your territory. gaia/special_settlement.png settlement true false false true true false false true false + false false 0 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml (revision 16918) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml (revision 16919) @@ -1,123 +1,124 @@ 1 1 15 1 0 1 0 0 0 0 false false 80.0 0.01 0.0 2.5 corpse 100 0 false false Unit Unit ConquestCritical formations/null unit true true false false true false false 2.0 1.0 1 10 10 10 10 circle/128x128.png circle/128x128_mask.png interface/alarm/alarm_attackplayer.xml 2.0 0.333 5.0 2 aggressive 12.0 false true false 9 15.0 50.0 0.0 0.1 0.2 default false false + false false 12 true false false Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_fauna_decorative.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_fauna_decorative.xml (revision 16918) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_fauna_decorative.xml (revision 16919) @@ -1,54 +1,55 @@ - - 0 - upright - false - 6.0 - - - - - - circle/128x128.png - circle/128x128_mask.png - - - - - 0 - passive - passive - false - false - 20 - 10.0 - 10000 - 100000 - 1 - 2 - - - false - 8.0 - unrestricted - default - - - true - false - false - - - 0 - - - false - false - false - + + 0 + upright + false + 6.0 + + + + + + circle/128x128.png + circle/128x128_mask.png + + + + + 0 + passive + passive + false + false + 20 + 10.0 + 10000 + 100000 + 1 + 2 + + + false + 8.0 + unrestricted + default + + + true + false + false + false + + + 0 + + + false + false + false + Index: ps/trunk/source/ps/TemplateLoader.cpp =================================================================== --- ps/trunk/source/ps/TemplateLoader.cpp (revision 16918) +++ ps/trunk/source/ps/TemplateLoader.cpp (revision 16919) @@ -1,557 +1,557 @@ /* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "TemplateLoader.h" #include "lib/utf8.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "ps/XML/Xeromyces.h" static const wchar_t TEMPLATE_ROOT[] = L"simulation/templates/"; static const wchar_t ACTOR_ROOT[] = L"art/actors/"; static CParamNode NULL_NODE(false); bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int depth) { // If this file was already loaded, we don't need to do anything if (m_TemplateFileData.find(templateName) != m_TemplateFileData.end()) return true; // Handle infinite loops more gracefully than running out of stack space and crashing if (depth > 100) { LOGERROR("Probable infinite inheritance loop in entity template '%s'", templateName.c_str()); return false; } // Handle special case "actor|foo" if (templateName.find("actor|") == 0) { ConstructTemplateActor(templateName.substr(6), m_TemplateFileData[templateName]); return true; } // Handle special case "preview|foo" if (templateName.find("preview|") == 0) { // Load the base entity template, if it wasn't already loaded std::string baseName = templateName.substr(8); if (!LoadTemplateFile(baseName, depth+1)) { LOGERROR("Failed to load entity template '%s'", baseName.c_str()); return false; } // Copy a subset to the requested template CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], false); return true; } // Handle special case "corpse|foo" if (templateName.find("corpse|") == 0) { // Load the base entity template, if it wasn't already loaded std::string baseName = templateName.substr(7); if (!LoadTemplateFile(baseName, depth+1)) { LOGERROR("Failed to load entity template '%s'", baseName.c_str()); return false; } // Copy a subset to the requested template CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], true); return true; } // Handle special case "mirage|foo" if (templateName.find("mirage|") == 0) { // Load the base entity template, if it wasn't already loaded std::string baseName = templateName.substr(7); if (!LoadTemplateFile(baseName, depth+1)) { LOGERROR("Failed to load entity template '%s'", baseName.c_str()); return false; } // Copy a subset to the requested template CopyMirageSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]); return true; } // Handle special case "foundation|foo" if (templateName.find("foundation|") == 0) { // Load the base entity template, if it wasn't already loaded std::string baseName = templateName.substr(11); if (!LoadTemplateFile(baseName, depth+1)) { LOGERROR("Failed to load entity template '%s'", baseName.c_str()); return false; } // Copy a subset to the requested template CopyFoundationSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]); return true; } // Handle special case "construction|foo" if (templateName.find("construction|") == 0) { // Load the base entity template, if it wasn't already loaded std::string baseName = templateName.substr(13); if (!LoadTemplateFile(baseName, depth+1)) { LOGERROR("Failed to load entity template '%s'", baseName.c_str()); return false; } // Copy a subset to the requested template CopyConstructionSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]); return true; } // Handle special case "resource|foo" if (templateName.find("resource|") == 0) { // Load the base entity template, if it wasn't already loaded std::string baseName = templateName.substr(9); if (!LoadTemplateFile(baseName, depth+1)) { LOGERROR("Failed to load entity template '%s'", baseName.c_str()); return false; } // Copy a subset to the requested template CopyResourceSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]); return true; } // Normal case: templateName is an XML file: VfsPath path = VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(templateName + ".xml"); CXeromyces xero; PSRETURN ok = xero.Load(g_VFS, path); if (ok != PSRETURN_OK) return false; // (Xeromyces already logged an error with the full filename) int attr_parent = xero.GetAttributeID("parent"); CStr parentName = xero.GetRoot().GetAttributes().GetNamedItem(attr_parent); if (!parentName.empty()) { // To prevent needless complexity in template design, we don't allow |-separated strings as parents if (parentName.find('|') != parentName.npos) { LOGERROR("Invalid parent '%s' in entity template '%s'", parentName.c_str(), templateName.c_str()); return false; } // Ensure the parent is loaded if (!LoadTemplateFile(parentName, depth+1)) { LOGERROR("Failed to load parent '%s' of entity template '%s'", parentName.c_str(), templateName.c_str()); return false; } CParamNode& parentData = m_TemplateFileData[parentName]; // Initialise this template with its parent m_TemplateFileData[templateName] = parentData; } // Load the new file into the template data (overriding parent values) CParamNode::LoadXML(m_TemplateFileData[templateName], xero, wstring_from_utf8(templateName).c_str()); return true; } static Status AddToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData) { std::vector& templates = *(std::vector*)cbData; // Strip the .xml extension VfsPath pathstem = pathname.ChangeExtension(L""); // Strip the root from the path std::wstring name = pathstem.string().substr(ARRAY_SIZE(TEMPLATE_ROOT)-1); // We want to ignore template_*.xml templates, since they should never be built in the editor if (name.substr(0, 9) == L"template_") return INFO::OK; templates.push_back(std::string(name.begin(), name.end())); return INFO::OK; } static Status AddActorToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData) { std::vector& templates = *(std::vector*)cbData; // Strip the root from the path std::wstring name = pathname.string().substr(ARRAY_SIZE(ACTOR_ROOT)-1); templates.push_back("actor|" + std::string(name.begin(), name.end())); return INFO::OK; } bool CTemplateLoader::TemplateExists(const std::string& templateName) const { size_t pos = templateName.rfind('|'); std::string baseName(pos != std::string::npos ? templateName.substr(pos+1) : templateName); return VfsFileExists(VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(baseName + ".xml")); } std::vector CTemplateLoader::FindPlaceableTemplates(const std::string& path, bool includeSubdirectories, ETemplatesType templatesType, ScriptInterface& scriptInterface) { JSContext* cx = scriptInterface.GetContext(); JSAutoRequest rq(cx); std::vector templates; Status ok; VfsPath templatePath; if (templatesType == SIMULATION_TEMPLATES || templatesType == ALL_TEMPLATES) { JS::RootedValue placeablesFilter(cx); scriptInterface.ReadJSONFile("simulation/data/placeablesFilter.json", &placeablesFilter); JS::RootedObject folders(cx); if (scriptInterface.GetProperty(placeablesFilter, "templates", &folders)) { if (!(JS_IsArrayObject(cx, folders))) { LOGERROR("FindPlaceableTemplates: Argument must be an array!"); return templates; } u32 length; if (!JS_GetArrayLength(cx, folders, &length)) { LOGERROR("FindPlaceableTemplates: Failed to get array length!"); return templates; } templatePath = VfsPath(TEMPLATE_ROOT) / path; //I have every object inside, just run for each for (u32 i=0; i CTemplateLoader::FindTemplates(const std::string& path, bool includeSubdirectories, ETemplatesType templatesType) { std::vector templates; Status ok; VfsPath templatePath; if (templatesType == SIMULATION_TEMPLATES || templatesType == ALL_TEMPLATES) { templatePath = VfsPath(TEMPLATE_ROOT) / path; if (includeSubdirectories) ok = vfs::ForEachFile(g_VFS, templatePath, AddToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE); else ok = vfs::ForEachFile(g_VFS, templatePath, AddToTemplates, (uintptr_t)&templates, L"*.xml"); WARN_IF_ERR(ok); } if (templatesType == ACTOR_TEMPLATES || templatesType == ALL_TEMPLATES) { templatePath = VfsPath(ACTOR_ROOT) / path; if (includeSubdirectories) ok = vfs::ForEachFile(g_VFS, templatePath, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE); else ok = vfs::ForEachFile(g_VFS, templatePath, AddActorToTemplates, (uintptr_t)&templates, L"*.xml"); WARN_IF_ERR(ok); } if (templatesType != SIMULATION_TEMPLATES && templatesType != ACTOR_TEMPLATES && templatesType != ALL_TEMPLATES) LOGERROR("Undefined template type (valid: all, simulation, actor)"); return templates; } const CParamNode& CTemplateLoader::GetTemplateFileData(const std::string& templateName) { // Load the template if necessary if (!LoadTemplateFile(templateName, 0)) { LOGERROR("Failed to load entity template '%s'", templateName.c_str()); return NULL_NODE; } return m_TemplateFileData[templateName]; } void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CParamNode& out) { // Load the base actor template if necessary const char* templateName = "special/actor"; if (!LoadTemplateFile(templateName, 0)) { LOGERROR("Failed to load entity template '%s'", templateName); return; } // Copy the actor template out = m_TemplateFileData[templateName]; // Initialise the actor's name and make it an Atlas selectable entity. std::wstring actorNameW = wstring_from_utf8(actorName); std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(actorNameW)); std::string xml = "" "" + name + "" // arbitrary-sized Footprint definition to make actors' selection outlines show up in Atlas "1.0" "" "" "actor.pngactor_mask.png" "" ""; CParamNode::LoadXMLString(out, xml.c_str(), actorNameW.c_str()); } void CTemplateLoader::CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse) { // We only want to include components which are necessary (for the visual previewing of an entity) // and safe (i.e. won't do anything that affects the synchronised simulation state), so additions // to this list should be carefully considered std::set permittedComponentTypes; permittedComponentTypes.insert("Identity"); permittedComponentTypes.insert("Ownership"); permittedComponentTypes.insert("Position"); permittedComponentTypes.insert("Visibility"); permittedComponentTypes.insert("VisualActor"); permittedComponentTypes.insert("Footprint"); permittedComponentTypes.insert("Obstruction"); permittedComponentTypes.insert("Decay"); permittedComponentTypes.insert("BuildRestrictions"); // Need these for the Actor Viewer: permittedComponentTypes.insert("Attack"); permittedComponentTypes.insert("UnitMotion"); permittedComponentTypes.insert("Sound"); // (This set could be initialised once and reused, but it's not worth the effort) CParamNode::LoadXMLString(out, ""); out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes); // Disable the Obstruction component (if there is one) so it doesn't affect pathfinding // (but can still be used for testing this entity for collisions against others) if (out.GetChild("Entity").GetChild("Obstruction").IsOk()) CParamNode::LoadXMLString(out, "false"); if (!corpse) { // Previews should not cast shadows if (out.GetChild("Entity").GetChild("VisualActor").IsOk()) CParamNode::LoadXMLString(out, ""); // Previews should always be visible in fog-of-war/etc - CParamNode::LoadXMLString(out, "falsetruetrue"); + CParamNode::LoadXMLString(out, "truetrue"); } if (corpse) { // Corpses should include decay components and activate them if (out.GetChild("Entity").GetChild("Decay").IsOk()) CParamNode::LoadXMLString(out, "true"); // Corpses shouldn't display silhouettes (especially since they're often half underground) if (out.GetChild("Entity").GetChild("VisualActor").IsOk()) CParamNode::LoadXMLString(out, "false"); - // Corpses should remain visible in fog-of-war - CParamNode::LoadXMLString(out, "truefalsefalse"); + // Corpses should remain visible in fog-of-war (for the owner only) + CParamNode::LoadXMLString(out, "true"); } } void CTemplateLoader::CopyMirageSubset(CParamNode& out, const CParamNode& in) { // Currently used for mirage entities replacing real ones in fog-of-war std::set permittedComponentTypes; permittedComponentTypes.insert("Footprint"); permittedComponentTypes.insert("Minimap"); permittedComponentTypes.insert("Ownership"); permittedComponentTypes.insert("OverlayRenderer"); permittedComponentTypes.insert("Position"); permittedComponentTypes.insert("Selectable"); permittedComponentTypes.insert("StatusBars"); permittedComponentTypes.insert("Visibility"); permittedComponentTypes.insert("VisualActor"); CParamNode::LoadXMLString(out, ""); out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes); // Select a subset of identity data. We don't want to have, for example, a CC mirage // that has also the CC class and then prevents construction of other CCs std::set identitySubset; identitySubset.insert("Civ"); identitySubset.insert("GenericName"); identitySubset.insert("SpecificName"); identitySubset.insert("Tooltip"); identitySubset.insert("History"); identitySubset.insert("Icon"); CParamNode identity; CParamNode::LoadXMLString(identity, ""); identity.CopyFilteredChildrenOfChild(in.GetChild("Entity"), "Identity", identitySubset); CParamNode::LoadXMLString(out, (""+utf8_from_wstring(identity.ToXML())+"").c_str()); // Set the entity as mirage entity CParamNode::LoadXMLString(out, ""); } void CTemplateLoader::CopyFoundationSubset(CParamNode& out, const CParamNode& in) { // TODO: this is all kind of yucky and hard-coded; it'd be nice to have a more generic // extensible scriptable way to define these subsets std::set permittedComponentTypes; permittedComponentTypes.insert("Ownership"); permittedComponentTypes.insert("Position"); permittedComponentTypes.insert("VisualActor"); permittedComponentTypes.insert("Identity"); permittedComponentTypes.insert("BuildRestrictions"); permittedComponentTypes.insert("Obstruction"); permittedComponentTypes.insert("Selectable"); permittedComponentTypes.insert("Footprint"); permittedComponentTypes.insert("Fogging"); permittedComponentTypes.insert("Armour"); permittedComponentTypes.insert("Health"); permittedComponentTypes.insert("StatusBars"); permittedComponentTypes.insert("OverlayRenderer"); permittedComponentTypes.insert("Decay"); permittedComponentTypes.insert("Cost"); permittedComponentTypes.insert("Sound"); permittedComponentTypes.insert("Visibility"); permittedComponentTypes.insert("Vision"); permittedComponentTypes.insert("AIProxy"); permittedComponentTypes.insert("RallyPoint"); permittedComponentTypes.insert("RallyPointRenderer"); CParamNode::LoadXMLString(out, ""); out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes); // Switch the actor to foundation mode CParamNode::LoadXMLString(out, ""); // Add the Foundation component, to deal with the construction process CParamNode::LoadXMLString(out, ""); // Initialise health to 1 CParamNode::LoadXMLString(out, "1"); // Foundations shouldn't initially block unit movement if (out.GetChild("Entity").GetChild("Obstruction").IsOk()) CParamNode::LoadXMLString(out, "truetrue"); // Don't provide population bonuses yet (but still do take up population cost) if (out.GetChild("Entity").GetChild("Cost").IsOk()) CParamNode::LoadXMLString(out, "0"); // Foundations should be visible themselves in fog-of-war if their base template is, // but shouldn't have any vision range if (out.GetChild("Entity").GetChild("Vision").IsOk()) { CParamNode::LoadXMLString(out, "0"); // Foundations should not have special vision capabilities either if (out.GetChild("Entity").GetChild("Vision").GetChild("RevealShore").IsOk()) CParamNode::LoadXMLString(out, "false"); } } void CTemplateLoader::CopyConstructionSubset(CParamNode& out, const CParamNode& in) { // Currently used for buildings rising during construction // Mostly serves to filter out components like Vision, UnitAI, etc. std::set permittedComponentTypes; permittedComponentTypes.insert("Footprint"); permittedComponentTypes.insert("Ownership"); permittedComponentTypes.insert("Position"); permittedComponentTypes.insert("VisualActor"); CParamNode::LoadXMLString(out, ""); out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes); } void CTemplateLoader::CopyResourceSubset(CParamNode& out, const CParamNode& in) { // Currently used for animals which die and leave a gatherable corpse. // Mostly serves to filter out components like Vision, UnitAI, etc. std::set permittedComponentTypes; permittedComponentTypes.insert("Ownership"); permittedComponentTypes.insert("Position"); permittedComponentTypes.insert("VisualActor"); permittedComponentTypes.insert("Identity"); permittedComponentTypes.insert("Obstruction"); permittedComponentTypes.insert("Minimap"); permittedComponentTypes.insert("ResourceSupply"); permittedComponentTypes.insert("Selectable"); permittedComponentTypes.insert("Footprint"); permittedComponentTypes.insert("StatusBars"); permittedComponentTypes.insert("OverlayRenderer"); permittedComponentTypes.insert("Sound"); permittedComponentTypes.insert("AIProxy"); CParamNode::LoadXMLString(out, ""); out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes); }