Index: ps/trunk/source/simulation2/tests/test_CmpTemplateManager.h =================================================================== --- ps/trunk/source/simulation2/tests/test_CmpTemplateManager.h (revision 9182) +++ ps/trunk/source/simulation2/tests/test_CmpTemplateManager.h (revision 9183) @@ -1,258 +1,256 @@ /* Copyright (C) 2011 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 "lib/self_test.h" #include "simulation2/system/ComponentManager.h" #include "simulation2/components/ICmpTemplateManager.h" #include "simulation2/components/ICmpTest.h" #include "simulation2/MessageTypes.h" #include "simulation2/system/ParamNode.h" #include "simulation2/system/SimContext.h" #include "simulation2/Simulation2.h" #include "graphics/Terrain.h" #include "ps/Filesystem.h" #include "ps/CLogger.h" #include "ps/XML/Xeromyces.h" class TestCmpTemplateManager : public CxxTest::TestSuite { public: void setUp() { g_VFS = CreateVfs(20 * MiB); TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods/_test.sim", VFS_MOUNT_MUST_EXIST)); TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache")); CXeromyces::Startup(); } void tearDown() { CXeromyces::Terminate(); g_VFS.reset(); DeleteDirectory(DataDir()/"_testcache"); } void test_LoadTemplate() { CSimContext context; CComponentManager man(context); man.LoadComponentTypes(); entity_id_t ent1 = 1, ent2 = 2; CParamNode noParam; TS_ASSERT(man.AddComponent(ent1, CID_TemplateManager, noParam)); ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager)); TS_ASSERT(tempMan != NULL); const CParamNode* basic = tempMan->LoadTemplate(ent2, "basic", -1); TS_ASSERT(basic != NULL); TS_ASSERT_WSTR_EQUALS(basic->ToXML(), L"12345"); const CParamNode* inherit2 = tempMan->LoadTemplate(ent2, "inherit2", -1); TS_ASSERT(inherit2 != NULL); TS_ASSERT_WSTR_EQUALS(inherit2->ToXML(), L"d2e1f1g2"); const CParamNode* inherit1 = tempMan->LoadTemplate(ent2, "inherit1", -1); TS_ASSERT(inherit1 != NULL); TS_ASSERT_WSTR_EQUALS(inherit1->ToXML(), L"d1e1f1"); const CParamNode* actor = tempMan->LoadTemplate(ent2, "actor|example1", -1); TS_ASSERT(actor != NULL); TS_ASSERT_WSTR_EQUALS(actor->ToXML(), - L"0uprightfalse" L"example1falsefalse"); const CParamNode* preview = tempMan->LoadTemplate(ent2, "preview|unit", -1); TS_ASSERT(preview != NULL); TS_ASSERT_WSTR_EQUALS(preview->ToXML(), L"0uprightfalse" L"true0false" L"examplefalsefalse"); const CParamNode* previewobstruct = tempMan->LoadTemplate(ent2, "preview|unitobstruct", -1); TS_ASSERT(previewobstruct != NULL); TS_ASSERT_WSTR_EQUALS(previewobstruct->ToXML(), L"1.0" L"" L"falsetruefalse" L"truefalse" L"falsefalse" L"" L"" L"0uprightfalse" L"true0false" L"examplefalsefalse"); const CParamNode* previewactor = tempMan->LoadTemplate(ent2, "preview|actor|example2", -1); TS_ASSERT(previewactor != NULL); TS_ASSERT_WSTR_EQUALS(previewactor->ToXML(), - L"0uprightfalse" L"true0false" L"example2falsefalse"); } void test_LoadTemplate_scriptcache() { CSimContext context; CComponentManager man(context); man.LoadComponentTypes(); entity_id_t ent1 = 1, ent2 = 2; CParamNode noParam; TS_ASSERT(man.AddComponent(ent1, CID_TemplateManager, noParam)); ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager)); TS_ASSERT(tempMan != NULL); tempMan->DisableValidation(); CScriptValRooted val; JSContext* cx = man.GetScriptInterface().GetContext(); // This is testing some bugs in the template JS object caching const CParamNode* inherit1 = tempMan->LoadTemplate(ent2, "inherit1", -1); val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, inherit1)); TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({Test1A:{'@a':\"a1\", '@b':\"b1\", '@c':\"c1\", d:\"d1\", e:\"e1\", f:\"f1\"}})"); const CParamNode* inherit2 = tempMan->LoadTemplate(ent2, "inherit2", -1); val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, inherit2)); TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({'@parent':\"inherit1\", Test1A:{'@a':\"a2\", '@b':\"b1\", '@c':\"c1\", d:\"d2\", e:\"e1\", f:\"f1\", g:\"g2\"}})"); const CParamNode* actor = tempMan->LoadTemplate(ent2, "actor|example1", -1); val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, &actor->GetChild("VisualActor"))); TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({Actor:\"example1\", SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\"})"); const CParamNode* foundation = tempMan->LoadTemplate(ent2, "foundation|actor|example1", -1); val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, &foundation->GetChild("VisualActor"))); TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({Actor:\"example1\", Foundation:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\"})"); } void test_LoadTemplate_errors() { CSimContext context; CComponentManager man(context); man.LoadComponentTypes(); entity_id_t ent1 = 1, ent2 = 2; CParamNode noParam; TS_ASSERT(man.AddComponent(ent1, CID_TemplateManager, noParam)); ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager)); TS_ASSERT(tempMan != NULL); TestLogger logger; TS_ASSERT(tempMan->LoadTemplate(ent2, "illformed", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "invalid", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "nonexistent", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-loop", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-broken", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-special", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "preview|nonexistent", -1) == NULL); } void test_LoadTemplate_multiple() { CSimContext context; CComponentManager man(context); man.LoadComponentTypes(); entity_id_t ent1 = 1, ent2 = 2; CParamNode noParam; TS_ASSERT(man.AddComponent(ent1, CID_TemplateManager, noParam)); ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager)); TS_ASSERT(tempMan != NULL); const CParamNode* basicA = tempMan->LoadTemplate(ent2, "basic", -1); TS_ASSERT(basicA != NULL); const CParamNode* basicB = tempMan->LoadTemplate(ent2, "basic", -1); TS_ASSERT(basicA == basicB); const CParamNode* inherit2A = tempMan->LoadTemplate(ent2, "inherit2", -1); TS_ASSERT(inherit2A != NULL); const CParamNode* inherit2B = tempMan->LoadTemplate(ent2, "inherit2", -1); TS_ASSERT(inherit2A == inherit2B); TestLogger logger; TS_ASSERT(tempMan->LoadTemplate(ent2, "nonexistent", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "nonexistent", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-loop", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-loop", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-broken", -1) == NULL); TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-broken", -1) == NULL); } }; class TestCmpTemplateManager_2 : public CxxTest::TestSuite { public: void setUp() { g_VFS = CreateVfs(20 * MiB); TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods/public", VFS_MOUNT_MUST_EXIST)); TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache")); CXeromyces::Startup(); } void tearDown() { CXeromyces::Terminate(); g_VFS.reset(); DeleteDirectory(DataDir()/"_testcache"); } // This just attempts loading every public entity, to check there's no validation errors void test_load_all_DISABLED() // disabled since it's a bit slow and noisy { CTerrain dummy; CSimulation2 sim(NULL, &dummy); sim.LoadDefaultScripts(); sim.ResetState(); CmpPtr cmpTempMan(sim, SYSTEM_ENTITY); TS_ASSERT(!cmpTempMan.null()); std::vector templates = cmpTempMan->FindAllTemplates(true); for (size_t i = 0; i < templates.size(); ++i) { std::string name = templates[i]; printf("# %s\n", name.c_str()); const CParamNode* p = cmpTempMan->GetTemplate(name); TS_ASSERT(p != NULL); } } }; Index: ps/trunk/source/simulation2/components/CCmpTemplateManager.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpTemplateManager.cpp (revision 9182) +++ ps/trunk/source/simulation2/components/CCmpTemplateManager.cpp (revision 9183) @@ -1,539 +1,538 @@ /* Copyright (C) 2011 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 "simulation2/system/Component.h" #include "ICmpTemplateManager.h" #include "simulation2/MessageTypes.h" #include "lib/utf8.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "ps/XML/RelaxNG.h" #include "ps/XML/Xeromyces.h" static const wchar_t TEMPLATE_ROOT[] = L"simulation/templates/"; static const wchar_t ACTOR_ROOT[] = L"art/actors/"; class CCmpTemplateManager : public ICmpTemplateManager { public: static void ClassInit(CComponentManager& componentManager) { componentManager.SubscribeGloballyToMessageType(MT_Destroy); } DEFAULT_COMPONENT_ALLOCATOR(TemplateManager) static std::string GetSchema() { return ""; } virtual void Init(const CParamNode& UNUSED(paramNode)) { m_DisableValidation = false; m_Validator.LoadGrammar(GetSimContext().GetComponentManager().GenerateSchema()); // TODO: handle errors loading the grammar here? // TODO: support hotloading changes to the grammar } virtual void Deinit() { } virtual void Serialize(ISerializer& serialize) { size_t count = 0; for (std::map::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it) { if (ENTITY_IS_LOCAL(it->first)) continue; ++count; } serialize.NumberU32_Unbounded("num entities", (u32)count); for (std::map::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it) { if (ENTITY_IS_LOCAL(it->first)) continue; serialize.NumberU32_Unbounded("id", it->first); serialize.StringASCII("template", it->second, 0, 256); } // TODO: maybe we should do some kind of interning thing instead of printing so many strings? // TODO: will need to serialize techs too, because we need to be giving out // template data before other components (like the tech components) have been deserialized } virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) { Init(paramNode); u32 numEntities; deserialize.NumberU32_Unbounded("num entities", numEntities); for (u32 i = 0; i < numEntities; ++i) { entity_id_t ent; std::string templateName; deserialize.NumberU32_Unbounded("id", ent); deserialize.StringASCII("template", templateName, 0, 256); m_LatestTemplates[ent] = templateName; } } virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) { switch (msg.GetType()) { case MT_Destroy: { const CMessageDestroy& msgData = static_cast (msg); // Clean up m_LatestTemplates so it doesn't record any data for destroyed entities m_LatestTemplates.erase(msgData.entity); break; } } } virtual void DisableValidation() { m_DisableValidation = true; } virtual const CParamNode* LoadTemplate(entity_id_t ent, const std::string& templateName, int playerID); virtual const CParamNode* GetTemplate(std::string templateName); virtual const CParamNode* GetTemplateWithoutValidation(std::string templateName); virtual const CParamNode* LoadLatestTemplate(entity_id_t ent); virtual std::string GetCurrentTemplateName(entity_id_t ent); virtual std::vector FindAllTemplates(bool includeActors); virtual std::vector GetEntitiesUsingTemplate(std::string templateName); private: // Entity template XML validator RelaxNGValidator m_Validator; // Disable validation, for test cases bool m_DisableValidation; // Map from template name (XML filename or special |-separated string) to the most recently // loaded non-broken template data. This includes files that will fail schema validation. // (Failed loads won't remove existing entries under the same name, so we behave more nicely // when hotloading broken files) std::map m_TemplateFileData; // Map from template name to schema validation status. // (Some files, e.g. inherited parent templates, may not be valid themselves but we still need to load // them and use them; we only reject invalid templates that were requested directly by GetTemplate/etc) std::map m_TemplateSchemaValidity; // Remember the template used by each entity, so we can return them // again for deserialization. // TODO: should store player ID etc. std::map m_LatestTemplates; // (Re)loads the given template, regardless of whether it exists already, // and saves into m_TemplateFileData. Also loads any parents that are not yet // loaded. Returns false on error. // @param templateName XML filename to load (not a |-separated string) bool LoadTemplateFile(const std::string& templateName, int depth); // Constructs a standard static-decorative-object template for the given actor void ConstructTemplateActor(const std::string& actorName, CParamNode& out); // Copy the non-interactive components of an entity template (position, actor, etc) into // a new entity template void CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse); // Copy the components of an entity necessary for a construction foundation // (position, actor, armour, health, etc) into a new entity template void CopyFoundationSubset(CParamNode& out, const CParamNode& in); }; REGISTER_COMPONENT_TYPE(TemplateManager) const CParamNode* CCmpTemplateManager::LoadTemplate(entity_id_t ent, const std::string& templateName, int UNUSED(playerID)) { m_LatestTemplates[ent] = templateName; const CParamNode* templateRoot = GetTemplate(templateName); if (!templateRoot) return NULL; // TODO: Eventually we need to support techs in here, and return a different template per playerID return templateRoot; } const CParamNode* CCmpTemplateManager::GetTemplate(std::string templateName) { // Load the template if necessary if (!LoadTemplateFile(templateName, 0)) { LOGERROR(L"Failed to load entity template '%hs'", templateName.c_str()); return NULL; } if (!m_DisableValidation) { // Compute validity, if it's not computed before if (m_TemplateSchemaValidity.find(templateName) == m_TemplateSchemaValidity.end()) m_TemplateSchemaValidity[templateName] = m_Validator.Validate(wstring_from_utf8(templateName), m_TemplateFileData[templateName].ToXML()); // Refuse to return invalid templates if (!m_TemplateSchemaValidity[templateName]) return NULL; } const CParamNode& templateRoot = m_TemplateFileData[templateName].GetChild("Entity"); if (!templateRoot.IsOk()) { // The validator should never let this happen LOGERROR(L"Invalid root element in entity template '%hs'", templateName.c_str()); return NULL; } return &templateRoot; } const CParamNode* CCmpTemplateManager::GetTemplateWithoutValidation(std::string templateName) { // Load the template if necessary if (!LoadTemplateFile(templateName, 0)) { LOGERROR(L"Failed to load entity template '%hs'", templateName.c_str()); return NULL; } const CParamNode& templateRoot = m_TemplateFileData[templateName].GetChild("Entity"); if (!templateRoot.IsOk()) return NULL; return &templateRoot; } const CParamNode* CCmpTemplateManager::LoadLatestTemplate(entity_id_t ent) { std::map::const_iterator it = m_LatestTemplates.find(ent); if (it == m_LatestTemplates.end()) return NULL; return LoadTemplate(ent, it->second, -1); } std::string CCmpTemplateManager::GetCurrentTemplateName(entity_id_t ent) { std::map::const_iterator it = m_LatestTemplates.find(ent); if (it == m_LatestTemplates.end()) return ""; return it->second; } bool CCmpTemplateManager::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(L"Probable infinite inheritance loop in entity template '%hs'", 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(L"Failed to load entity template '%hs'", 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(L"Failed to load entity template '%hs'", 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 "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(L"Failed to load entity template '%hs'", baseName.c_str()); return false; } // Copy a subset to the requested template CopyFoundationSubset(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(L"Invalid parent '%hs' in entity template '%hs'", parentName.c_str(), templateName.c_str()); return false; } // Ensure the parent is loaded if (!LoadTemplateFile(parentName, depth+1)) { LOGERROR(L"Failed to load parent '%hs' of entity template '%hs'", 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); return true; } void CCmpTemplateManager::ConstructTemplateActor(const std::string& actorName, CParamNode& out) { - std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(wstring_from_utf8(actorName))); - std::string xml = "" - "" - "" - "upright" - "0" - "false" - "" - "" - "" + name + "" - "false" - "false" - "" - ""; + // Load the base actor template if necessary + const char* templateName = "special/actor"; + if (!LoadTemplateFile(templateName, 0)) + { + LOGERROR(L"Failed to load entity template '%hs'", templateName); + return; + } + + // Copy the actor template + out = m_TemplateFileData[templateName]; - out.LoadXMLString(out, xml.c_str()); + // Initialise the actor's name + std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(wstring_from_utf8(actorName))); + std::string xml = "" + name + ""; + CParamNode::LoadXMLString(out, xml.c_str()); } static LibError AddToTemplates(const VfsPath& pathname, const FileInfo& 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 LibError AddActorToTemplates(const VfsPath& pathname, const FileInfo& 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; } std::vector CCmpTemplateManager::FindAllTemplates(bool includeActors) { // TODO: eventually this should probably read all the template files and look for flags to // determine which should be displayed in the editor (and in what categories etc); for now we'll // just return all the files std::vector templates; LibError ok; // Find all the normal entity templates first ok = fs_util::ForEachFile(g_VFS, TEMPLATE_ROOT, AddToTemplates, (uintptr_t)&templates, L"*.xml", fs_util::DIR_RECURSIVE); WARN_ERR(ok); if (includeActors) { // Add all the actors too ok = fs_util::ForEachFile(g_VFS, ACTOR_ROOT, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", fs_util::DIR_RECURSIVE); WARN_ERR(ok); } return templates; } /** * Get the list of entities using the specified template */ std::vector CCmpTemplateManager::GetEntitiesUsingTemplate(std::string templateName) { std::vector entities; for (std::map::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it) { if(it->second == templateName) entities.push_back(it->first); } return entities; } void CCmpTemplateManager::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("Ownership"); permittedComponentTypes.insert("Position"); permittedComponentTypes.insert("VisualActor"); permittedComponentTypes.insert("Footprint"); permittedComponentTypes.insert("Obstruction"); permittedComponentTypes.insert("Decay"); // 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 always be visible in fog-of-war/etc CParamNode::LoadXMLString(out, "0falsetrue"); } if (corpse) { // Corpses should include decay components and un-inactivate them if (out.GetChild("Entity").GetChild("Decay").IsOk()) CParamNode::LoadXMLString(out, ""); // Corpses shouldn't display silhouettes (especially since they're often half underground) if (out.GetChild("Entity").GetChild("VisualActor").IsOk()) CParamNode::LoadXMLString(out, "false"); } } void CCmpTemplateManager::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("Armour"); permittedComponentTypes.insert("Health"); permittedComponentTypes.insert("StatusBars"); permittedComponentTypes.insert("OverlayRenderer"); permittedComponentTypes.insert("Decay"); permittedComponentTypes.insert("Cost"); permittedComponentTypes.insert("Sound"); permittedComponentTypes.insert("Vision"); permittedComponentTypes.insert("AIProxy"); 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"); } Index: ps/trunk/binaries/data/mods/_test.sim/simulation/templates/special/actor.xml =================================================================== --- ps/trunk/binaries/data/mods/_test.sim/simulation/templates/special/actor.xml (nonexistent) +++ ps/trunk/binaries/data/mods/_test.sim/simulation/templates/special/actor.xml (revision 9183) @@ -0,0 +1,7 @@ + + + + false + false + + Index: ps/trunk/binaries/data/mods/public/simulation/templates/special/actor.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/special/actor.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/special/actor.xml (revision 9183) @@ -0,0 +1,12 @@ + + + + 0 + upright + false + + + false + false + +