From 7dcfda78bae89031758e1e7e0ab745d3cebd9eff Mon Sep 17 00:00:00 2001 From: Aruki Date: Mon, 15 Oct 2018 02:58:11 -0600 Subject: [PATCH] Added support for converting some property types to other types --- src/Common/TString.h | 10 ++ src/Core/Resource/Script/NPropertyMap.cpp | 9 +- .../Resource/Script/Property/CEnumProperty.h | 5 + .../Script/Property/CFlagsProperty.cpp | 5 + .../Resource/Script/Property/CFlagsProperty.h | 1 + .../Resource/Script/Property/IProperty.cpp | 119 +++++++++++++++++- src/Core/Resource/Script/Property/IProperty.h | 15 ++- .../Resource/Script/Property/TPropertyRef.h | 2 +- src/Editor/PropertyEdit/CPropertyDelegate.cpp | 54 ++------ src/Editor/PropertyEdit/CPropertyDelegate.h | 1 - src/Editor/PropertyEdit/CPropertyModel.cpp | 43 ++++++- src/Editor/PropertyEdit/CPropertyModel.h | 1 + src/Editor/PropertyEdit/CPropertyView.cpp | 17 ++- src/Editor/PropertyEdit/CPropertyView.h | 3 +- .../WorldEditor/CTemplateEditDialog.cpp | 63 ++++++++++ src/Editor/WorldEditor/CTemplateEditDialog.h | 10 ++ src/Editor/WorldEditor/CTemplateEditDialog.ui | 38 ++++++ templates/PropertyMap.xml | 40 +++++- templates/mp2/Script/DebrisExtended.xml | 12 +- templates/mp2/Script/Kralee.xml | 6 +- templates/mp2/Script/RoomAcoustics.xml | 4 +- templates/mp2/Script/RumbleEffect.xml | 2 +- templates/mp2/Script/StreamedAudio.xml | 4 +- templates/mp2/Structs/SpawnPointStruct.xml | 4 +- 24 files changed, 393 insertions(+), 75 deletions(-) diff --git a/src/Common/TString.h b/src/Common/TString.h index cc08fc50..36c87cf1 100644 --- a/src/Common/TString.h +++ b/src/Common/TString.h @@ -1060,6 +1060,16 @@ public: return (Chr >= CHAR_LITERAL('a') && Chr <= CHAR_LITERAL('z')) ? Chr - 0x20 : Chr; } + static bool IsVowel(CharType Chr) + { + Chr = CharToUpper(Chr); + return (Chr == 'A' || + Chr == 'E' || + Chr == 'I' || + Chr == 'O' || + Chr == 'U'); + } + static bool IsWhitespace(CharType Chr) { return ( (Chr == CHAR_LITERAL('\t')) || diff --git a/src/Core/Resource/Script/NPropertyMap.cpp b/src/Core/Resource/Script/NPropertyMap.cpp index 09cb0379..2843c114 100644 --- a/src/Core/Resource/Script/NPropertyMap.cpp +++ b/src/Core/Resource/Script/NPropertyMap.cpp @@ -369,12 +369,13 @@ void ChangeTypeName(IProperty* pProperty, const char* pkOldTypeName, const char* SNameKey NewKey(NewTypeHash, pProperty->ID()); // Disassociate this property from the old mapping. + bool WasRegistered = false; auto Find = gNameMap.find(OldKey); if (Find != gNameMap.end()) { SNameValue& Value = Find->second; - NBasics::ListRemoveOne(Value.PropertyList, pProperty); + WasRegistered = NBasics::ListRemoveOne(Value.PropertyList, pProperty); } // Create a key for the new property and add it to the list. @@ -389,7 +390,11 @@ void ChangeTypeName(IProperty* pProperty, const char* pkOldTypeName, const char* Find = gNameMap.find(NewKey); } ASSERT(Find != gNameMap.end()); - Find->second.PropertyList.push_back(pProperty); + + if (WasRegistered) + { + Find->second.PropertyList.push_back(pProperty); + } gMapIsDirty = true; } diff --git a/src/Core/Resource/Script/Property/CEnumProperty.h b/src/Core/Resource/Script/Property/CEnumProperty.h index 60e5f791..63af11fd 100644 --- a/src/Core/Resource/Script/Property/CEnumProperty.h +++ b/src/Core/Resource/Script/Property/CEnumProperty.h @@ -99,6 +99,11 @@ public: mValues = pOtherEnum->mValues; } + virtual TString ValueAsString(void* pData) const + { + return TString::FromInt32( Value(pData), 0, 10 ); + } + void AddValue(TString ValueName, u32 ValueID) { mValues.push_back( SEnumValue(ValueName, ValueID) ); diff --git a/src/Core/Resource/Script/Property/CFlagsProperty.cpp b/src/Core/Resource/Script/Property/CFlagsProperty.cpp index eefb88ad..53006f4d 100644 --- a/src/Core/Resource/Script/Property/CFlagsProperty.cpp +++ b/src/Core/Resource/Script/Property/CFlagsProperty.cpp @@ -37,6 +37,11 @@ void CFlagsProperty::InitFromArchetype(IProperty* pOther) mAllFlags = pOtherFlags->mAllFlags; } +TString CFlagsProperty::ValueAsString(void* pData) const +{ + return TString::FromInt32( Value(pData), 0, 10 ); +} + /** * Checks whether there are any unrecognized bits toggled on in the property value. * Returns the mask of any invalid bits. If all bits are valid, returns 0. diff --git a/src/Core/Resource/Script/Property/CFlagsProperty.h b/src/Core/Resource/Script/Property/CFlagsProperty.h index 079b2a4c..e65b1b45 100644 --- a/src/Core/Resource/Script/Property/CFlagsProperty.h +++ b/src/Core/Resource/Script/Property/CFlagsProperty.h @@ -62,6 +62,7 @@ public: virtual void PostInitialize(); virtual void SerializeValue(void* pData, IArchive& rArc) const; virtual void InitFromArchetype(IProperty* pOther); + virtual TString ValueAsString(void* pData) const; /** * Checks whether there are any unrecognized bits toggled on in the property value. diff --git a/src/Core/Resource/Script/Property/IProperty.cpp b/src/Core/Resource/Script/Property/IProperty.cpp index 56b0979c..8976d068 100644 --- a/src/Core/Resource/Script/Property/IProperty.cpp +++ b/src/Core/Resource/Script/Property/IProperty.cpp @@ -393,6 +393,123 @@ void IProperty::ClearDirtyFlag() } } +bool IProperty::ConvertType(EPropertyType NewType, IProperty* pNewArchetype /*= nullptr*/) +{ + if (mpArchetype && !pNewArchetype) + { + // We need to start from the root archetype and cascade down sub-instances. + // The archetype will re-call this function with a valid pNewArchetype pointer. + return mpArchetype->ConvertType(NewType, nullptr); + } + + IProperty* pNewProperty = Create(NewType, Game()); + + // We can only replace properties with types that have the same size and alignment + if( pNewProperty->DataSize() != DataSize() || pNewProperty->DataAlignment() != DataAlignment() ) + { + delete pNewProperty; + return false; + } + + // Use InitFromArchetype to copy most parameters over from the original property. + // Note we do *not* want to call the virtual version, because the new property isn't + // actually the same type, so the virtual overrides will likely crash. + pNewProperty->IProperty::InitFromArchetype(this); + pNewProperty->mpArchetype = pNewArchetype; + NBasics::VectorRemoveOne(mSubInstances, pNewProperty); + + if( pNewArchetype ) + { + pNewArchetype->mSubInstances.push_back(pNewProperty); + } + + // We use CopyDefaultValueTo to ensure that the default value is preserved (as the default value + // is important in most games, and necessary to cook correctly in DKCR). However, note that + // other type-specific parameters (such as min/max values) are lost in the conversion. + CopyDefaultValueTo(pNewProperty); + + // Since we are about to delete this property, we need to unregister it and all its sub-instances + // from the name map, and change the type name. The reason we need to do it this way is because + // after we change the type name in the map, we won't be able to unregister the original properties + // because their type name won't match what's in the map. However, the change has to be done before + // initializing any new properties, or else they won't be able to initialize correctly, as the + // name won't be tracked in the map under the new type name. So we need to manually unregister + // everything to clear the original properties from the map, then change the type name, and then + // we're free to start creating and initializing new properties. + if (IsRootArchetype() && mGame >= EGame::EchoesDemo) + { + std::list SubInstances; + GatherAllSubInstances(SubInstances, true); + + for (auto Iter = SubInstances.begin(); Iter != SubInstances.end(); Iter++) + { + IProperty* pProperty = *Iter; + + if (pProperty->UsesNameMap()) + { + NPropertyMap::UnregisterProperty(pProperty); + } + } + + NPropertyMap::ChangeTypeName(this, HashableTypeName(), pNewProperty->HashableTypeName()); + } + + // Swap out our parent's reference to us to point to the new property. + if (mpParent) + { + for (u32 SiblingIdx = 0; SiblingIdx < mpParent->mChildren.size(); SiblingIdx++) + { + IProperty* pSibling = mpParent->mChildren[SiblingIdx]; + if (pSibling == this) + { + mpParent->mChildren[SiblingIdx] = pNewProperty; + break; + } + } + } + + // Change all our child properties to be parented under the new property. (Is this adoption?) + for (u32 ChildIdx = 0; ChildIdx < mChildren.size(); ChildIdx++) + { + mChildren[ChildIdx]->mpParent = pNewProperty; + pNewProperty->mChildren.push_back(mChildren[ChildIdx]); + } + ASSERT( pNewProperty->mChildren.size() == mChildren.size() ); + mChildren.clear(); + + // Create new versions of all sub-instances that inherit from the new property. + // Note that when the sub-instances complete their conversion, they delete themselves. + // The IProperty destructor removes the property from the archetype's sub-instance list. + // So we shouldn't use a for loop, instead we should just wait until the array is empty + u32 SubCount = mSubInstances.size(); + + while (!mSubInstances.empty()) + { + bool SubSuccess = mSubInstances[0]->ConvertType(NewType, pNewProperty); + ASSERT( SubSuccess ); + } + ASSERT( pNewProperty->mSubInstances.size() == SubCount ); + + // Conversion is complete! Initialize the new property and flag it dirty. + pNewProperty->Initialize( mpParent, mpScriptTemplate, mOffset ); + pNewProperty->MarkDirty(); + + // Finally, if we are done converting this property and all its instances, resave the templates. + if (IsRootArchetype()) + { + NGameList::SaveTemplates(); + + if (mGame >= EGame::EchoesDemo) + { + NPropertyMap::SaveMap(true); + } + } + + // We're done! + delete this; + return true; +} + bool IProperty::UsesNameMap() { return Game() >= EGame::EchoesDemo && @@ -440,7 +557,7 @@ bool IProperty::HasAccurateName() return mFlags.HasFlag( EPropertyFlag::HasCorrectPropertyName ); } -/** IPropertyNew Accessors */ +/** IProperty Accessors */ EGame IProperty::Game() const { return mGame; diff --git a/src/Core/Resource/Script/Property/IProperty.h b/src/Core/Resource/Script/Property/IProperty.h index 528d336a..27cd3be4 100644 --- a/src/Core/Resource/Script/Property/IProperty.h +++ b/src/Core/Resource/Script/Property/IProperty.h @@ -178,7 +178,8 @@ public: virtual void SerializeValue(void* pData, IArchive& Arc) const = 0; virtual void PostInitialize() {} - virtual void PropertyValueChanged(void* pPropertyData) {} + virtual void PropertyValueChanged(void* pPropertyData) {} + virtual void CopyDefaultValueTo(IProperty* pOtherProperty) {} virtual bool IsNumericalType() const { return false; } virtual bool IsPointerType() const { return false; } virtual TString ValueAsString(void* pData) const { return ""; } @@ -201,6 +202,7 @@ public: void SetSuffix(const TString& rkNewSuffix); void MarkDirty(); void ClearDirtyFlag(); + bool ConvertType(EPropertyType NewType, IProperty* pNewArchetype = nullptr); bool UsesNameMap(); bool HasAccurateName(); @@ -228,6 +230,7 @@ public: inline bool IsIntrinsic() const { return mFlags.HasFlag(EPropertyFlag::IsIntrinsic); } inline bool IsDirty() const { return mFlags.HasFlag(EPropertyFlag::IsDirty); } inline bool IsRootParent() const { return mpParent == nullptr; } + inline bool IsRootArchetype() const { return mpArchetype == nullptr; } /** Create */ static IProperty* Create(EPropertyType Type, @@ -375,6 +378,16 @@ public: mDefaultValue = static_cast(pOther)->mDefaultValue; } + virtual void CopyDefaultValueTo(IProperty* pOtherProperty) + { + // WARNING: We don't do any type checking here because this function is used for type conversion, + // which necessitates that the property class is allowed to be different. The underlying type is + // assumed to be the same. It is the caller's responsibility to ensure this function is not called + // with incompatible property types. + TTypedProperty* pTypedOther = static_cast(pOtherProperty); + pTypedOther->mDefaultValue = mDefaultValue; + } + inline PropType* ValuePtr(void* pData) const { return (PropType*) RawValuePtr(pData); diff --git a/src/Core/Resource/Script/Property/TPropertyRef.h b/src/Core/Resource/Script/Property/TPropertyRef.h index 727a8b72..2b1598c7 100644 --- a/src/Core/Resource/Script/Property/TPropertyRef.h +++ b/src/Core/Resource/Script/Property/TPropertyRef.h @@ -89,7 +89,7 @@ typedef TPropertyRef CSequenceRef; typedef TPropertyRef CSplineRef; typedef TPropertyRef CGuidRef; typedef TPropertyRef CPointerRef; -typedef TPropertyRef CStructRef; +typedef TPropertyRef CStructRef; typedef TPropertyRef CArrayRef; /** Special version for enums */ diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.cpp b/src/Editor/PropertyEdit/CPropertyDelegate.cpp index 19033116..a284e398 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.cpp +++ b/src/Editor/PropertyEdit/CPropertyDelegate.cpp @@ -53,7 +53,7 @@ QWidget* CPropertyDelegate::createEditor(QWidget *pParent, const QStyleOptionVie if (pProp) { - EPropertyType Type = GetEffectiveFieldType(pProp); + EPropertyType Type = mpModel->GetEffectiveFieldType(pProp); switch (Type) { @@ -168,7 +168,7 @@ QWidget* CPropertyDelegate::createEditor(QWidget *pParent, const QStyleOptionVie else if (rkIndex.internalId() & 0x80000000) { pProp = mpModel->PropertyForIndex(rkIndex, true); - EPropertyType Type = GetEffectiveFieldType(pProp); + EPropertyType Type = mpModel->GetEffectiveFieldType(pProp); // Handle character if (Type == EPropertyType::AnimationSet) @@ -208,7 +208,7 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd { if (!mEditInProgress) { - EPropertyType Type = pProp->Type(); + EPropertyType Type = mpModel->GetEffectiveFieldType(pProp); switch (Type) { @@ -240,7 +240,8 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd if (!pSpinBox->hasFocus()) { - CIntProperty *pInt = TPropCast(pProp); + // Ints use static_cast since sometimes we treat other property types as ints + CIntProperty *pInt = static_cast(pProp); pSpinBox->setValue( pInt->Value(pData) ); } @@ -334,7 +335,7 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd else if (rkIndex.internalId() & 0x80000000) { pProp = mpModel->PropertyForIndex(rkIndex, true); - EPropertyType Type = GetEffectiveFieldType(pProp); + EPropertyType Type = mpModel->GetEffectiveFieldType(pProp); if (Type == EPropertyType::AnimationSet) SetCharacterEditorData(pEditor, rkIndex); @@ -364,7 +365,7 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo if (pProp) { - EPropertyType Type = GetEffectiveFieldType(pProp); + EPropertyType Type = mpModel->GetEffectiveFieldType(pProp); QVector Objects; Objects << mpModel->GetScriptObject(); @@ -417,6 +418,7 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo case EPropertyType::Int: { + // Ints use static_cast since sometimes we treat other property types as ints WIntegralSpinBox* pSpinBox = static_cast(pEditor); CIntProperty* pInt = static_cast(pProp); pInt->ValueRef(pData) = pSpinBox->value(); @@ -675,46 +677,6 @@ EPropertyType CPropertyDelegate::DetermineCharacterPropType(EGame Game, const QM return EPropertyType::Invalid; } -/** Determine the effective property type to use. Allows some types to be treated as other types. */ -EPropertyType CPropertyDelegate::GetEffectiveFieldType(IProperty* pProperty) const -{ - EPropertyType Out = pProperty->Type(); - - switch (Out) - { - - // Allow Choice/Enum properties to be edited as Int properties if they don't have any values set. - case EPropertyType::Choice: - case EPropertyType::Enum: - { - CChoiceProperty* pChoice = TPropCast(pProperty); - - if (pChoice->NumPossibleValues() == 0) - { - Out = EPropertyType::Int; - } - - break; - } - - // Same deal with Flag properties - case EPropertyType::Flags: - { - CFlagsProperty* pFlags = TPropCast(pProperty); - - if (pFlags->NumFlags() == 0) - { - Out = EPropertyType::Int; - } - - break; - } - - } - - return Out; -} - // ************ PUBLIC SLOTS ************ void CPropertyDelegate::WidgetEdited(QWidget *pWidget, const QModelIndex& rkIndex) { diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.h b/src/Editor/PropertyEdit/CPropertyDelegate.h index cbfff2b5..95948a81 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.h +++ b/src/Editor/PropertyEdit/CPropertyDelegate.h @@ -29,7 +29,6 @@ public: void SetCharacterEditorData(QWidget *pEditor, const QModelIndex& rkIndex) const; void SetCharacterModelData(QWidget *pEditor, const QModelIndex& rkIndex) const; EPropertyType DetermineCharacterPropType(EGame Game, const QModelIndex& rkIndex) const; - EPropertyType GetEffectiveFieldType(IProperty* pProperty) const; public slots: void WidgetEdited(QWidget *pWidget, const QModelIndex& rkIndex); diff --git a/src/Editor/PropertyEdit/CPropertyModel.cpp b/src/Editor/PropertyEdit/CPropertyModel.cpp index 57d1e9df..1c27e1bc 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.cpp +++ b/src/Editor/PropertyEdit/CPropertyModel.cpp @@ -321,8 +321,9 @@ QVariant CPropertyModel::data(const QModelIndex& rkIndex, int Role) const if (rkIndex.column() == 1) { void* pData = DataPointerForIndex(rkIndex); + EPropertyType Type = GetEffectiveFieldType(pProp); - switch (pProp->Type()) + switch (Type) { // Enclose vector property text in parentheses case EPropertyType::Vector: @@ -620,6 +621,46 @@ void CPropertyModel::ClearSlot(int ID) mFirstUnusedID = ID; } +/** Determine the effective property type to use. Allows some types to be treated as other types. */ +EPropertyType CPropertyModel::GetEffectiveFieldType(IProperty* pProperty) const +{ + EPropertyType Out = pProperty->Type(); + + switch (Out) + { + + // Allow Choice/Enum properties to be edited as Int properties if they don't have any values set. + case EPropertyType::Choice: + case EPropertyType::Enum: + { + CChoiceProperty* pChoice = TPropCast(pProperty); + + if (pChoice->NumPossibleValues() == 0) + { + Out = EPropertyType::Int; + } + + break; + } + + // Same deal with Flag properties + case EPropertyType::Flags: + { + CFlagsProperty* pFlags = TPropCast(pProperty); + + if (pFlags->NumFlags() == 0) + { + Out = EPropertyType::Int; + } + + break; + } + + } + + return Out; +} + void CPropertyModel::SetShowPropertyNameValidity(bool Enable) { mShowNameValidity = Enable; diff --git a/src/Editor/PropertyEdit/CPropertyModel.h b/src/Editor/PropertyEdit/CPropertyModel.h index 24e57331..64dbb059 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.h +++ b/src/Editor/PropertyEdit/CPropertyModel.h @@ -52,6 +52,7 @@ public: void ResizeArray(const QModelIndex& rkIndex, u32 NewSize); void ClearSlot(int ID); + EPropertyType GetEffectiveFieldType(IProperty* pProperty) const; void SetShowPropertyNameValidity(bool Enable); inline void SetFont(QFont Font) { mFont = Font; } diff --git a/src/Editor/PropertyEdit/CPropertyView.cpp b/src/Editor/PropertyEdit/CPropertyView.cpp index 2c319630..0561a8e0 100644 --- a/src/Editor/PropertyEdit/CPropertyView.cpp +++ b/src/Editor/PropertyEdit/CPropertyView.cpp @@ -91,7 +91,7 @@ void CPropertyView::SetEditor(CWorldEditor *pEditor) connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), mpModel, SLOT(NotifyPropertyModified(CScriptObject*,IProperty*))); } -void CPropertyView::SetProperties(CStructRef InProperties) +void CPropertyView::SetIntrinsicProperties(CStructRef InProperties) { mpObject = nullptr; mpModel->SetBoldModifiedProperties(false); // todo, we prob want this, but can't set default properties on non script yet @@ -190,12 +190,17 @@ void CPropertyView::SetPersistentEditors(const QModelIndex& rkParent) switch (Type) { case EPropertyType::Bool: - case EPropertyType::Enum: - case EPropertyType::Choice: case EPropertyType::Color: case EPropertyType::Asset: openPersistentEditor(ChildIndex); break; + + case EPropertyType::Enum: + case EPropertyType::Choice: + if (TPropCast(pProp)->NumPossibleValues() > 0) + openPersistentEditor(ChildIndex); + break; + case EPropertyType::Struct: setFirstColumnSpanned(iChild, rkParent, true); break; @@ -232,6 +237,11 @@ void CPropertyView::OnPropertyModified(const QModelIndex& rkIndex) } } +void CPropertyView::RefreshView() +{ + SetInstance(mpObject); +} + void CPropertyView::CreateContextMenu(const QPoint& rkPos) { QModelIndex Index = indexAt(rkPos); @@ -286,6 +296,7 @@ void CPropertyView::ToggleShowNameValidity(bool ShouldShow) void CPropertyView::EditPropertyTemplate() { CTemplateEditDialog Dialog(mpMenuProperty, mpEditor); + connect(&Dialog, SIGNAL(PerformedTypeConversion()), this, SLOT(RefreshView())); Dialog.exec(); } diff --git a/src/Editor/PropertyEdit/CPropertyView.h b/src/Editor/PropertyEdit/CPropertyView.h index c03947f2..abced927 100644 --- a/src/Editor/PropertyEdit/CPropertyView.h +++ b/src/Editor/PropertyEdit/CPropertyView.h @@ -27,7 +27,7 @@ public: void setModel(QAbstractItemModel *pModel); bool event(QEvent *pEvent); void SetEditor(CWorldEditor *pEditor); - void SetProperties(CStructRef InProperties); + void SetIntrinsicProperties(CStructRef InProperties); void SetInstance(CScriptObject *pObj); void UpdateEditorProperties(const QModelIndex& rkParent); @@ -38,6 +38,7 @@ public slots: void ClosePersistentEditors(const QModelIndex& rkIndex); void OnPropertyModified(const QModelIndex& rkIndex); + void RefreshView(); void CreateContextMenu(const QPoint& rkPos); void ToggleShowNameValidity(bool ShouldShow); void EditPropertyTemplate(); diff --git a/src/Editor/WorldEditor/CTemplateEditDialog.cpp b/src/Editor/WorldEditor/CTemplateEditDialog.cpp index 821a9304..bdd2604d 100644 --- a/src/Editor/WorldEditor/CTemplateEditDialog.cpp +++ b/src/Editor/WorldEditor/CTemplateEditDialog.cpp @@ -7,6 +7,8 @@ #include #include +#include + CTemplateEditDialog::CTemplateEditDialog(IProperty *pProperty, QWidget *pParent) : QDialog(pParent) , mpUI(new Ui::CTemplateEditDialog) @@ -55,6 +57,21 @@ CTemplateEditDialog::CTemplateEditDialog(IProperty *pProperty, QWidget *pParent) } RefreshTypeNameOverride(); + // Configure convert button + if (Type == EPropertyType::Int || Type == EPropertyType::Choice || Type == EPropertyType::Flags || Type == EPropertyType::Sound) + { + QMenu* pConvertMenu = new QMenu(this); + if (Type != EPropertyType::Int) pConvertMenu->addAction("Int", this, SLOT(ConvertToInt())); + if (Type != EPropertyType::Choice) pConvertMenu->addAction("Choice", this, SLOT(ConvertToChoice())); + if (Type != EPropertyType::Flags) pConvertMenu->addAction("Flags", this, SLOT(ConvertToFlags())); + if (Type != EPropertyType::Sound) pConvertMenu->addAction("Sound", this, SLOT(ConvertToSound())); + mpUI->TypeConversionButton->setMenu(pConvertMenu); + } + else + { + mpUI->TypeConversionWidget->setHidden(true); + } + // Hide templates list for MP1 if (mGame <= EGame::Prime) { @@ -156,6 +173,52 @@ void CTemplateEditDialog::RefreshTypeNameOverride() } } +void CTemplateEditDialog::ConvertPropertyType(EPropertyType Type) +{ + const char* pkCurType = TEnumReflection::ConvertValueToString(mpProperty->Type()); + const char* pkNewType = TEnumReflection::ConvertValueToString(Type); + + if ( + UICommon::YesNoQuestion(this, "Warning", + QString("You are converting %1 %2 property to %3. This cannot be undone. Are you sure?") + .arg( TString::IsVowel(pkCurType[0]) ? "an" : "a" ) + .arg( pkCurType ) + .arg( pkNewType ) ) + ) + { + if( mpProperty->ConvertType(Type) ) + { + mpProperty = nullptr; + emit PerformedTypeConversion(); + close(); + } + else + { + UICommon::ErrorMsg(this, "Type conversion failed; conversion between these types is not supported."); + } + } +} + +void CTemplateEditDialog::ConvertToInt() +{ + ConvertPropertyType( EPropertyType::Int ); +} + +void CTemplateEditDialog::ConvertToChoice() +{ + ConvertPropertyType( EPropertyType::Choice ); +} + +void CTemplateEditDialog::ConvertToSound() +{ + ConvertPropertyType( EPropertyType::Sound ); +} + +void CTemplateEditDialog::ConvertToFlags() +{ + ConvertPropertyType( EPropertyType::Flags ); +} + // ************ PROTECTED ************ void CTemplateEditDialog::UpdateDescription(const TString& rkNewDesc) { diff --git a/src/Editor/WorldEditor/CTemplateEditDialog.h b/src/Editor/WorldEditor/CTemplateEditDialog.h index 79739b17..944f0e06 100644 --- a/src/Editor/WorldEditor/CTemplateEditDialog.h +++ b/src/Editor/WorldEditor/CTemplateEditDialog.h @@ -32,10 +32,20 @@ public: CTemplateEditDialog(IProperty* pProperty, QWidget *pParent = 0); ~CTemplateEditDialog(); +signals: + void PerformedTypeConversion(); + public slots: void ApplyChanges(); void RefreshTypeNameOverride(); +protected slots: + void ConvertPropertyType(EPropertyType Type); + void ConvertToInt(); + void ConvertToChoice(); + void ConvertToSound(); + void ConvertToFlags(); + protected: void UpdateDescription(const TString& rkNewDesc); void UpdateTypeName(const TString& kNewTypeName, bool AllowOverride); diff --git a/src/Editor/WorldEditor/CTemplateEditDialog.ui b/src/Editor/WorldEditor/CTemplateEditDialog.ui index cddc54ef..737d0046 100644 --- a/src/Editor/WorldEditor/CTemplateEditDialog.ui +++ b/src/Editor/WorldEditor/CTemplateEditDialog.ui @@ -170,6 +170,44 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 221 + 20 + + + + + + + + Convert to... + + + + + + diff --git a/templates/PropertyMap.xml b/templates/PropertyMap.xml index 5c6e4d01..01abee66 100644 --- a/templates/PropertyMap.xml +++ b/templates/PropertyMap.xml @@ -6045,6 +6045,10 @@ + + + + @@ -7617,6 +7621,10 @@ + + + + @@ -11329,6 +11337,10 @@ + + + + @@ -13917,6 +13929,10 @@ + + + + @@ -20217,6 +20233,10 @@ + + + + @@ -23617,9 +23637,13 @@ + + + + - + @@ -24729,6 +24753,10 @@ + + + + @@ -28541,6 +28569,10 @@ + + + + @@ -32729,6 +32761,10 @@ + + + + @@ -35259,7 +35295,7 @@ - + diff --git a/templates/mp2/Script/DebrisExtended.xml b/templates/mp2/Script/DebrisExtended.xml index 5de37bea..51a62455 100644 --- a/templates/mp2/Script/DebrisExtended.xml +++ b/templates/mp2/Script/DebrisExtended.xml @@ -121,8 +121,8 @@ false - - 0 + + 0x0 @@ -142,8 +142,8 @@ false - - 0 + + 0x0 @@ -157,8 +157,8 @@ 1.0 - - 0 + + 0x0 true diff --git a/templates/mp2/Script/Kralee.xml b/templates/mp2/Script/Kralee.xml index 9c874f3c..bdba590e 100644 --- a/templates/mp2/Script/Kralee.xml +++ b/templates/mp2/Script/Kralee.xml @@ -8,8 +8,8 @@ Kralee - - 0 + + 0x0 @@ -135,7 +135,7 @@ 0 - + -1 diff --git a/templates/mp2/Script/RoomAcoustics.xml b/templates/mp2/Script/RoomAcoustics.xml index 6b50411e..72d2e206 100644 --- a/templates/mp2/Script/RoomAcoustics.xml +++ b/templates/mp2/Script/RoomAcoustics.xml @@ -7,8 +7,8 @@ 117 - - 1 + + 0x1 false diff --git a/templates/mp2/Script/RumbleEffect.xml b/templates/mp2/Script/RumbleEffect.xml index 70795bbf..6d794643 100644 --- a/templates/mp2/Script/RumbleEffect.xml +++ b/templates/mp2/Script/RumbleEffect.xml @@ -8,7 +8,7 @@ 20.0 - 0 + 0x0 0 diff --git a/templates/mp2/Script/StreamedAudio.xml b/templates/mp2/Script/StreamedAudio.xml index 778ba56b..04eaccc3 100644 --- a/templates/mp2/Script/StreamedAudio.xml +++ b/templates/mp2/Script/StreamedAudio.xml @@ -17,8 +17,8 @@ 127 - - 0 + + 0x0 true diff --git a/templates/mp2/Structs/SpawnPointStruct.xml b/templates/mp2/Structs/SpawnPointStruct.xml index dc79be92..6e30be7f 100644 --- a/templates/mp2/Structs/SpawnPointStruct.xml +++ b/templates/mp2/Structs/SpawnPointStruct.xml @@ -72,8 +72,8 @@ 1 - - 1 + + 0x1 0