diff --git a/src/Core/Resource/Cooker/CTemplateWriter.cpp b/src/Core/Resource/Cooker/CTemplateWriter.cpp index 61f4a107..2bb53cc7 100644 --- a/src/Core/Resource/Cooker/CTemplateWriter.cpp +++ b/src/Core/Resource/Cooker/CTemplateWriter.cpp @@ -11,6 +11,33 @@ CTemplateWriter::CTemplateWriter() { } +void CTemplateWriter::SavePropertyTemplate(IPropertyTemplate *pTemp) +{ + // Check for a source file in the template's hierarchy; that indicates it's part of a struct template, not a script template + TString SourceFile = pTemp->FindStructSource(); + + // Struct + if (!SourceFile.IsEmpty()) + { + CMasterTemplate *pMaster = pTemp->MasterTemplate(); + auto StructIt = pMaster->mStructTemplates.find(SourceFile); + + if (StructIt != pMaster->mStructTemplates.end()) + { + CStructTemplate *pStruct = StructIt->second; + CTemplateWriter::SaveStructTemplate(pStruct); + } + } + + // Script + else if (pTemp->ScriptTemplate()) + CTemplateWriter::SaveScriptTemplate(pTemp->ScriptTemplate()); + + // Error + else + Log::Error("Couldn't save property template " + pTemp->IDString(true) + "; no struct template source path or script template found"); +} + void CTemplateWriter::SaveAllTemplates() { // Create directory @@ -76,7 +103,7 @@ void CTemplateWriter::SaveGameTemplates(CMasterTemplate *pMaster) // Resave struct templates for (auto it = pMaster->mStructTemplates.begin(); it != pMaster->mStructTemplates.end(); it++) - SaveStructTemplate(it->second, pMaster); + SaveStructTemplate(it->second); // Resave master template XMLDocument Master; @@ -202,7 +229,7 @@ void CTemplateWriter::SaveScriptTemplate(CScriptTemplate *pTemp) pRoot->LinkEndChild(pName); // Write properties - SaveProperties(&ScriptXML, pRoot, pTemp->mpBaseStruct, pMaster); + SaveProperties(&ScriptXML, pRoot, pTemp->mpBaseStruct); // States/Messages [todo] XMLElement *pStates = ScriptXML.NewElement("states"); @@ -345,9 +372,10 @@ void CTemplateWriter::SaveScriptTemplate(CScriptTemplate *pTemp) ScriptXML.SaveFile(*OutFile); } -void CTemplateWriter::SaveStructTemplate(CStructTemplate *pTemp, CMasterTemplate *pMaster) +void CTemplateWriter::SaveStructTemplate(CStructTemplate *pTemp) { // Create directory + CMasterTemplate *pMaster = pTemp->MasterTemplate(); TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile; TString OutDir = OutFile.GetFileDirectory(); TString Name = OutFile.GetFileName(false); @@ -364,13 +392,14 @@ void CTemplateWriter::SaveStructTemplate(CStructTemplate *pTemp, CMasterTemplate pRoot->SetAttribute("type", (pTemp->IsSingleProperty() ? "single" : "multi")); StructXML.LinkEndChild(pRoot); - SaveProperties(&StructXML, pRoot, pTemp, pMaster); + SaveProperties(&StructXML, pRoot, pTemp); StructXML.SaveFile(*OutFile); } -void CTemplateWriter::SaveEnumTemplate(CEnumTemplate *pTemp, CMasterTemplate *pMaster) +void CTemplateWriter::SaveEnumTemplate(CEnumTemplate *pTemp) { // Create directory + CMasterTemplate *pMaster = pTemp->MasterTemplate(); TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile; TString OutDir = OutFile.GetFileDirectory(); TString Name = OutFile.GetFileName(false); @@ -390,9 +419,10 @@ void CTemplateWriter::SaveEnumTemplate(CEnumTemplate *pTemp, CMasterTemplate *pM EnumXML.SaveFile(*OutFile); } -void CTemplateWriter::SaveBitfieldTemplate(CBitfieldTemplate *pTemp, CMasterTemplate *pMaster) +void CTemplateWriter::SaveBitfieldTemplate(CBitfieldTemplate *pTemp) { // Create directory + CMasterTemplate *pMaster = pTemp->MasterTemplate(); TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile; TString OutDir = OutFile.GetFileDirectory(); TString Name = pTemp->mSourceFile.GetFileName(false); @@ -412,7 +442,7 @@ void CTemplateWriter::SaveBitfieldTemplate(CBitfieldTemplate *pTemp, CMasterTemp BitfieldXML.SaveFile(*OutFile); } -void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CStructTemplate *pTemp, CMasterTemplate *pMaster) +void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CStructTemplate *pTemp) { // Create base element XMLElement *pPropsBlock = pDoc->NewElement("properties"); @@ -447,7 +477,7 @@ void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CSt // Name TString Name = pProp->Name(); - if (pMaster->GetGame() >= eEchoesDemo && ID > 0xFF) + if (pProp->Game() >= eEchoesDemo && ID > 0xFF) { TString MasterName = CMasterTemplate::GetPropertyName(ID); @@ -471,6 +501,7 @@ void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CSt pElem->SetAttribute("type", *PropEnumToPropString(pProp->Type())); // Versions + CMasterTemplate *pMaster = pProp->MasterTemplate(); u32 NumVersions = pProp->mAllowedVersions.size(); if (NumVersions > 0 && NumVersions != pMaster->mGameVersions.size()) @@ -490,7 +521,7 @@ void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CSt } // Default - if (pProp->CanHaveDefault() && pMaster->GetGame() >= eEchoesDemo) + if (pProp->CanHaveDefault() && pProp->Game() >= eEchoesDemo) { XMLElement *pDefault = pDoc->NewElement("default"); pDefault->SetText(*pProp->DefaultToString()); @@ -561,7 +592,7 @@ void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CSt else { - SaveEnumTemplate(pEnum, pMaster); + SaveEnumTemplate(pEnum); pElem->SetAttribute("template", *pEnum->mSourceFile); } } @@ -576,7 +607,7 @@ void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CSt else { - SaveBitfieldTemplate(pBitfield, pMaster); + SaveBitfieldTemplate(pBitfield); pElem->SetAttribute("template", *pBitfield->mSourceFile); } } @@ -601,14 +632,14 @@ void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CSt CStructTemplate *pStruct = static_cast(pProp); if (pStruct->mSourceFile.IsEmpty()) - SaveProperties(pDoc, pElem, pStruct, pMaster); + SaveProperties(pDoc, pElem, pStruct); else { - auto it = pMaster->mStructTemplates.find(pStruct->mSourceFile); + CStructTemplate *pOriginal = pMaster->GetStructAtSource(pStruct->mSourceFile); - if (it != pMaster->mStructTemplates.end()) - SavePropertyOverrides(pDoc, pElem, pStruct, it->second); + if (pOriginal) + SavePropertyOverrides(pDoc, pElem, pStruct, pOriginal); pElem->SetAttribute("template", *pStruct->mSourceFile); } diff --git a/src/Core/Resource/Cooker/CTemplateWriter.h b/src/Core/Resource/Cooker/CTemplateWriter.h index 2f48f24b..156e343a 100644 --- a/src/Core/Resource/Cooker/CTemplateWriter.h +++ b/src/Core/Resource/Cooker/CTemplateWriter.h @@ -11,14 +11,15 @@ class CTemplateWriter static TString smTemplatesDir; public: + static void SavePropertyTemplate(IPropertyTemplate *pTemp); static void SaveAllTemplates(); static void SaveGameTemplates(CMasterTemplate *pMaster); static void SavePropertyList(); static void SaveScriptTemplate(CScriptTemplate *pTemp); - static void SaveStructTemplate(CStructTemplate *pTemp, CMasterTemplate *pMaster); - static void SaveEnumTemplate(CEnumTemplate *pTemp, CMasterTemplate *pMaster); - static void SaveBitfieldTemplate(CBitfieldTemplate *pTemp, CMasterTemplate *pMaster); - static void SaveProperties(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CStructTemplate *pTemp, CMasterTemplate *pMaster); + static void SaveStructTemplate(CStructTemplate *pTemp); + static void SaveEnumTemplate(CEnumTemplate *pTemp); + static void SaveBitfieldTemplate(CBitfieldTemplate *pTemp); + static void SaveProperties(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CStructTemplate *pTemp); static void SavePropertyOverrides(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CStructTemplate *pStruct, CStructTemplate *pOriginal); static void SaveEnumerators(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CEnumTemplate *pTemp); static void SaveBitFlags(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CBitfieldTemplate *pTemp); diff --git a/src/Core/Resource/Factory/CTemplateLoader.cpp b/src/Core/Resource/Factory/CTemplateLoader.cpp index 541614f6..dffce4f3 100644 --- a/src/Core/Resource/Factory/CTemplateLoader.cpp +++ b/src/Core/Resource/Factory/CTemplateLoader.cpp @@ -9,7 +9,7 @@ const TString CTemplateLoader::mskGameListPath = CTemplateLoader::mskTemplatesDi using namespace tinyxml2; -IPropertyTemplate* CTemplateLoader::LoadProperty(XMLElement *pElem, CStructTemplate *pStruct, const TString& rkTemplateName) +IPropertyTemplate* CTemplateLoader::LoadProperty(XMLElement *pElem, CScriptTemplate *pScript, CStructTemplate *pStruct, const TString& rkTemplateName) { TString NodeType = TString(pElem->Name()).ToLower(); TString IDAttr = TString(pElem->Attribute("ID")).ToLower(); @@ -60,7 +60,7 @@ IPropertyTemplate* CTemplateLoader::LoadProperty(XMLElement *pElem, CStructTempl return nullptr; } - pProp = CreateProperty(ID, Type, Name, pStruct); + pProp = CreateProperty(ID, Type, Name, pScript, pStruct); if (!pProp) { @@ -162,15 +162,15 @@ IPropertyTemplate* CTemplateLoader::LoadProperty(XMLElement *pElem, CStructTempl XMLElement *pProperties = pElem->FirstChildElement("properties"); if (pProperties) - LoadProperties(pProperties, pStruct, rkTemplateName); + LoadProperties(pProperties, pScript, pStruct, rkTemplateName); } CMasterTemplate::AddProperty(pProp, mMasterDir + rkTemplateName); return pProp; } -#define CREATE_PROP_TEMP(Class) new Class(ID, rkName, eNoCookPreference, pStruct) -IPropertyTemplate* CTemplateLoader::CreateProperty(u32 ID, EPropertyType Type, const TString& rkName, CStructTemplate *pStruct) +#define CREATE_PROP_TEMP(Class) new Class(ID, rkName, eNoCookPreference, pScript, mpMaster, pStruct) +IPropertyTemplate* CTemplateLoader::CreateProperty(u32 ID, EPropertyType Type, const TString& rkName, CScriptTemplate *pScript, CStructTemplate *pStruct) { IPropertyTemplate *pOut = pStruct->PropertyByID(ID); @@ -216,7 +216,7 @@ void CTemplateLoader::LoadStructTemplate(const TString& rkTemplateFileName, CStr if (pStruct->Type() == eStructProperty) { - pSource = new CStructTemplate(-1, nullptr); + pSource = new CStructTemplate(-1, nullptr, mpMaster); pRootElem = Doc.FirstChildElement("struct"); if (!pRootElem) @@ -238,6 +238,7 @@ void CTemplateLoader::LoadStructTemplate(const TString& rkTemplateFileName, CStr else if (pStruct->Type() == eArrayProperty) { + pSource = new CArrayTemplate(-1, nullptr, mpMaster); pRootElem = Doc.FirstChildElement("array"); if (!pRootElem) @@ -246,15 +247,19 @@ void CTemplateLoader::LoadStructTemplate(const TString& rkTemplateFileName, CStr return; } } + pSource->mSourceFile = rkTemplateFileName; + + TString NameAttr = TString(pRootElem->Attribute("name")); + if (!NameAttr.IsEmpty()) + pSource->mName = NameAttr; // Read sub-properties XMLElement *pSubPropsElem = pRootElem->FirstChildElement("properties"); if (pSubPropsElem) { - LoadProperties(pSubPropsElem, pSource, rkTemplateFileName); + LoadProperties(pSubPropsElem, nullptr, pSource, rkTemplateFileName); mpMaster->mStructTemplates[rkTemplateFileName] = pSource; - pSource->mSourceFile = rkTemplateFileName; } else @@ -278,6 +283,7 @@ void CTemplateLoader::LoadEnumTemplate(const TString& rkTemplateFileName, CEnumT if (!Doc.Error()) { + pEnum->mSourceFile = rkTemplateFileName; XMLElement *pRootElem = Doc.FirstChildElement("enum"); if (!pRootElem) @@ -294,7 +300,6 @@ void CTemplateLoader::LoadEnumTemplate(const TString& rkTemplateFileName, CEnumT else Log::Error(rkTemplateFileName + ": There is no \"enumerators\" block element"); - pEnum->mSourceFile = rkTemplateFileName; } } @@ -305,6 +310,7 @@ void CTemplateLoader::LoadBitfieldTemplate(const TString& rkTemplateFileName, CB if (!Doc.Error()) { + pBitfield->mSourceFile = rkTemplateFileName; XMLElement *pRootElem = Doc.FirstChildElement("bitfield"); if (!pRootElem) @@ -320,12 +326,10 @@ void CTemplateLoader::LoadBitfieldTemplate(const TString& rkTemplateFileName, CB else Log::Error(rkTemplateFileName + ": There is no \"flags\" block element"); - - pBitfield->mSourceFile = rkTemplateFileName; } } -void CTemplateLoader::LoadProperties(XMLElement *pPropertiesElem, CStructTemplate *pStruct, const TString& rkTemplateName) +void CTemplateLoader::LoadProperties(XMLElement *pPropertiesElem, CScriptTemplate *pScript, CStructTemplate *pStruct, const TString& rkTemplateName) { XMLElement *pChild = pPropertiesElem->FirstChildElement(); @@ -341,7 +345,7 @@ void CTemplateLoader::LoadProperties(XMLElement *pPropertiesElem, CStructTemplat // LoadProperty adds newly created properties to the struct, so we don't need to do anything other than call it for each sub-element. else { - LoadProperty(pChild, pStruct, rkTemplateName); + LoadProperty(pChild, pScript, pStruct, rkTemplateName); } pChild = pChild->NextSiblingElement(); @@ -406,7 +410,7 @@ CScriptTemplate* CTemplateLoader::LoadScriptTemplate(XMLDocument *pDoc, const TS { CScriptTemplate *pScript = new CScriptTemplate(mpMaster); pScript->mObjectID = ObjectID; - pScript->mpBaseStruct = new CStructTemplate(-1, nullptr); + pScript->mpBaseStruct = new CStructTemplate(-1, nullptr, mpMaster); pScript->mSourceFile = rkTemplateName; XMLElement *pRoot = pDoc->FirstChildElement("ScriptTemplate"); @@ -424,7 +428,7 @@ CScriptTemplate* CTemplateLoader::LoadScriptTemplate(XMLDocument *pDoc, const TS XMLElement *pPropsElem = pRoot->FirstChildElement("properties"); if (pPropsElem) - LoadProperties(pPropsElem, pScript->mpBaseStruct, rkTemplateName); + LoadProperties(pPropsElem, pScript, pScript->mpBaseStruct, rkTemplateName); else Log::Error(rkTemplateName + ": There is no \"properties\" block element"); diff --git a/src/Core/Resource/Factory/CTemplateLoader.h b/src/Core/Resource/Factory/CTemplateLoader.h index 2026892b..848b1782 100644 --- a/src/Core/Resource/Factory/CTemplateLoader.h +++ b/src/Core/Resource/Factory/CTemplateLoader.h @@ -20,14 +20,14 @@ class CTemplateLoader : mTemplatesDir(rkTemplatesDir) {} // Load Property - IPropertyTemplate* LoadProperty(tinyxml2::XMLElement *pElem, CStructTemplate *pParentStruct, const TString& rkTemplateName); - IPropertyTemplate* CreateProperty(u32 ID, EPropertyType Type, const TString& rkName, CStructTemplate *pStruct); + IPropertyTemplate* LoadProperty(tinyxml2::XMLElement *pElem, CScriptTemplate *pScript, CStructTemplate *pParentStruct, const TString& rkTemplateName); + IPropertyTemplate* CreateProperty(u32 ID, EPropertyType Type, const TString& rkName, CScriptTemplate *pScript, CStructTemplate *pStruct); void LoadStructTemplate(const TString& rkTemplateFileName, CStructTemplate *pStruct); void LoadEnumTemplate(const TString& rkTemplateFileName, CEnumTemplate *pEnum); void LoadBitfieldTemplate(const TString& rkTemplateFileName, CBitfieldTemplate *pBitfield); - void LoadProperties(tinyxml2::XMLElement *pPropertiesElem, CStructTemplate *pStruct, const TString& rkTemplateName); + void LoadProperties(tinyxml2::XMLElement *pPropertiesElem, CScriptTemplate *pScript, CStructTemplate *pStruct, const TString& rkTemplateName); void LoadEnumerators(tinyxml2::XMLElement *pEnumeratorsElem, CEnumTemplate *pEnum, const TString& rkTemplateName); void LoadBitFlags(tinyxml2::XMLElement *pFlagsElem, CBitfieldTemplate *pBitfield, const TString& rkTemplateName); diff --git a/src/Core/Resource/Script/CMasterTemplate.cpp b/src/Core/Resource/Script/CMasterTemplate.cpp index 01357892..dfe553cd 100644 --- a/src/Core/Resource/Script/CMasterTemplate.cpp +++ b/src/Core/Resource/Script/CMasterTemplate.cpp @@ -104,6 +104,16 @@ TString CMasterTemplate::GetDirectory() const return mSourceFile.GetFileDirectory(); } +CStructTemplate* CMasterTemplate::GetStructAtSource(const TString& rkSource) +{ + auto InfoIt = mStructTemplates.find(rkSource); + + if (InfoIt != mStructTemplates.end()) + return InfoIt->second; + + else return nullptr; +} + bool CMasterTemplate::IsLoadedSuccessfully() { return mFullyLoaded; @@ -140,46 +150,92 @@ TString CMasterTemplate::GetPropertyName(u32 PropertyID) return "Unknown"; } -void CMasterTemplate::AddProperty(IPropertyTemplate *pTemp, const TString& rkTemplateName) +u32 CMasterTemplate::CreatePropertyID(IPropertyTemplate *pTemp) { - auto it = smIDMap.find(pTemp->PropertyID()); + // MP1 properties don't have IDs so we can use this function to create one to track instances of a particular property. + // To ensure the IDs are unique we'll create a hash using two things: the struct source file and the ID string (relative to the struct). + TString IDString = pTemp->IDString(false); + TString Source; + CStructTemplate *pStruct = pTemp->Parent(); + + while (pStruct) + { + Source = pStruct->SourceFile(); + if (!Source.IsEmpty()) break; + IDString.Prepend(pStruct->IDString(false) + ":"); + pStruct = pStruct->Parent(); + } + + return IDString.Hash32() * Source.Hash32(); +} + +void CMasterTemplate::AddProperty(IPropertyTemplate *pTemp, const TString& rkTemplateName /*= ""*/) +{ + u32 ID; + + if (pTemp->Game() >= eEchoesDemo) + ID = pTemp->PropertyID(); + + // Use a different ID for MP1 + else + { + // For MP1 we only really need to track properties that come from struct templates. + if (!pTemp->IsFromStructTemplate()) return; + else ID = CreatePropertyID(pTemp); + } + + auto it = smIDMap.find(ID); // Add this property/template to existing ID info if (it != smIDMap.end()) { SPropIDInfo& rInfo = it->second; - bool NewTemplate = true; + rInfo.PropertyList.push_back(pTemp); - for (u32 iTemp = 0; iTemp < rInfo.XMLList.size(); iTemp++) + if (!rkTemplateName.IsEmpty()) { - if (rInfo.XMLList[iTemp] == rkTemplateName) + bool NewTemplate = true; + + for (u32 iTemp = 0; iTemp < rInfo.XMLList.size(); iTemp++) { - NewTemplate = false; - break; + if (rInfo.XMLList[iTemp] == rkTemplateName) + { + NewTemplate = false; + break; + } } + + if (NewTemplate) + rInfo.XMLList.push_back(rkTemplateName); } - - if (NewTemplate) - rInfo.XMLList.push_back(rkTemplateName); - - it->second.PropertyList.push_back(pTemp); } // Create new ID info else { SPropIDInfo Info; - Info.XMLList.push_back(rkTemplateName); + if (!rkTemplateName.IsEmpty()) Info.XMLList.push_back(rkTemplateName); Info.PropertyList.push_back(pTemp); - smIDMap[pTemp->PropertyID()] = Info; + smIDMap[ID] = Info; } } -void CMasterTemplate::RenameProperty(u32 ID, const TString& rkNewName) +void CMasterTemplate::RenameProperty(IPropertyTemplate *pTemp, const TString& rkNewName) { - auto NameIt = smPropertyNames.find(ID); - TString CurName = (NameIt == smPropertyNames.end() ? "" : NameIt->second); + u32 ID = pTemp->PropertyID(); + if (ID <= 0xFF) ID = CreatePropertyID(pTemp); + // Master name list + auto NameIt = smPropertyNames.find(ID); + TString Original; + + if (NameIt != smPropertyNames.end()) + { + Original = NameIt->second; + smPropertyNames[ID] = rkNewName; + } + + // Properties auto InfoIt = smIDMap.find(ID); if (InfoIt != smIDMap.end()) @@ -188,18 +244,13 @@ void CMasterTemplate::RenameProperty(u32 ID, const TString& rkNewName) for (u32 iTemp = 0; iTemp < rkInfo.PropertyList.size(); iTemp++) { - IPropertyTemplate *pTemp = rkInfo.PropertyList[iTemp]; - - if (pTemp->Name() == CurName) - pTemp->SetName(rkNewName); + if (Original.IsEmpty() || rkInfo.PropertyList[iTemp]->Name() == Original) + rkInfo.PropertyList[iTemp]->SetName(rkNewName); } } - - if (NameIt != smPropertyNames.end()) - smPropertyNames[ID] = rkNewName; } -std::vector CMasterTemplate::GetTemplatesUsingID(u32 ID) +std::vector CMasterTemplate::GetXMLsUsingID(u32 ID) { auto InfoIt = smIDMap.find(ID); @@ -212,6 +263,21 @@ std::vector CMasterTemplate::GetTemplatesUsingID(u32 ID) return std::vector(); } +const std::vector* CMasterTemplate::GetTemplatesWithMatchingID(IPropertyTemplate *pTemp) +{ + u32 ID = pTemp->PropertyID(); + if (ID <= 0xFF) ID = CreatePropertyID(pTemp); + + auto InfoIt = smIDMap.find(ID); + + if (InfoIt != smIDMap.end()) + { + const SPropIDInfo& rkInfo = InfoIt->second; + return &rkInfo.PropertyList; + } + return nullptr; +} + std::map CMasterTemplate::smIDMap; std::map CMasterTemplate::smMasterMap; std::map CMasterTemplate::smPropertyNames; diff --git a/src/Core/Resource/Script/CMasterTemplate.h b/src/Core/Resource/Script/CMasterTemplate.h index 740ad581..36932e42 100644 --- a/src/Core/Resource/Script/CMasterTemplate.h +++ b/src/Core/Resource/Script/CMasterTemplate.h @@ -53,14 +53,17 @@ public: TString MessageByID(const CFourCC& MessageID); TString MessageByIndex(u32 Index); TString GetDirectory() const; + CStructTemplate* GetStructAtSource(const TString& rkSource); bool IsLoadedSuccessfully(); static CMasterTemplate* GetMasterForGame(EGame Game); static std::list GetMasterList(); static TString GetPropertyName(u32 PropertyID); - static void AddProperty(IPropertyTemplate *pTemp, const TString& rkTemplateName); - static void RenameProperty(u32 ID, const TString& rkNewName); - static std::vector GetTemplatesUsingID(u32 ID); + static u32 CreatePropertyID(IPropertyTemplate *pTemp); + static void AddProperty(IPropertyTemplate *pTemp, const TString& rkTemplateName = ""); + static void RenameProperty(IPropertyTemplate *pTemp, const TString& rkNewName); + static std::vector GetXMLsUsingID(u32 ID); + static const std::vector* GetTemplatesWithMatchingID(IPropertyTemplate *pTemp); }; // ************ INLINE ************ diff --git a/src/Core/Resource/Script/CScriptTemplate.h b/src/Core/Resource/Script/CScriptTemplate.h index cb5c7192..ef139b24 100644 --- a/src/Core/Resource/Script/CScriptTemplate.h +++ b/src/Core/Resource/Script/CScriptTemplate.h @@ -117,6 +117,7 @@ public: CCollisionMeshGroup* FindCollision(CPropertyStruct *pProperties); bool HasInGameModel(CPropertyStruct *pProperties); + inline TString SourceFile() const { return mSourceFile; } inline bool HasName() const { return !mNameIDString.IsEmpty(); } inline bool HasPosition() const { return !mPositionIDString.IsEmpty(); } inline bool HasRotation() const { return !mRotationIDString.IsEmpty(); } diff --git a/src/Core/Resource/Script/IPropertyTemplate.cpp b/src/Core/Resource/Script/IPropertyTemplate.cpp index e31f1665..b21899e8 100644 --- a/src/Core/Resource/Script/IPropertyTemplate.cpp +++ b/src/Core/Resource/Script/IPropertyTemplate.cpp @@ -1,7 +1,13 @@ #include "IPropertyTemplate.h" +#include "CMasterTemplate.h" #include // ************ IPropertyTemplate ************ +EGame IPropertyTemplate::Game() const +{ + return (mpMasterTemplate ? mpMasterTemplate->GetGame() : eUnknownVersion); +} + bool IPropertyTemplate::IsInVersion(u32 Version) const { if (mAllowedVersions.empty()) @@ -32,6 +38,45 @@ TIDString IPropertyTemplate::IDString(bool FullPath) const else return ""; } +bool IPropertyTemplate::IsDescendantOf(const CStructTemplate *pStruct) const +{ + CStructTemplate *pParent = mpParent; + + while (pParent) + { + if (pParent == pStruct) return true; + pParent = pParent->Parent(); + } + + return false; +} + +bool IPropertyTemplate::IsFromStructTemplate() const +{ + const CStructTemplate *pParent = Parent(); + + while (pParent) + { + if (!pParent->SourceFile().IsEmpty()) return true; + pParent = pParent->Parent(); + } + + return false; +} + +TString IPropertyTemplate::FindStructSource() const +{ + const CStructTemplate *pkStruct = mpParent; + + while (pkStruct) + { + if (!pkStruct->SourceFile().IsEmpty()) return pkStruct->SourceFile(); + pkStruct = pkStruct->Parent(); + } + + return ""; +} + CStructTemplate* IPropertyTemplate::RootStruct() { if (mpParent) return mpParent->RootStruct(); @@ -40,19 +85,19 @@ CStructTemplate* IPropertyTemplate::RootStruct() } // ************ CStructTemplate ************ -bool CStructTemplate::IsSingleProperty() const +void CStructTemplate::CopyStructData(const CStructTemplate *pkStruct) { - return mIsSingleProperty; -} + mVersionPropertyCounts = pkStruct->mVersionPropertyCounts; + mIsSingleProperty = pkStruct->mIsSingleProperty; + mSourceFile = pkStruct->mSourceFile; -u32 CStructTemplate::Count() const -{ - return mSubProperties.size(); -} + mSubProperties.resize(pkStruct->mSubProperties.size()); -u32 CStructTemplate::NumVersions() -{ - return mVersionPropertyCounts.size(); + for (u32 iSub = 0; iSub < pkStruct->mSubProperties.size(); iSub++) + { + mSubProperties[iSub] = pkStruct->mSubProperties[iSub]->Clone(mpScriptTemplate, this); + CMasterTemplate::AddProperty(mSubProperties[iSub]); + } } u32 CStructTemplate::PropertyCountForVersion(u32 Version) diff --git a/src/Core/Resource/Script/IPropertyTemplate.h b/src/Core/Resource/Script/IPropertyTemplate.h index 5588b3f0..e63758ee 100644 --- a/src/Core/Resource/Script/IPropertyTemplate.h +++ b/src/Core/Resource/Script/IPropertyTemplate.h @@ -12,6 +12,7 @@ #include typedef TString TIDString; +class CMasterTemplate; class CStructTemplate; class IProperty; @@ -31,6 +32,8 @@ class IPropertyTemplate protected: CStructTemplate *mpParent; + CScriptTemplate *mpScriptTemplate; + CMasterTemplate *mpMasterTemplate; TString mName; TString mDescription; u32 mID; @@ -38,17 +41,21 @@ protected: std::vector mAllowedVersions; public: - IPropertyTemplate(u32 ID, CStructTemplate *pParent = 0) + IPropertyTemplate(u32 ID, CScriptTemplate *pScript, CMasterTemplate *pMaster, CStructTemplate *pParent = 0) : mID(ID) , mpParent(pParent) + , mpScriptTemplate(pScript) + , mpMasterTemplate(pMaster) , mName("UNSET PROPERTY NAME") , mCookPreference(eNoCookPreference) { } - IPropertyTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CStructTemplate *pParent = 0) + IPropertyTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CScriptTemplate *pScript, CMasterTemplate *pMaster, CStructTemplate *pParent = 0) : mID(ID) , mpParent(pParent) + , mpScriptTemplate(pScript) + , mpMasterTemplate(pMaster) , mName(rkName) , mCookPreference(CookPreference) { @@ -58,7 +65,7 @@ public: virtual bool CanHaveDefault() const = 0; virtual bool IsNumerical() const = 0; virtual IProperty* InstantiateProperty(CPropertyStruct *pParent) = 0; - virtual IPropertyTemplate* Clone(CStructTemplate *pParent = 0) const = 0; + virtual IPropertyTemplate* Clone(CScriptTemplate *pScript, CStructTemplate *pParent = 0) const = 0; virtual void Copy(const IPropertyTemplate *pkTemp) { @@ -104,8 +111,12 @@ public: mDescription = rkValue; } + EGame Game() const; bool IsInVersion(u32 Version) const; TIDString IDString(bool FullPath) const; + bool IsDescendantOf(const CStructTemplate *pStruct) const; + bool IsFromStructTemplate() const; + TString FindStructSource() const; CStructTemplate* RootStruct(); // Inline Accessors @@ -114,16 +125,19 @@ public: inline u32 PropertyID() const { return mID; } inline ECookPreference CookPreference() const { return mCookPreference; } inline CStructTemplate* Parent() const { return mpParent; } + inline CScriptTemplate* ScriptTemplate() const { return mpScriptTemplate; } + inline CMasterTemplate* MasterTemplate() const { return mpMasterTemplate; } inline void SetName(const TString& rkName) { mName = rkName; } inline void SetDescription(const TString& rkDesc) { mDescription = rkDesc; } }; // Macro for defining reimplementations of IPropertyTemplate::Clone(), which are usually identical to each other aside from the class being instantiated -#define DEFINE_TEMPLATE_CLONE(ClassName) \ - virtual IPropertyTemplate* Clone(CStructTemplate *pParent = 0) const \ +#define IMPLEMENT_TEMPLATE_CLONE(ClassName) \ + virtual IPropertyTemplate* Clone(CScriptTemplate *pScript, CStructTemplate *pParent = 0) const \ { \ if (!pParent) pParent = mpParent; \ - ClassName *pTemp = new ClassName(mID, pParent); \ + if (!pScript) pScript = mpScriptTemplate; \ + ClassName *pTemp = new ClassName(mID, pScript, mpMasterTemplate, pParent); \ pTemp->Copy(this); \ return pTemp; \ } @@ -140,11 +154,11 @@ protected: ValueClass mDefaultValue; public: - TTypedPropertyTemplate(u32 ID, CStructTemplate *pParent = 0) - : IPropertyTemplate(ID, pParent) {} + TTypedPropertyTemplate(u32 ID, CScriptTemplate *pScript, CMasterTemplate *pMaster, CStructTemplate *pParent = 0) + : IPropertyTemplate(ID, pScript, pMaster, pParent) {} - TTypedPropertyTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CStructTemplate *pParent = 0) - : IPropertyTemplate(ID, rkName, CookPreference, pParent) {} + TTypedPropertyTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CScriptTemplate *pScript, CMasterTemplate *pMaster, CStructTemplate *pParent = 0) + : IPropertyTemplate(ID, rkName, CookPreference, pScript, pMaster, pParent) {} virtual EPropertyType Type() const { return PropTypeEnum; } virtual bool CanHaveDefault() const { return CanHaveDefaultValue; } @@ -159,7 +173,7 @@ public: return pOut; } - DEFINE_TEMPLATE_CLONE(TTypedPropertyTemplate) + IMPLEMENT_TEMPLATE_CLONE(TTypedPropertyTemplate) virtual void Copy(const IPropertyTemplate *pkTemp) { @@ -210,12 +224,12 @@ class TNumericalPropertyTemplate : public TTypedPropertyTemplatemSourceFile) ); } - inline u32 NumEnumerators() const - { - return mEnumerators.size(); - } + inline TString SourceFile() const { return mSourceFile; } + inline u32 NumEnumerators() const { return mEnumerators.size(); } u32 EnumeratorIndex(u32 enumID) const { @@ -469,13 +481,13 @@ class CBitfieldTemplate : public TTypedPropertyTemplatemSourceFile) ); } - u32 NumFlags() const { return mBitFlags.size(); } - TString FlagName(u32 index) const { return mBitFlags[index].Name; } - u32 FlagMask(u32 index) const { return mBitFlags[index].Mask; } + inline TString SourceFile() const { return mSourceFile; } + inline u32 NumFlags() const { return mBitFlags.size(); } + inline TString FlagName(u32 index) const { return mBitFlags[index].Name; } + inline u32 FlagMask(u32 index) const { return mBitFlags[index].Mask; } }; // CStructTemplate - Defines structs composed of multiple sub-properties. @@ -529,12 +542,12 @@ protected: void DetermineVersionPropertyCounts(); public: - CStructTemplate(u32 ID, CStructTemplate *pParent = 0) - : IPropertyTemplate(ID, pParent) + CStructTemplate(u32 ID, CScriptTemplate *pScript, CMasterTemplate *pMaster, CStructTemplate *pParent = 0) + : IPropertyTemplate(ID, pScript, pMaster, pParent) , mIsSingleProperty(false) {} - CStructTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CStructTemplate *pParent = 0) - : IPropertyTemplate(ID, rkName, CookPreference, pParent) + CStructTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CScriptTemplate *pScript, CMasterTemplate *pMaster, CStructTemplate *pParent = 0) + : IPropertyTemplate(ID, rkName, CookPreference, pScript, pMaster, pParent) , mIsSingleProperty(false) {} ~CStructTemplate() @@ -560,7 +573,7 @@ public: return pStruct; } - DEFINE_TEMPLATE_CLONE(CStructTemplate) + IMPLEMENT_TEMPLATE_CLONE(CStructTemplate) virtual void Copy(const IPropertyTemplate *pkTemp) { @@ -570,17 +583,7 @@ public: CopyStructData(pkStruct); } - void CopyStructData(const CStructTemplate *pkStruct) - { - mVersionPropertyCounts = pkStruct->mVersionPropertyCounts; - mIsSingleProperty = pkStruct->mIsSingleProperty; - mSourceFile = pkStruct->mSourceFile; - - mSubProperties.resize(pkStruct->mSubProperties.size()); - - for (u32 iSub = 0; iSub < pkStruct->mSubProperties.size(); iSub++) - mSubProperties[iSub] = pkStruct->mSubProperties[iSub]->Clone(this); - } + void CopyStructData(const CStructTemplate *pkStruct); virtual bool Matches(const IPropertyTemplate *pkTemp) const { @@ -614,9 +617,11 @@ public: return false; } - bool IsSingleProperty() const; - u32 Count() const; - u32 NumVersions(); + inline TString SourceFile() const { return mSourceFile; } + inline bool IsSingleProperty() const { return mIsSingleProperty; } + inline u32 Count() const { return mSubProperties.size(); } + inline u32 NumVersions() const { return mVersionPropertyCounts.size(); } + u32 PropertyCountForVersion(u32 Version); u32 VersionForPropertyCount(u32 PropCount); IPropertyTemplate* PropertyByIndex(u32 index); @@ -638,14 +643,14 @@ class CArrayTemplate : public CStructTemplate TString mElementName; public: - CArrayTemplate(u32 ID, CStructTemplate *pParent = 0) - : CStructTemplate(ID, pParent) + CArrayTemplate(u32 ID, CScriptTemplate *pScript, CMasterTemplate *pMaster, CStructTemplate *pParent = 0) + : CStructTemplate(ID, pScript, pMaster, pParent) { mIsSingleProperty = true; } - CArrayTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CStructTemplate *pParent = 0) - : CStructTemplate(ID, rkName, CookPreference, pParent) + CArrayTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CScriptTemplate *pScript, CMasterTemplate *pMaster, CStructTemplate *pParent = 0) + : CStructTemplate(ID, rkName, CookPreference, pScript, pMaster, pParent) { mIsSingleProperty = true; } @@ -657,12 +662,10 @@ public: return new CArrayProperty(this, pParent); } - DEFINE_TEMPLATE_CLONE(CArrayTemplate) + IMPLEMENT_TEMPLATE_CLONE(CArrayTemplate) virtual void Copy(const IPropertyTemplate *pkTemp) { - IPropertyTemplate::Copy(pkTemp); - CStructTemplate::Copy(pkTemp); mElementName = static_cast(pkTemp)->mElementName; } diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index b7f1cf71..9d8d18f9 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -139,7 +139,8 @@ HEADERS += \ Undo/CBasicPropertyCommand.h \ Undo/IUndoCommand.h \ WorldEditor/WEditorProperties.h \ - Undo/CChangeLayerCommand.h + Undo/CChangeLayerCommand.h \ + WorldEditor/CTemplateEditDialog.h # Source Files SOURCES += \ @@ -195,7 +196,8 @@ SOURCES += \ Undo/CResizeScriptArrayCommand.cpp \ Undo/CBasicPropertyCommand.cpp \ WorldEditor/WEditorProperties.cpp \ - Undo/CChangeLayerCommand.cpp + Undo/CChangeLayerCommand.cpp \ + WorldEditor/CTemplateEditDialog.cpp # UI Files FORMS += \ @@ -211,4 +213,5 @@ FORMS += \ WorldEditor/WInstancesTab.ui \ WorldEditor/WModifyTab.ui \ CErrorLogDialog.ui \ - WorldEditor/CPoiMapEditDialog.ui + WorldEditor/CPoiMapEditDialog.ui \ + WorldEditor/CTemplateEditDialog.ui diff --git a/src/Editor/PropertyEdit/CPropertyView.cpp b/src/Editor/PropertyEdit/CPropertyView.cpp index b4e27148..2400f3f5 100644 --- a/src/Editor/PropertyEdit/CPropertyView.cpp +++ b/src/Editor/PropertyEdit/CPropertyView.cpp @@ -1,13 +1,16 @@ #include "CPropertyView.h" #include "CPropertyDelegate.h" +#include "Editor/WorldEditor/CTemplateEditDialog.h" #include #include +#include #include CPropertyView::CPropertyView(QWidget *pParent) : QTreeView(pParent) , mpEditor(nullptr) + , mpMenuProperty(nullptr) { mpModel = new CPropertyModel(this); mpDelegate = new CPropertyDelegate(this); @@ -15,8 +18,13 @@ CPropertyView::CPropertyView(QWidget *pParent) setEditTriggers(AllEditTriggers); setModel(mpModel); + setContextMenuPolicy(Qt::CustomContextMenu); + mpEditTemplateAction = new QAction("Edit template", this); + connect(mpEditTemplateAction, SIGNAL(triggered()), this, SLOT(EditPropertyTemplate())); + connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(SetPersistentEditors(QModelIndex))); connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(edit(QModelIndex))); + connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CreateContextMenu(QPoint))); connect(mpModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(SetPersistentEditors(QModelIndex))); connect(mpModel, SIGNAL(PropertyModified(const QModelIndex&)), this, SLOT(OnPropertyModified(const QModelIndex&))); } @@ -196,3 +204,24 @@ void CPropertyView::OnPropertyModified(const QModelIndex& rkIndex) SetPersistentEditors(rkIndex); } } + +void CPropertyView::CreateContextMenu(const QPoint& rkPos) +{ + QModelIndex Index = indexAt(rkPos); + + if (Index.isValid()) + { + IProperty *pProp = mpModel->PropertyForIndex(Index, true); + mpMenuProperty = pProp; + + QMenu Menu; + Menu.addAction(mpEditTemplateAction); + Menu.exec(viewport()->mapToGlobal(rkPos)); + } +} + +void CPropertyView::EditPropertyTemplate() +{ + CTemplateEditDialog Dialog(mpMenuProperty->Template(), mpEditor); + Dialog.exec(); +} diff --git a/src/Editor/PropertyEdit/CPropertyView.h b/src/Editor/PropertyEdit/CPropertyView.h index df9e8577..29602de0 100644 --- a/src/Editor/PropertyEdit/CPropertyView.h +++ b/src/Editor/PropertyEdit/CPropertyView.h @@ -15,6 +15,9 @@ class CPropertyView : public QTreeView CPropertyDelegate *mpDelegate; CScriptObject *mpObject; + IProperty *mpMenuProperty; + QAction *mpEditTemplateAction; + public: CPropertyView(QWidget *pParent = 0); void setModel(QAbstractItemModel *pModel); @@ -29,6 +32,9 @@ public slots: void SetPersistentEditors(const QModelIndex& rkIndex); void ClosePersistentEditors(const QModelIndex& rkIndex); void OnPropertyModified(const QModelIndex& rkIndex); + + void CreateContextMenu(const QPoint& rkPos); + void EditPropertyTemplate(); }; #endif // CPROPERTYVIEW_H diff --git a/src/Editor/WorldEditor/CTemplateEditDialog.cpp b/src/Editor/WorldEditor/CTemplateEditDialog.cpp new file mode 100644 index 00000000..b4fab768 --- /dev/null +++ b/src/Editor/WorldEditor/CTemplateEditDialog.cpp @@ -0,0 +1,259 @@ +#include "CTemplateEditDialog.h" +#include "ui_CTemplateEditDialog.h" + +#include "Editor/UICommon.h" +#include +#include +#include + +CTemplateEditDialog::CTemplateEditDialog(IPropertyTemplate *pTemplate, QWidget *pParent) + : QDialog(pParent) + , ui(new Ui::CTemplateEditDialog) + , mpTemplate(pTemplate) +{ + ui->setupUi(this); + + mGame = pTemplate->Game(); + mOriginalName = pTemplate->Name(); + mOriginalDescription = pTemplate->Description(); + + ui->IDDisplayLabel->setText(TO_QSTRING(pTemplate->IDString(false))); + ui->PathDisplayLabel->setText(TO_QSTRING(pTemplate->IDString(true))); + ui->NameLineEdit->setText(TO_QSTRING(pTemplate->Name())); + ui->DescriptionTextEdit->setPlainText(TO_QSTRING(pTemplate->Description())); + + if (mGame <= ePrime) + { + ui->TemplatesGroupBox->hide(); + ui->RenameAllCheckBox->setText("Rename all copies of this property"); + resize(width(), minimumHeight()); + } + + else + { + CTemplateLoader::LoadAllGames(); + std::vector TemplateList = CMasterTemplate::GetXMLsUsingID(pTemplate->PropertyID()); + + for (u32 iTemp = 0; iTemp < TemplateList.size(); iTemp++) + ui->TemplatesListWidget->addItem(TO_QSTRING(TemplateList[iTemp])); + } + + TString Source; + + if (mpTemplate->Type() == eStructProperty || mpTemplate->Type() == eArrayProperty) + Source = static_cast(mpTemplate)->SourceFile(); + else if (mpTemplate->Type() == eEnumProperty) + Source = static_cast(mpTemplate)->SourceFile(); + else if (mpTemplate->Type() == eBitfieldProperty) + Source = static_cast(mpTemplate)->SourceFile(); + + if (Source.IsEmpty()) + { + CStructTemplate *pParent = mpTemplate->Parent(); + while (pParent) + { + Source = pParent->SourceFile(); + if (!Source.IsEmpty()) break; + pParent = pParent->Parent(); + } + } + + if (Source.IsEmpty()) + { + if (mpTemplate->ScriptTemplate()) + Source = mpTemplate->ScriptTemplate()->SourceFile(); + if (Source.IsEmpty()) + Source = "None"; + } + + ui->SourceFileDisplayLabel->setText(TO_QSTRING(Source)); + + connect(ui->ButtonBox, SIGNAL(accepted()), this, SLOT(ApplyChanges())); + connect(ui->ButtonBox, SIGNAL(rejected()), this, SLOT(close())); +} + +CTemplateEditDialog::~CTemplateEditDialog() +{ + delete ui; +} + +// ************ PUBLIC SLOTS ************ +void CTemplateEditDialog::ApplyChanges() +{ + FindEquivalentProperties(mpTemplate); + + bool NeedsListResave = false; + bool RenameAll = ui->RenameAllCheckBox->isChecked(); + + TString NewName = TO_TSTRING(ui->NameLineEdit->text()); + if (NewName.IsEmpty()) NewName = "Unknown"; + + if (mOriginalName != NewName) + { + // Rename properties + if (RenameAll && (mGame >= eEchoesDemo || mpTemplate->IsFromStructTemplate())) + { + CMasterTemplate::RenameProperty(mpTemplate, NewName); + + // Add modified templates to pending resave list + const std::vector *pList = CMasterTemplate::GetTemplatesWithMatchingID(mpTemplate); + + if (pList) + { + for (u32 iTemp = 0; iTemp < pList->size(); iTemp++) + AddTemplate( pList->at(iTemp) ); + } + } + + mpTemplate->SetName(NewName); // If mpTemplate has an overridden name then CMasterTemplate::RenameProperty won't touch it + + if (RenameAll && mGame >= eEchoesDemo) + NeedsListResave = true; + } + + TString NewDescription = TO_TSTRING(ui->DescriptionTextEdit->toPlainText()); + UpdateDescription(NewDescription); + + // Resave templates + foreach (CScriptTemplate *pScript, mScriptTemplatesToResave) + CTemplateWriter::SaveScriptTemplate(pScript); + + foreach (CStructTemplate *pStruct, mStructTemplatesToResave) + CTemplateWriter::SaveStructTemplate(pStruct); + + if (NeedsListResave) + CTemplateWriter::SavePropertyList(); + + close(); +} + +// ************ PROTECTED ************ +void CTemplateEditDialog::AddTemplate(IPropertyTemplate *pTemp) +{ + if (pTemp->IsFromStructTemplate()) + { + TString Source = pTemp->FindStructSource(); + + if (!Source.IsEmpty()) + { + CStructTemplate *pStruct = pTemp->MasterTemplate()->GetStructAtSource(Source); + + if (!mStructTemplatesToResave.contains(pStruct)) + mStructTemplatesToResave << pStruct; + } + } + + else + { + CScriptTemplate *pScript = pTemp->ScriptTemplate(); + + if (pScript) + { + if (!mScriptTemplatesToResave.contains(pScript)) + mScriptTemplatesToResave << pScript; + } + + else + { + Log::Error("Can't determine where property " + pTemp->IDString(true) + " comes from"); + } + } +} + +void CTemplateEditDialog::UpdateDescription(const TString& rkNewDesc) +{ + mpTemplate->SetDescription(rkNewDesc); + AddTemplate(mpTemplate); + + // Update all copies of this property in memory with the new description + TString SourceFile = mpTemplate->FindStructSource(); + + if (!SourceFile.IsEmpty()) + { + const std::vector *pkTemplates = CMasterTemplate::GetTemplatesWithMatchingID(mpTemplate); + + if (pkTemplates) + { + for (u32 iTemp = 0; iTemp < pkTemplates->size(); iTemp++) + { + IPropertyTemplate *pTemp = pkTemplates->at(iTemp); + + if (pTemp->FindStructSource() == SourceFile && pTemp->Description() == mOriginalDescription) + pTemp->SetDescription(rkNewDesc); + } + } + } + + // Update equivalent properties with new description + foreach (IPropertyTemplate *pTemp, mEquivalentProperties) + { + pTemp->SetDescription(rkNewDesc); + AddTemplate(pTemp); + } +} + +void CTemplateEditDialog::FindEquivalentProperties(IPropertyTemplate *pTemp) +{ + if (mGame <= ePrime) return; + + // Find the equivalent version of this property in other games. + CScriptTemplate *pScript = pTemp->ScriptTemplate(); + TString Source = pTemp->FindStructSource(); + + // Determine struct-relative ID string + TIDString IDString; + + if (Source.IsEmpty()) + IDString = pTemp->IDString(true); + + else + { + IDString = pTemp->IDString(false); + CStructTemplate *pParent = pTemp->Parent(); + + while (pParent) + { + if (!pParent->SourceFile().IsEmpty()) break; + IDString.Prepend(pParent->IDString(false) + ":"); + pParent = pParent->Parent(); + } + } + + QList MasterList = QList::fromStdList(CMasterTemplate::GetMasterList()); + + if (Source.IsEmpty()) + { + u32 ObjectID = pScript->ObjectID(); + + foreach (CMasterTemplate *pMaster, MasterList) + { + if (pMaster == pTemp->MasterTemplate() || pMaster->GetGame() <= ePrime) continue; + CScriptTemplate *pNewScript = pMaster->TemplateByID(ObjectID); + + if (pNewScript) + { + IPropertyTemplate *pNewTemp = pNewScript->BaseStruct()->PropertyByIDString(IDString); + + if (pNewTemp) + mEquivalentProperties << pNewTemp; + } + } + } + + else + { + foreach (CMasterTemplate *pMaster, MasterList) + { + if (pMaster == pTemp->MasterTemplate() || pMaster->GetGame() <= ePrime) continue; + CStructTemplate *pStruct = pMaster->GetStructAtSource(Source); + + if (pStruct) + { + IPropertyTemplate *pNewTemp = pStruct->PropertyByIDString(IDString); + + if (pNewTemp) + mEquivalentProperties << pNewTemp; + } + } + } +} diff --git a/src/Editor/WorldEditor/CTemplateEditDialog.h b/src/Editor/WorldEditor/CTemplateEditDialog.h new file mode 100644 index 00000000..8fbe1607 --- /dev/null +++ b/src/Editor/WorldEditor/CTemplateEditDialog.h @@ -0,0 +1,41 @@ +#ifndef CTEMPLATEEDITDIALOG_H +#define CTEMPLATEEDITDIALOG_H + +#include +#include +#include + +namespace Ui { +class CTemplateEditDialog; +} + +class CTemplateEditDialog : public QDialog +{ + Q_OBJECT + Ui::CTemplateEditDialog *ui; + + IPropertyTemplate *mpTemplate; + EGame mGame; + + TString mOriginalName; + TString mOriginalDescription; + + // These members help track what templates need to be updated and resaved after the user clicks OK + QVector mScriptTemplatesToResave; + QVector mStructTemplatesToResave; + QVector mEquivalentProperties; + +public: + CTemplateEditDialog(IPropertyTemplate *pTemplate, QWidget *pParent = 0); + ~CTemplateEditDialog(); + +public slots: + void ApplyChanges(); + +protected: + void AddTemplate(IPropertyTemplate *pTemp); + void UpdateDescription(const TString& rkNewDesc); + void FindEquivalentProperties(IPropertyTemplate *pTemp); +}; + +#endif // CTEMPLATEEDITDIALOG_H diff --git a/src/Editor/WorldEditor/CTemplateEditDialog.ui b/src/Editor/WorldEditor/CTemplateEditDialog.ui new file mode 100644 index 00000000..cd480b6a --- /dev/null +++ b/src/Editor/WorldEditor/CTemplateEditDialog.ui @@ -0,0 +1,243 @@ + + + CTemplateEditDialog + + + + 0 + 0 + 328 + 558 + + + + Edit Template + + + + + + + + ID: + + + + + + + IBeamCursor + + + QFrame::NoFrame + + + ID GOES HERE + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Path: + + + + + + + IBeamCursor + + + PATH GOES HERE + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Name: + + + + + + + + + + Description: + + + + + + + + 0 + 0 + + + + + 0 + 60 + + + + + 16777215 + 60 + + + + + + + + + + + Source File: + + + + + + + IBeamCursor + + + SOURCE FILE GOES HERE + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + 0 + 1 + + + + Templates + + + + + + This property ID is used in these templates: + + + + + + + Qt::ScrollBarAlwaysOn + + + QAbstractScrollArea::AdjustToContentsOnFirstShow + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + + + + + + + If checked, all properties in all games that use this ID will be renamed. Otherwise, only the selected property will be renamed. This option only applies to name changes. + + + Apply name changes to all properties using this ID + + + true + + + + + + + <b>WARNING: Template edits cannot be undone</b> + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + ButtonBox + accepted() + CTemplateEditDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + ButtonBox + rejected() + CTemplateEditDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/templates/Properties.xml b/templates/Properties.xml index 2912644d..6b31dfc3 100644 --- a/templates/Properties.xml +++ b/templates/Properties.xml @@ -88,7 +88,7 @@ - + @@ -649,7 +649,7 @@ - + @@ -1067,7 +1067,7 @@ - + @@ -2216,7 +2216,7 @@ - + @@ -2599,7 +2599,7 @@ - + @@ -3031,7 +3031,7 @@ - + @@ -3237,7 +3237,7 @@ - + @@ -3593,7 +3593,7 @@ - + @@ -5525,7 +5525,7 @@ - + @@ -5940,7 +5940,7 @@ - + @@ -6680,7 +6680,7 @@ - + @@ -8244,7 +8244,7 @@ - + @@ -9435,7 +9435,7 @@ - + @@ -9542,7 +9542,7 @@ - + @@ -11046,7 +11046,7 @@ - + diff --git a/templates/dkcr/Enums/InventorySlot.xml b/templates/dkcr/Enums/InventorySlot.xml deleted file mode 100644 index f01c48d0..00000000 --- a/templates/dkcr/Enums/InventorySlot.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/templates/dkcr/Enums/Item.xml b/templates/dkcr/Enums/Item.xml new file mode 100644 index 00000000..e9b8c211 --- /dev/null +++ b/templates/dkcr/Enums/Item.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/dkcr/Script/EOLDisplay.xml b/templates/dkcr/Script/EOLDisplay.xml index f99d8bc3..df5aa8ce 100644 --- a/templates/dkcr/Script/EOLDisplay.xml +++ b/templates/dkcr/Script/EOLDisplay.xml @@ -6,7 +6,7 @@ false - + 0xB22FD89B diff --git a/templates/dkcr/Script/Pickup.xml b/templates/dkcr/Script/Pickup.xml index fc2881c1..6042d10e 100644 --- a/templates/dkcr/Script/Pickup.xml +++ b/templates/dkcr/Script/Pickup.xml @@ -17,7 +17,7 @@ - + 0xB22FD89B diff --git a/templates/dkcr/Script/SpecialFunction.xml b/templates/dkcr/Script/SpecialFunction.xml index 8196a90d..eed14edc 100644 --- a/templates/dkcr/Script/SpecialFunction.xml +++ b/templates/dkcr/Script/SpecialFunction.xml @@ -76,7 +76,7 @@ 0 - + 0xB22FD89B diff --git a/templates/dkcr/Script/Timer.xml b/templates/dkcr/Script/Timer.xml index 1d3057f1..043c194c 100644 --- a/templates/dkcr/Script/Timer.xml +++ b/templates/dkcr/Script/Timer.xml @@ -8,9 +8,11 @@ 0.0 + A random value between this number and 0 will be added to the timer's start time. false + If enabled, the timer will start ticking automatically on load. Otherwise, it will need to be started with a script message. false diff --git a/templates/dkcr/Structs/BeatUpHandlerStruct.xml b/templates/dkcr/Structs/BeatUpHandlerStruct.xml index 64afb498..9f828c9f 100644 --- a/templates/dkcr/Structs/BeatUpHandlerStruct.xml +++ b/templates/dkcr/Structs/BeatUpHandlerStruct.xml @@ -1,7 +1,7 @@ - + 0xB22FD89B diff --git a/templates/dkcr/Structs/ConditionalTest.xml b/templates/dkcr/Structs/ConditionalTest.xml index a661cbfd..32e70453 100644 --- a/templates/dkcr/Structs/ConditionalTest.xml +++ b/templates/dkcr/Structs/ConditionalTest.xml @@ -7,7 +7,7 @@ false - + 0xB22FD89B diff --git a/templates/dkcr/Structs/UnknownStruct41.xml b/templates/dkcr/Structs/UnknownStruct41.xml index 4bbd9e05..4f9f1a29 100644 --- a/templates/dkcr/Structs/UnknownStruct41.xml +++ b/templates/dkcr/Structs/UnknownStruct41.xml @@ -1,7 +1,7 @@ - + 0xB22FD89B diff --git a/templates/mp1/Script/PointOfInterest.xml b/templates/mp1/Script/PointOfInterest.xml index 182fc9b0..535e96dc 100644 --- a/templates/mp1/Script/PointOfInterest.xml +++ b/templates/mp1/Script/PointOfInterest.xml @@ -6,11 +6,7 @@ - - - - - + diff --git a/templates/mp1/Structs/ActorParameters.xml b/templates/mp1/Structs/ActorParameters.xml index 9ee1a9d1..9b39f2cd 100644 --- a/templates/mp1/Structs/ActorParameters.xml +++ b/templates/mp1/Structs/ActorParameters.xml @@ -2,11 +2,7 @@ - - - - - + diff --git a/templates/mp1/Structs/PatternedInfo.xml b/templates/mp1/Structs/PatternedInfo.xml index c55c257b..aac4f3a2 100644 --- a/templates/mp1/Structs/PatternedInfo.xml +++ b/templates/mp1/Structs/PatternedInfo.xml @@ -28,7 +28,7 @@ - + diff --git a/templates/mp1/Structs/ScannableParameters.xml b/templates/mp1/Structs/ScannableParameters.xml new file mode 100644 index 00000000..1e93c861 --- /dev/null +++ b/templates/mp1/Structs/ScannableParameters.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/templates/mp2/Script/PointOfInterest.xml b/templates/mp2/Script/PointOfInterest.xml index 810d89ee..d76eb54a 100644 --- a/templates/mp2/Script/PointOfInterest.xml +++ b/templates/mp2/Script/PointOfInterest.xml @@ -9,6 +9,7 @@ false + If enabled, Samus will turn to look at the POI while scanning it. diff --git a/templates/mp2/Script/Timer.xml b/templates/mp2/Script/Timer.xml index dc0347e9..ebf48d29 100644 --- a/templates/mp2/Script/Timer.xml +++ b/templates/mp2/Script/Timer.xml @@ -8,12 +8,15 @@ 0.0 + A random value between this number and 0 will be added to the timer's start time. false + If enabled, when the timer reaches 0, it will reset back to its start time and begin ticking again. false + If enabled, the timer will start ticking automatically on load. Otherwise, it will need to be started with a script message. diff --git a/templates/mp2demo/Script/Timer.xml b/templates/mp2demo/Script/Timer.xml index dc0347e9..ebf48d29 100644 --- a/templates/mp2demo/Script/Timer.xml +++ b/templates/mp2demo/Script/Timer.xml @@ -8,12 +8,15 @@ 0.0 + A random value between this number and 0 will be added to the timer's start time. false + If enabled, when the timer reaches 0, it will reset back to its start time and begin ticking again. false + If enabled, the timer will start ticking automatically on load. Otherwise, it will need to be started with a script message. diff --git a/templates/mp3/Script/Timer.xml b/templates/mp3/Script/Timer.xml index 1d3057f1..043c194c 100644 --- a/templates/mp3/Script/Timer.xml +++ b/templates/mp3/Script/Timer.xml @@ -8,9 +8,11 @@ 0.0 + A random value between this number and 0 will be added to the timer's start time. false + If enabled, the timer will start ticking automatically on load. Otherwise, it will need to be started with a script message. false diff --git a/templates/mp3proto/Script/Timer.xml b/templates/mp3proto/Script/Timer.xml index 1d3057f1..043c194c 100644 --- a/templates/mp3proto/Script/Timer.xml +++ b/templates/mp3proto/Script/Timer.xml @@ -8,9 +8,11 @@ 0.0 + A random value between this number and 0 will be added to the timer's start time. false + If enabled, the timer will start ticking automatically on load. Otherwise, it will need to be started with a script message. false