mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-15 16:16:14 +00:00
New templates
This commit is contained in:
@@ -170,7 +170,7 @@ public:
|
||||
|
||||
inline void SetVisible(bool Visible) { mVisible = Visible; }
|
||||
inline void MarkDirty() { mDirty = true; }
|
||||
inline bool IsDirty() const { return mDirty; }
|
||||
inline bool IsDirty() const { return mDirty || mpProperties->IsDirty(); }
|
||||
|
||||
// Object Tracking
|
||||
u32 NumObjects() const;
|
||||
|
||||
171
src/Core/Resource/Script/NGameList.cpp
Normal file
171
src/Core/Resource/Script/NGameList.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "NGameList.h"
|
||||
|
||||
namespace NGameList
|
||||
{
|
||||
|
||||
/** Path for the templates directory */
|
||||
const TString gkTemplatesDir = "../templates/";
|
||||
|
||||
/** Path to the game list file */
|
||||
const TString gkGameListPath = gkTemplatesDir + "GameList.xml";
|
||||
|
||||
/** Info about a particular game serialized to the list */
|
||||
struct SGameInfo
|
||||
{
|
||||
TString Name;
|
||||
TString TemplatePath;
|
||||
std::unique_ptr<CGameTemplate> pTemplate;
|
||||
bool IsValid;
|
||||
|
||||
SGameInfo()
|
||||
: IsValid(false)
|
||||
{}
|
||||
|
||||
void Serialize(IArchive& Arc)
|
||||
{
|
||||
Arc << SerialParameter("Name", Name)
|
||||
<< SerialParameter("GameTemplate", TemplatePath);
|
||||
|
||||
if (Arc.IsReader())
|
||||
{
|
||||
IsValid = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
SGameInfo gGameList[EGame::Max];
|
||||
|
||||
/** Whether the game list has been loaded */
|
||||
bool gLoadedGameList = false;
|
||||
|
||||
/** Returns whether a game template has been loaded or not */
|
||||
bool IsGameTemplateLoaded(EGame Game)
|
||||
{
|
||||
int GameIdx = (int) Game;
|
||||
const SGameInfo& GameInfo = gGameList[GameIdx];
|
||||
return GameInfo.pTemplate != nullptr;
|
||||
}
|
||||
|
||||
/** Serialize the game list to/from a file */
|
||||
inline void SerializeGameList(IArchive& Arc)
|
||||
{
|
||||
// Serialize the number of games with valid GameInfos.
|
||||
u32 NumGames = 0;
|
||||
|
||||
if (Arc.IsWriter())
|
||||
{
|
||||
for (u32 GameIdx = 0; GameIdx < (u32) EGame::Max; GameIdx++)
|
||||
{
|
||||
if ( gGameList[GameIdx].IsValid )
|
||||
NumGames++;
|
||||
}
|
||||
}
|
||||
|
||||
Arc.SerializeArraySize(NumGames);
|
||||
|
||||
// Serialize the actual game info
|
||||
for (u32 GameIdx = 0; GameIdx < (u32) EGame::Max; GameIdx++)
|
||||
{
|
||||
// Skip games that don't have game templates when writing.
|
||||
if (Arc.IsWriter() && !gGameList[GameIdx].IsValid)
|
||||
continue;
|
||||
|
||||
ENSURE( Arc.ParamBegin("Game", 0) );
|
||||
|
||||
// Determine which game is being serialized
|
||||
EGame Game = (EGame) GameIdx;
|
||||
Arc << SerialParameter("ID", Game, SH_Attribute);
|
||||
ASSERT( Game != EGame::Invalid );
|
||||
|
||||
gGameList[ (u32) Game ].Serialize(Arc);
|
||||
Arc.ParamEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/** Load the game list into memory */
|
||||
void LoadGameList()
|
||||
{
|
||||
ASSERT(!gLoadedGameList);
|
||||
|
||||
CXMLReader Reader(gkGameListPath);
|
||||
ASSERT(Reader.IsValid());
|
||||
|
||||
SerializeGameList(Reader);
|
||||
gLoadedGameList = true;
|
||||
}
|
||||
|
||||
/** Save the game list back out to a file */
|
||||
void SaveGameList()
|
||||
{
|
||||
ASSERT(gLoadedGameList);
|
||||
|
||||
CXMLWriter Writer(gkGameListPath, "GameList");
|
||||
ASSERT(Writer.IsValid());
|
||||
|
||||
SerializeGameList(Writer);
|
||||
}
|
||||
|
||||
/** Load all game templates into memory */
|
||||
void LoadAllGameTemplates()
|
||||
{
|
||||
for (int GameIdx = 0; GameIdx < (int) EGame::Max; GameIdx++)
|
||||
GetGameTemplate( (EGame) GameIdx );
|
||||
}
|
||||
|
||||
/** Resave templates. If ForceAll is false, only saves templates that have been modified. */
|
||||
void SaveTemplates(bool ForceAll /*= false*/)
|
||||
{
|
||||
for (int GameIdx = 0; GameIdx < (int) EGame::Max; GameIdx++)
|
||||
{
|
||||
EGame Game = (EGame) GameIdx;
|
||||
if ( IsGameTemplateLoaded(Game) )
|
||||
{
|
||||
CGameTemplate* pGameTemplate = GetGameTemplate(Game);
|
||||
pGameTemplate->SaveGameTemplates(ForceAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the game template for a given game */
|
||||
CGameTemplate* GetGameTemplate(EGame Game)
|
||||
{
|
||||
// Game must be valid!
|
||||
if (Game == EGame::Invalid)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ASSERT(Game >= (EGame) 0 && Game < EGame::Max);
|
||||
|
||||
// Initialize the game list, if it hasn't been loaded yet.
|
||||
if (!gLoadedGameList)
|
||||
{
|
||||
LoadGameList();
|
||||
}
|
||||
|
||||
int GameIdx = (int) Game;
|
||||
SGameInfo& GameInfo = gGameList[GameIdx];
|
||||
|
||||
// Load the game template, if it hasn't been loaded yet.
|
||||
if (!GameInfo.pTemplate && !GameInfo.Name.IsEmpty())
|
||||
{
|
||||
TString GamePath = gkTemplatesDir + GameInfo.TemplatePath;
|
||||
GameInfo.pTemplate = std::make_unique<CGameTemplate>();
|
||||
GameInfo.pTemplate->Load(GamePath);
|
||||
}
|
||||
|
||||
return GameInfo.pTemplate.get();
|
||||
}
|
||||
|
||||
/** Clean up game list resources. This needs to be called on app shutdown to ensure things are cleaned up in the right order. */
|
||||
void Shutdown()
|
||||
{
|
||||
for (int GameIdx = 0; GameIdx < (int) EGame::Max; GameIdx++)
|
||||
{
|
||||
gGameList[GameIdx].Name = "";
|
||||
gGameList[GameIdx].TemplatePath = "";
|
||||
gGameList[GameIdx].pTemplate = nullptr;
|
||||
}
|
||||
gLoadedGameList = false;
|
||||
}
|
||||
|
||||
}
|
||||
32
src/Core/Resource/Script/NGameList.h
Normal file
32
src/Core/Resource/Script/NGameList.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef NGAMELIST_H
|
||||
#define NGAMELIST_H
|
||||
|
||||
#include "CGameTemplate.h"
|
||||
|
||||
namespace NGameList
|
||||
{
|
||||
|
||||
/** Load all game templates into memory
|
||||
* This normally isn't necessary to call, as game templates will be lazy-loaded the
|
||||
* first time they are requested.
|
||||
*/
|
||||
void LoadAllGameTemplates();
|
||||
|
||||
/** Load the game list into memory. This is normally not necessary to call. */
|
||||
void LoadGameList();
|
||||
|
||||
/** Save the game list back out to a file */
|
||||
void SaveGameList();
|
||||
|
||||
/** Resave templates. If ForceAll is false, only saves templates that have been modified. */
|
||||
void SaveTemplates(bool ForceAll = false);
|
||||
|
||||
/** Get the game template for a given game */
|
||||
CGameTemplate* GetGameTemplate(EGame Game);
|
||||
|
||||
/** Clean up game list resources. This needs to be called on app shutdown to ensure things are cleaned up in the right order. */
|
||||
void Shutdown();
|
||||
|
||||
}
|
||||
|
||||
#endif // NGAMELIST_H
|
||||
317
src/Core/Resource/Script/NPropertyMap.cpp
Normal file
317
src/Core/Resource/Script/NPropertyMap.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
#include "NPropertyMap.h"
|
||||
#include <Common/NBasics.h>
|
||||
#include <Common/Serialization/XML.h>
|
||||
|
||||
/** NPropertyMap: Namespace for property ID -> name mappings */
|
||||
namespace NPropertyMap
|
||||
{
|
||||
|
||||
/** Path to the property map file */
|
||||
const char* gpkLegacyMapPath = "../templates/PropertyMapLegacy.xml";
|
||||
const char* gpkMapPath = "../templates/PropertyMap.xml";
|
||||
|
||||
/** Whether to do name lookups from the legacy map */
|
||||
const bool gkUseLegacyMapForNameLookups = false;
|
||||
|
||||
/** Whether to update names in the legacy map */
|
||||
const bool gkUseLegacyMapForUpdates = false;
|
||||
|
||||
/** Whether the map is dirty (has unsaved changes */
|
||||
bool gMapIsDirty = false;
|
||||
|
||||
/** Whether the map has been loaded */
|
||||
bool gMapIsLoaded = false;
|
||||
|
||||
/** Mapping of typename hashes back to the original string */
|
||||
std::unordered_map<u32, TString> gHashToTypeName;
|
||||
|
||||
/** Register a hash -> name mapping */
|
||||
inline void RegisterTypeName(u32 TypeHash, TString TypeName)
|
||||
{
|
||||
ASSERT( !TypeName.IsEmpty() );
|
||||
ASSERT( TypeName != "Unknown" );
|
||||
gHashToTypeName.emplace( std::make_pair<u32, TString>(std::move(TypeHash), std::move(TypeName)) );
|
||||
}
|
||||
|
||||
/** Key structure for name map lookups */
|
||||
struct SNameKey
|
||||
{
|
||||
union
|
||||
{
|
||||
struct {
|
||||
u32 TypeHash;
|
||||
u32 ID;
|
||||
};
|
||||
struct {
|
||||
u64 Key;
|
||||
};
|
||||
};
|
||||
|
||||
SNameKey()
|
||||
: TypeHash(-1), ID(-1)
|
||||
{}
|
||||
|
||||
SNameKey(u32 InTypeHash, u32 InID)
|
||||
: TypeHash(InTypeHash), ID(InID)
|
||||
{}
|
||||
|
||||
void Serialize(IArchive& Arc)
|
||||
{
|
||||
TString TypeName;
|
||||
|
||||
if (Arc.IsWriter())
|
||||
{
|
||||
TypeName = gHashToTypeName[TypeHash];
|
||||
ASSERT(!TypeName.IsEmpty());
|
||||
}
|
||||
|
||||
Arc << SerialParameter("ID", ID, SH_Attribute | SH_HexDisplay)
|
||||
<< SerialParameter("Type", TypeName, SH_Attribute);
|
||||
|
||||
if (Arc.IsReader())
|
||||
{
|
||||
TypeHash = TypeName.Hash32();
|
||||
RegisterTypeName(TypeHash, TypeName);
|
||||
}
|
||||
}
|
||||
|
||||
friend bool operator==(const SNameKey& kLHS, const SNameKey& kRHS)
|
||||
{
|
||||
return kLHS.Key == kRHS.Key;
|
||||
}
|
||||
|
||||
friend bool operator<(const SNameKey& kLHS, const SNameKey& kRHS)
|
||||
{
|
||||
return kLHS.Key < kRHS.Key;
|
||||
}
|
||||
};
|
||||
|
||||
/** Hasher for name keys for use in std::unordered_map */
|
||||
struct KeyHash
|
||||
{
|
||||
inline size_t operator()(const SNameKey& kKey) const
|
||||
{
|
||||
return std::hash<u64>()(kKey.Key);
|
||||
}
|
||||
};
|
||||
|
||||
/** Value structure for name map lookups */
|
||||
struct SNameValue
|
||||
{
|
||||
/** Name of the property */
|
||||
TString Name;
|
||||
|
||||
/** List of all properties using this ID */
|
||||
std::list<IProperty*> PropertyList;
|
||||
|
||||
void Serialize(IArchive& Arc)
|
||||
{
|
||||
Arc << SerialParameter("Name", Name, SH_Attribute);
|
||||
}
|
||||
|
||||
friend bool operator==(const SNameValue& kLHS, const SNameValue& kRHS)
|
||||
{
|
||||
return kLHS.Name == kRHS.Name;
|
||||
}
|
||||
};
|
||||
|
||||
/** Mapping of property IDs to names. In the key, the upper 32 bits
|
||||
* are the type, and the lower 32 bits are the ID.
|
||||
*/
|
||||
std::map<SNameKey, SNameValue> gNameMap;
|
||||
|
||||
/** Legacy map that only includes the ID in the key */
|
||||
std::map<u32, TString> gLegacyNameMap;
|
||||
|
||||
/** Internal: Creates a name key for the given property. */
|
||||
SNameKey CreateKey(IProperty* pProperty)
|
||||
{
|
||||
SNameKey Key;
|
||||
Key.ID = pProperty->ID();
|
||||
Key.TypeHash = CCRC32::StaticHashString( pProperty->HashableTypeName() );
|
||||
return Key;
|
||||
}
|
||||
|
||||
/** Loads property names into memory */
|
||||
void LoadMap()
|
||||
{
|
||||
ASSERT( !gMapIsLoaded );
|
||||
|
||||
if ( gkUseLegacyMapForNameLookups )
|
||||
{
|
||||
CXMLReader Reader(gpkLegacyMapPath);
|
||||
ASSERT(Reader.IsValid());
|
||||
Reader << SerialParameter("PropertyMap", gLegacyNameMap, SH_HexDisplay);
|
||||
}
|
||||
else
|
||||
{
|
||||
CXMLReader Reader(gpkMapPath);
|
||||
ASSERT(Reader.IsValid());
|
||||
Reader << SerialParameter("PropertyMap", gNameMap, SH_HexDisplay);
|
||||
}
|
||||
|
||||
gMapIsLoaded = true;
|
||||
}
|
||||
|
||||
inline void ConditionalLoadMap()
|
||||
{
|
||||
if( !gMapIsLoaded )
|
||||
{
|
||||
LoadMap();
|
||||
}
|
||||
}
|
||||
|
||||
/** Saves property names back out to the template file */
|
||||
void SaveMap(bool Force /*= false*/)
|
||||
{
|
||||
ASSERT( gMapIsLoaded );
|
||||
|
||||
if( gMapIsDirty || Force )
|
||||
{
|
||||
if( gkUseLegacyMapForUpdates )
|
||||
{
|
||||
CXMLWriter Writer(gpkLegacyMapPath, "PropertyMap");
|
||||
ASSERT(Writer.IsValid());
|
||||
Writer << SerialParameter("PropertyMap", gLegacyNameMap, SH_HexDisplay);
|
||||
}
|
||||
else
|
||||
{
|
||||
CXMLWriter Writer(gpkMapPath, "PropertyMap");
|
||||
ASSERT(Writer.IsValid());
|
||||
Writer << SerialParameter("PropertyMap", gNameMap, SH_HexDisplay);
|
||||
}
|
||||
gMapIsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Given a property ID and type, returns the name of the property */
|
||||
const char* GetPropertyName(IProperty* pInProperty)
|
||||
{
|
||||
ConditionalLoadMap();
|
||||
|
||||
if (gkUseLegacyMapForNameLookups)
|
||||
{
|
||||
auto MapFind = gLegacyNameMap.find( pInProperty->ID() );
|
||||
return (MapFind == gLegacyNameMap.end() ? "Unknown" : *MapFind->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
SNameKey Key = CreateKey(pInProperty);
|
||||
auto MapFind = gNameMap.find(Key);
|
||||
return (MapFind == gNameMap.end() ? "Unknown" : *MapFind->second.Name);
|
||||
}
|
||||
}
|
||||
|
||||
/** Given a property name and type, returns the name of the property.
|
||||
* This requires you to provide the exact type string used in the hash.
|
||||
*/
|
||||
const char* GetPropertyName(u32 ID, const char* pkTypeName)
|
||||
{
|
||||
// Does not support legacy map
|
||||
ConditionalLoadMap();
|
||||
|
||||
SNameKey Key( CCRC32::StaticHashString(pkTypeName), ID );
|
||||
auto MapFind = gNameMap.find(Key);
|
||||
return MapFind == gNameMap.end() ? "Unknown" : *MapFind->second.Name;
|
||||
}
|
||||
|
||||
/** Updates the name of a given property in the map */
|
||||
void SetPropertyName(IProperty* pProperty, const char* pkNewName)
|
||||
{
|
||||
if( gkUseLegacyMapForUpdates )
|
||||
{
|
||||
gLegacyNameMap[pProperty->ID()] = pkNewName;
|
||||
}
|
||||
else
|
||||
{
|
||||
SNameKey Key = CreateKey(pProperty);
|
||||
auto MapFind = gNameMap.find(Key);
|
||||
|
||||
if (MapFind != gNameMap.end())
|
||||
{
|
||||
SNameValue& Value = MapFind->second;
|
||||
|
||||
if (Value.Name != pkNewName)
|
||||
{
|
||||
TString OldName = Value.Name;
|
||||
Value.Name = pkNewName;
|
||||
|
||||
// Update all properties with this ID with the new name
|
||||
for (auto Iter = Value.PropertyList.begin(); Iter != Value.PropertyList.end(); Iter++)
|
||||
{
|
||||
// If the property overrides the name, then don't change it.
|
||||
IProperty* pIterProperty = *Iter;
|
||||
|
||||
if (pIterProperty->Name() == OldName)
|
||||
{
|
||||
pIterProperty->SetName(Value.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gMapIsDirty = true;
|
||||
}
|
||||
|
||||
/** Registers a property in the name map. Should be called on all properties that use the map */
|
||||
void RegisterProperty(IProperty* pProperty)
|
||||
{
|
||||
ConditionalLoadMap();
|
||||
|
||||
// Sanity checks to make sure we don't accidentally add non-hash property IDs to the map.
|
||||
ASSERT( pProperty->UsesNameMap() );
|
||||
ASSERT( pProperty->ID() > 0xFF && pProperty->ID() != 0xFFFFFFFF );
|
||||
|
||||
// Just need to register the property in the list.
|
||||
SNameKey Key = CreateKey(pProperty);
|
||||
auto MapFind = gNameMap.find(Key);
|
||||
|
||||
if( gkUseLegacyMapForNameLookups )
|
||||
{
|
||||
// If we are using the legacy map, gNameMap may be empty. We need to retrieve the name
|
||||
// from the legacy map, and create an entry in gNameMap with it.
|
||||
|
||||
//@todo this prob isn't the most efficient way to do this
|
||||
if (MapFind == gNameMap.end())
|
||||
{
|
||||
auto LegacyMapFind = gLegacyNameMap.find( pProperty->ID() );
|
||||
ASSERT( LegacyMapFind != gLegacyNameMap.end() );
|
||||
|
||||
SNameValue Value;
|
||||
Value.Name = LegacyMapFind->second;
|
||||
pProperty->SetName(Value.Name);
|
||||
|
||||
gNameMap[Key] = Value;
|
||||
MapFind = gNameMap.find(Key);
|
||||
|
||||
RegisterTypeName(Key.TypeHash, pProperty->HashableTypeName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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.
|
||||
pProperty->SetName( MapFind->second.Name );
|
||||
}
|
||||
|
||||
/** Unregisters a property from the name map. Should be called on all properties that use the map on destruction. */
|
||||
void UnregisterProperty(IProperty* pProperty)
|
||||
{
|
||||
SNameKey Key = CreateKey(pProperty);
|
||||
auto Iter = gNameMap.find(Key);
|
||||
|
||||
if (Iter != gNameMap.end())
|
||||
{
|
||||
// Found the value, now remove the element from the list.
|
||||
SNameValue& Value = Iter->second;
|
||||
NBasics::ListRemoveOne(Value.PropertyList, pProperty);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
36
src/Core/Resource/Script/NPropertyMap.h
Normal file
36
src/Core/Resource/Script/NPropertyMap.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef NPROPERTYMAP_H
|
||||
#define NPROPERTYMAP_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include "Core/Resource/Script/Property/IProperty.h"
|
||||
|
||||
/** NPropertyMap: Namespace for property ID -> name mappings */
|
||||
namespace NPropertyMap
|
||||
{
|
||||
|
||||
/** Loads property names into memory */
|
||||
void LoadMap();
|
||||
|
||||
/** Saves property names back out to the template file */
|
||||
void SaveMap(bool Force = false);
|
||||
|
||||
/** Returns the name of the property */
|
||||
const char* GetPropertyName(IProperty* pProperty);
|
||||
|
||||
/** Given a property name and type, returns the name of the property.
|
||||
* This requires you to provide the exact type string used in the hash.
|
||||
*/
|
||||
const char* GetPropertyName(u32 ID, const char* pkTypeName);
|
||||
|
||||
/** Updates the name of a given property in the map */
|
||||
void SetPropertyName(IProperty* pProperty, const char* pkNewName);
|
||||
|
||||
/** Registers a property in the name map. Should be called on all properties that use the map */
|
||||
void RegisterProperty(IProperty* pProperty);
|
||||
|
||||
/** Unregisters a property from the name map. Should be called on all properties that use the map on destruction. */
|
||||
void UnregisterProperty(IProperty* pProperty);
|
||||
|
||||
}
|
||||
|
||||
#endif // NPROPERTYMAP_H
|
||||
@@ -79,12 +79,16 @@ void CStructProperty::Serialize(IArchive& rArc)
|
||||
IProperty::Serialize(rArc);
|
||||
|
||||
// Serialize atomic flag
|
||||
bool Atomic = IsAtomic();
|
||||
rArc << SerialParameter("Atomic", Atomic, SH_Optional, false);
|
||||
|
||||
if (rArc.IsReader() && Atomic)
|
||||
// Only serialize this if we don't have an archetype. Otherwise we just inherit the archetype's atomic flag.
|
||||
if (!mpArchetype)
|
||||
{
|
||||
mFlags.SetFlag(EPropertyFlag::IsAtomic);
|
||||
bool Atomic = IsAtomic();
|
||||
rArc << SerialParameter("Atomic", Atomic, SH_Optional, false);
|
||||
|
||||
if (rArc.IsReader() && Atomic)
|
||||
{
|
||||
mFlags.SetFlag(EPropertyFlag::IsAtomic);
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize archetype
|
||||
|
||||
@@ -177,6 +177,13 @@ void IProperty::Initialize(IProperty* pInParent, CScriptTemplate* pInTemplate, u
|
||||
{
|
||||
mFlags |= EPropertyFlag::IsArrayArchetype;
|
||||
}
|
||||
|
||||
// MP1 has some weirdness we need to account for, most likely due to incorrect templates
|
||||
// The templates we have right now have non-atomic structs inside atomic structs...
|
||||
if (Game() <= EGame::Prime && mpParent->IsAtomic() && mpArchetype && !mpArchetype->IsAtomic())
|
||||
{
|
||||
mFlags.ClearFlag(EPropertyFlag::IsAtomic);
|
||||
}
|
||||
}
|
||||
else if (!mpScriptTemplate)
|
||||
{
|
||||
@@ -322,16 +329,7 @@ void IProperty::SetSuffix(const TString& rkNewSuffix)
|
||||
|
||||
void IProperty::MarkDirty()
|
||||
{
|
||||
// This property is either part of a script template, or a property archetype.
|
||||
// Figure out which one, set the dirty flag as needed
|
||||
if (mpScriptTemplate)
|
||||
{
|
||||
mpScriptTemplate->MarkDirty();
|
||||
}
|
||||
else
|
||||
{
|
||||
RootParent()->mFlags |= EPropertyFlag::IsDirty;
|
||||
}
|
||||
RootParent()->mFlags |= EPropertyFlag::IsDirty;
|
||||
}
|
||||
|
||||
void IProperty::ClearDirtyFlag()
|
||||
|
||||
Reference in New Issue
Block a user