mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-15 16:16:14 +00:00
Added support for renaming property archetypes. Added support for enums to override the default type name. Added the ability for enums/choices/flags with no values/flags to be edited as ints.
This commit is contained in:
@@ -219,6 +219,73 @@ TString CGameTemplate::GetPropertyArchetypeFilePath(const TString& kTypeName)
|
||||
return GetGameDirectory() + Iter->second.Path;
|
||||
}
|
||||
|
||||
bool CGameTemplate::RenamePropertyArchetype(const TString& kTypeName, const TString& kNewTypeName)
|
||||
{
|
||||
if( kTypeName != kNewTypeName )
|
||||
{
|
||||
// Fetch the property that we are going to be renaming.
|
||||
// Validate type, too, because we only support renaming struct archetypes at the moment
|
||||
auto Iter = mPropertyTemplates.find(kTypeName);
|
||||
|
||||
if( Iter != mPropertyTemplates.end() )
|
||||
{
|
||||
SPropertyTemplatePath& Path = Iter->second;
|
||||
IProperty* pArchetype = Path.pTemplate.get();
|
||||
|
||||
if( pArchetype )
|
||||
{
|
||||
// Attempt to move the XML to the new location.
|
||||
TString OldPath = GetGameDirectory() + Path.Path;
|
||||
TString NewPath = OldPath.GetFileDirectory() + kNewTypeName + ".xml";
|
||||
|
||||
if( FileUtil::MoveFile(OldPath, NewPath) )
|
||||
{
|
||||
// Update the name in the game template's internal mapping
|
||||
TString RelativePath = FileUtil::MakeRelative( NewPath, GetGameDirectory() );
|
||||
auto MapNode = mPropertyTemplates.extract(Iter);
|
||||
MapNode.key() = kNewTypeName;
|
||||
MapNode.mapped().Path = RelativePath;
|
||||
mPropertyTemplates.insert( std::move(MapNode) );
|
||||
mDirty = true;
|
||||
|
||||
// Renaming the archetype will handle updating the actual type name, and
|
||||
// dirtying/invalidating property sub-instances.
|
||||
TString OldTypeName = pArchetype->HashableTypeName();
|
||||
pArchetype->SetName(kNewTypeName);
|
||||
|
||||
// For MP2 and up, we also need to update the type names stored in the property map.
|
||||
if (pArchetype->Game() >= EGame::EchoesDemo)
|
||||
{
|
||||
NPropertyMap::ChangeTypeName(pArchetype, *OldTypeName, *kNewTypeName);
|
||||
}
|
||||
|
||||
// MP1 has a lot of unnamed properties that just use the type name as their name.
|
||||
// Update these properties so their name now refers to the updated type name.
|
||||
else
|
||||
{
|
||||
std::list<IProperty*> SubInstances;
|
||||
pArchetype->GatherAllSubInstances(SubInstances, true);
|
||||
|
||||
for (auto Iter = SubInstances.begin(); Iter != SubInstances.end(); Iter++)
|
||||
{
|
||||
IProperty* pProperty = *Iter;
|
||||
|
||||
if (pProperty->Name() == kTypeName)
|
||||
{
|
||||
pProperty->SetName(kNewTypeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TString CGameTemplate::GetGameDirectory() const
|
||||
{
|
||||
return mSourceFile.GetFileDirectory();
|
||||
|
||||
@@ -132,6 +132,7 @@ public:
|
||||
SMessage MessageByIndex(u32 Index);
|
||||
IProperty* FindPropertyArchetype(const TString& kTypeName);
|
||||
TString GetPropertyArchetypeFilePath(const TString& kTypeName);
|
||||
bool RenamePropertyArchetype(const TString& kTypeName, const TString& kNewTypeName);
|
||||
TString GetGameDirectory() const;
|
||||
|
||||
// Inline Accessors
|
||||
|
||||
@@ -170,7 +170,15 @@ inline void ConditionalLoadMap()
|
||||
/** Saves property names back out to the template file */
|
||||
void SaveMap(bool Force /*= false*/)
|
||||
{
|
||||
ASSERT( gMapIsLoaded );
|
||||
if( !gMapIsLoaded )
|
||||
{
|
||||
if (Force)
|
||||
{
|
||||
LoadMap();
|
||||
}
|
||||
else return;
|
||||
}
|
||||
|
||||
Log::Write("Saving property map");
|
||||
|
||||
if( gMapIsDirty || Force )
|
||||
@@ -306,31 +314,94 @@ void SetPropertyName(u32 ID, const char* pkTypeName, const char* pkNewName)
|
||||
}
|
||||
}
|
||||
|
||||
/** Change the type name associated with a property ID */
|
||||
void SetTypeName(u32 ID, const char* pkOldTypeName, const char* pkNewTypeName)
|
||||
/** Change a type name of a property. */
|
||||
void ChangeTypeName(IProperty* pProperty, const char* pkOldTypeName, const char* pkNewTypeName)
|
||||
{
|
||||
u32 OldTypeHash = CCRC32::StaticHashString(pkOldTypeName);
|
||||
u32 NewTypeHash = CCRC32::StaticHashString(pkNewTypeName);
|
||||
|
||||
SNameKey OldKey( OldTypeHash, ID );
|
||||
auto MapNode = gNameMap.extract(OldKey);
|
||||
|
||||
if (!MapNode.empty())
|
||||
if (OldTypeHash == NewTypeHash)
|
||||
{
|
||||
SNameKey& Key = MapNode.key();
|
||||
SNameValue& Value = MapNode.mapped();
|
||||
Key.TypeHash = NewTypeHash;
|
||||
gHashToTypeName[NewTypeHash] = pkNewTypeName;
|
||||
|
||||
for (auto Iter = Value.PropertyList.begin(); Iter != Value.PropertyList.end(); Iter++)
|
||||
{
|
||||
IProperty* pProperty = *Iter;
|
||||
pProperty->RecacheName();
|
||||
}
|
||||
|
||||
gNameMap.insert( std::move(MapNode) );
|
||||
gMapIsDirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Start off with a ist of all properties in the same inheritance chain as this one.
|
||||
std::list<IProperty*> Properties;
|
||||
IProperty* pArchetype = pProperty->RootArchetype();
|
||||
pArchetype->GatherAllSubInstances(Properties, true);
|
||||
|
||||
for (auto Iter = Properties.begin(); Iter != Properties.end(); Iter++)
|
||||
{
|
||||
pProperty = *Iter;
|
||||
|
||||
if (pProperty->UsesNameMap())
|
||||
{
|
||||
SNameKey OldKey(OldTypeHash, pProperty->ID());
|
||||
SNameKey NewKey(NewTypeHash, pProperty->ID());
|
||||
|
||||
// Disassociate this property from the old mapping.
|
||||
auto Find = gNameMap.find(OldKey);
|
||||
|
||||
if (Find != gNameMap.end())
|
||||
{
|
||||
SNameValue& Value = Find->second;
|
||||
NBasics::ListRemoveOne(Value.PropertyList, pProperty);
|
||||
}
|
||||
|
||||
// Create a key for the new property and add it to the list.
|
||||
Find = gNameMap.find(NewKey);
|
||||
|
||||
if (Find == gNameMap.end())
|
||||
{
|
||||
SNameValue Value;
|
||||
Value.Name = pProperty->Name();
|
||||
gNameMap[NewKey] = Value;
|
||||
Find = gNameMap.find(NewKey);
|
||||
}
|
||||
ASSERT(Find != gNameMap.end());
|
||||
Find->second.PropertyList.push_back(pProperty);
|
||||
|
||||
gMapIsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
gHashToTypeName[NewTypeHash] = pkNewTypeName;
|
||||
}
|
||||
|
||||
/** Change a type name. */
|
||||
void ChangeTypeNameGlobally(const char* pkOldTypeName, const char* pkNewTypeName)
|
||||
{
|
||||
u32 OldTypeHash = CCRC32::StaticHashString(pkOldTypeName);
|
||||
u32 NewTypeHash = CCRC32::StaticHashString(pkNewTypeName);
|
||||
|
||||
if (OldTypeHash == NewTypeHash)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The process here is basically to find all properties with a matching typename
|
||||
// hash and update the hashes to the new type. Not 100% sure if this is the best
|
||||
// way to go about doing it. From what I understand, insert() does not invalidate
|
||||
// iterators, and extract() only invalidates the iterator being extracted. So this
|
||||
// implementation should work correctly.
|
||||
for (auto MapIter = gNameMap.begin(); MapIter != gNameMap.end(); MapIter++)
|
||||
{
|
||||
if (MapIter->first.TypeHash == OldTypeHash)
|
||||
{
|
||||
auto PrevIter = MapIter;
|
||||
PrevIter--;
|
||||
|
||||
auto MapNode = gNameMap.extract(MapIter);
|
||||
MapIter = PrevIter;
|
||||
|
||||
SNameKey& Key = MapNode.key();
|
||||
Key.TypeHash = NewTypeHash;
|
||||
gNameMap.insert( std::move(MapNode) );
|
||||
gMapIsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
gHashToTypeName[NewTypeHash] = pkNewTypeName;
|
||||
}
|
||||
|
||||
/** Registers a property in the name map. Should be called on all properties that use the map */
|
||||
@@ -363,16 +434,17 @@ void RegisterProperty(IProperty* pProperty)
|
||||
|
||||
gNameMap[Key] = Value;
|
||||
MapFind = gNameMap.find(Key);
|
||||
ASSERT(MapFind != gNameMap.end());
|
||||
|
||||
RegisterTypeName(Key.TypeHash, pProperty->HashableTypeName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(MapFind != gNameMap.end());
|
||||
pProperty->SetName( MapFind->second.Name );
|
||||
}
|
||||
|
||||
ASSERT(MapFind != gNameMap.end());
|
||||
MapFind->second.PropertyList.push_back(pProperty);
|
||||
|
||||
// Update the property's Name field to match the mapped name.
|
||||
|
||||
@@ -34,8 +34,11 @@ void RetrieveXMLsWithProperty(u32 ID, const char* pkTypeName, std::set<TString>&
|
||||
/** Updates the name of a given property in the map */
|
||||
void SetPropertyName(u32 ID, const char* pkTypeName, const char* pkNewName);
|
||||
|
||||
/** Change the type name associated with a property ID */
|
||||
void SetTypeName(u32 ID, const char* pkOldTypeName, const char* pkNewTypeName);
|
||||
/** Change a type name of a property. */
|
||||
void ChangeTypeName(IProperty* pProperty, const char* pkOldTypeName, const char* pkNewTypeName);
|
||||
|
||||
/** Change a type name. */
|
||||
void ChangeTypeNameGlobally(const char* pkOldTypeName, const char* pkNewTypeName);
|
||||
|
||||
/** Registers a property in the name map. Should be called on all properties that use the map */
|
||||
void RegisterProperty(IProperty* pProperty);
|
||||
|
||||
@@ -42,16 +42,24 @@ class TEnumPropertyBase : public TSerializeableTypedProperty<s32, TypeEnum>
|
||||
};
|
||||
std::vector<SEnumValue> mValues;
|
||||
|
||||
/** If true, the archetype's name will be used as the type name instead of "enum" or "choice". */
|
||||
bool mOverrideTypeName;
|
||||
|
||||
protected:
|
||||
/** Constructor */
|
||||
TEnumPropertyBase(EGame Game)
|
||||
: TSerializeableTypedProperty(Game)
|
||||
, mOverrideTypeName(false)
|
||||
{}
|
||||
|
||||
public:
|
||||
virtual const char* GetHashableTypeName() const
|
||||
virtual const char* HashableTypeName() const
|
||||
{
|
||||
if (TypeEnum == EPropertyType::Enum)
|
||||
if (mpArchetype)
|
||||
return mpArchetype->HashableTypeName();
|
||||
else if (mOverrideTypeName)
|
||||
return *mName;
|
||||
else if (TypeEnum == EPropertyType::Enum)
|
||||
return "enum";
|
||||
else
|
||||
return "choice";
|
||||
@@ -64,8 +72,15 @@ public:
|
||||
|
||||
TEnumPropertyBase* pArchetype = static_cast<TEnumPropertyBase*>(mpArchetype);
|
||||
u32 DefaultValueFlags = SH_HexDisplay | (pArchetype || Game() <= EGame::Prime ? SH_Optional : 0);
|
||||
|
||||
rArc << SerialParameter("DefaultValue", mDefaultValue, DefaultValueFlags, pArchetype ? pArchetype->mDefaultValue : 0);
|
||||
|
||||
// Only serialize type name override for root archetypes.
|
||||
if (!mpArchetype)
|
||||
{
|
||||
rArc << SerialParameter("OverrideTypeName", mOverrideTypeName, SH_Optional, false);
|
||||
}
|
||||
|
||||
if (!pArchetype || !rArc.CanSkipParameters() || mValues != pArchetype->mValues)
|
||||
{
|
||||
rArc << SerialParameter("Values", mValues);
|
||||
@@ -117,10 +132,33 @@ public:
|
||||
|
||||
bool HasValidValue(void* pPropertyData)
|
||||
{
|
||||
if (mValues.empty()) return true;
|
||||
int ID = ValueRef(pPropertyData);
|
||||
u32 Index = ValueIndex(ID);
|
||||
return Index >= 0 && Index < mValues.size();
|
||||
}
|
||||
|
||||
bool OverridesTypeName() const
|
||||
{
|
||||
return mpArchetype ? TPropCast<TEnumPropertyBase>(mpArchetype)->OverridesTypeName() : mOverrideTypeName;
|
||||
}
|
||||
|
||||
void SetOverrideTypeName(bool Override)
|
||||
{
|
||||
if (mpArchetype)
|
||||
{
|
||||
TEnumPropertyBase* pArchetype = TPropCast<TEnumPropertyBase>(RootArchetype());
|
||||
pArchetype->SetOverrideTypeName(Override);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mOverrideTypeName != Override)
|
||||
{
|
||||
mOverrideTypeName = Override;
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef TEnumPropertyBase<EPropertyType::Choice> CChoiceProperty;
|
||||
|
||||
@@ -43,5 +43,6 @@ void CFlagsProperty::InitFromArchetype(IProperty* pOther)
|
||||
*/
|
||||
u32 CFlagsProperty::HasValidValue(void* pPropertyData)
|
||||
{
|
||||
if (!mAllFlags) return 0;
|
||||
return ValueRef(pPropertyData) & ~mAllFlags;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,6 @@ void IProperty::Initialize(IProperty* pInParent, CScriptTemplate* pInTemplate, u
|
||||
{
|
||||
// Make sure we only get initialized once.
|
||||
ASSERT( (mFlags & EPropertyFlag::IsInitialized) == 0 );
|
||||
mFlags |= EPropertyFlag::IsInitialized;
|
||||
|
||||
mpParent = pInParent;
|
||||
mOffset = InOffset;
|
||||
@@ -219,6 +218,8 @@ void IProperty::Initialize(IProperty* pInParent, CScriptTemplate* pInTemplate, u
|
||||
pChild->Initialize(this, pInTemplate, ChildOffset);
|
||||
}
|
||||
}
|
||||
|
||||
mFlags |= EPropertyFlag::IsInitialized;
|
||||
}
|
||||
|
||||
void* IProperty::RawValuePtr(void* pData) const
|
||||
@@ -273,6 +274,21 @@ IProperty* IProperty::ChildByIDString(const TIDString& rkIdString)
|
||||
}
|
||||
}
|
||||
|
||||
void IProperty::GatherAllSubInstances(std::list<IProperty*>& OutList, bool Recursive)
|
||||
{
|
||||
OutList.push_back(this);
|
||||
|
||||
for( u32 SubIdx = 0; SubIdx < mSubInstances.size(); SubIdx++ )
|
||||
{
|
||||
IProperty* pSubInstance = mSubInstances[SubIdx];
|
||||
|
||||
if( Recursive )
|
||||
pSubInstance->GatherAllSubInstances( OutList, true );
|
||||
else
|
||||
OutList.push_back( pSubInstance );
|
||||
}
|
||||
}
|
||||
|
||||
TString IProperty::GetTemplateFileName()
|
||||
{
|
||||
// We want to return the path to the XML file that this property originally belongs to.
|
||||
@@ -328,13 +344,7 @@ void IProperty::SetName(const TString& rkNewName)
|
||||
{
|
||||
mName = rkNewName;
|
||||
mFlags.ClearFlag(EPropertyFlag::HasCachedNameCheck);
|
||||
|
||||
// in Echoes and on, since property names are referenced by ID, renaming a property
|
||||
// doesn't directly affect the serialized data, so it doesn't need to be flagged dirty
|
||||
if (mGame <= EGame::Prime)
|
||||
{
|
||||
MarkDirty();
|
||||
}
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +368,21 @@ void IProperty::SetSuffix(const TString& rkNewSuffix)
|
||||
|
||||
void IProperty::MarkDirty()
|
||||
{
|
||||
RootParent()->mFlags |= EPropertyFlag::IsDirty;
|
||||
// Don't allow properties to be marked dirty before they are fully initialized.
|
||||
if (IsInitialized())
|
||||
{
|
||||
// Mark the root parent as dirty so the template file will get resaved
|
||||
RootParent()->mFlags |= EPropertyFlag::IsDirty;
|
||||
|
||||
// Clear property name cache in case something has been modified that affects the hash
|
||||
mFlags &= ~(EPropertyFlag::HasCachedNameCheck | EPropertyFlag::HasCorrectPropertyName);
|
||||
|
||||
// Mark sub-instances as dirty since they may need to resave as well
|
||||
for (u32 SubIdx = 0; SubIdx < mSubInstances.size(); SubIdx++)
|
||||
{
|
||||
mSubInstances[SubIdx]->MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IProperty::ClearDirtyFlag()
|
||||
@@ -416,11 +440,6 @@ bool IProperty::HasAccurateName()
|
||||
return mFlags.HasFlag( EPropertyFlag::HasCorrectPropertyName );
|
||||
}
|
||||
|
||||
void IProperty::RecacheName()
|
||||
{
|
||||
mFlags.ClearFlag( EPropertyFlag::HasCachedNameCheck | EPropertyFlag::HasCorrectPropertyName );
|
||||
}
|
||||
|
||||
/** IPropertyNew Accessors */
|
||||
EGame IProperty::Game() const
|
||||
{
|
||||
|
||||
@@ -193,6 +193,7 @@ public:
|
||||
void* RawValuePtr(void* pData) const;
|
||||
IProperty* ChildByID(u32 ID) const;
|
||||
IProperty* ChildByIDString(const TIDString& rkIdString);
|
||||
void GatherAllSubInstances(std::list<IProperty*>& OutList, bool Recursive);
|
||||
TString GetTemplateFileName();
|
||||
bool ShouldCook(void* pPropertyData) const;
|
||||
void SetName(const TString& rkNewName);
|
||||
@@ -202,7 +203,6 @@ public:
|
||||
void ClearDirtyFlag();
|
||||
bool UsesNameMap();
|
||||
bool HasAccurateName();
|
||||
void RecacheName();
|
||||
|
||||
/** Accessors */
|
||||
EGame Game() const;
|
||||
@@ -221,6 +221,7 @@ public:
|
||||
inline u32 Offset() const;
|
||||
inline u32 ID() const;
|
||||
|
||||
inline bool IsInitialized() const { return mFlags.HasFlag(EPropertyFlag::IsInitialized); }
|
||||
inline bool IsArchetype() const { return mFlags.HasFlag(EPropertyFlag::IsArchetype); }
|
||||
inline bool IsArrayArchetype() const { return mFlags.HasFlag(EPropertyFlag::IsArrayArchetype); }
|
||||
inline bool IsAtomic() const { return mFlags.HasFlag(EPropertyFlag::IsAtomic); }
|
||||
|
||||
Reference in New Issue
Block a user