Added support for converting some property types to other types

This commit is contained in:
Aruki 2018-10-15 02:58:11 -06:00
parent 95d270cde7
commit 7dcfda78ba
24 changed files with 393 additions and 75 deletions

View File

@ -1060,6 +1060,16 @@ public:
return (Chr >= CHAR_LITERAL('a') && Chr <= CHAR_LITERAL('z')) ? Chr - 0x20 : Chr; 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) static bool IsWhitespace(CharType Chr)
{ {
return ( (Chr == CHAR_LITERAL('\t')) || return ( (Chr == CHAR_LITERAL('\t')) ||

View File

@ -369,12 +369,13 @@ void ChangeTypeName(IProperty* pProperty, const char* pkOldTypeName, const char*
SNameKey NewKey(NewTypeHash, pProperty->ID()); SNameKey NewKey(NewTypeHash, pProperty->ID());
// Disassociate this property from the old mapping. // Disassociate this property from the old mapping.
bool WasRegistered = false;
auto Find = gNameMap.find(OldKey); auto Find = gNameMap.find(OldKey);
if (Find != gNameMap.end()) if (Find != gNameMap.end())
{ {
SNameValue& Value = Find->second; 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. // 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); Find = gNameMap.find(NewKey);
} }
ASSERT(Find != gNameMap.end()); ASSERT(Find != gNameMap.end());
if (WasRegistered)
{
Find->second.PropertyList.push_back(pProperty); Find->second.PropertyList.push_back(pProperty);
}
gMapIsDirty = true; gMapIsDirty = true;
} }

View File

@ -99,6 +99,11 @@ public:
mValues = pOtherEnum->mValues; mValues = pOtherEnum->mValues;
} }
virtual TString ValueAsString(void* pData) const
{
return TString::FromInt32( Value(pData), 0, 10 );
}
void AddValue(TString ValueName, u32 ValueID) void AddValue(TString ValueName, u32 ValueID)
{ {
mValues.push_back( SEnumValue(ValueName, ValueID) ); mValues.push_back( SEnumValue(ValueName, ValueID) );

View File

@ -37,6 +37,11 @@ void CFlagsProperty::InitFromArchetype(IProperty* pOther)
mAllFlags = pOtherFlags->mAllFlags; 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. * 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. * Returns the mask of any invalid bits. If all bits are valid, returns 0.

View File

@ -62,6 +62,7 @@ public:
virtual void PostInitialize(); virtual void PostInitialize();
virtual void SerializeValue(void* pData, IArchive& rArc) const; virtual void SerializeValue(void* pData, IArchive& rArc) const;
virtual void InitFromArchetype(IProperty* pOther); virtual void InitFromArchetype(IProperty* pOther);
virtual TString ValueAsString(void* pData) const;
/** /**
* Checks whether there are any unrecognized bits toggled on in the property value. * Checks whether there are any unrecognized bits toggled on in the property value.

View File

@ -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<IProperty*> 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() bool IProperty::UsesNameMap()
{ {
return Game() >= EGame::EchoesDemo && return Game() >= EGame::EchoesDemo &&
@ -440,7 +557,7 @@ bool IProperty::HasAccurateName()
return mFlags.HasFlag( EPropertyFlag::HasCorrectPropertyName ); return mFlags.HasFlag( EPropertyFlag::HasCorrectPropertyName );
} }
/** IPropertyNew Accessors */ /** IProperty Accessors */
EGame IProperty::Game() const EGame IProperty::Game() const
{ {
return mGame; return mGame;

View File

@ -179,6 +179,7 @@ public:
virtual void PostInitialize() {} 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 IsNumericalType() const { return false; }
virtual bool IsPointerType() const { return false; } virtual bool IsPointerType() const { return false; }
virtual TString ValueAsString(void* pData) const { return ""; } virtual TString ValueAsString(void* pData) const { return ""; }
@ -201,6 +202,7 @@ public:
void SetSuffix(const TString& rkNewSuffix); void SetSuffix(const TString& rkNewSuffix);
void MarkDirty(); void MarkDirty();
void ClearDirtyFlag(); void ClearDirtyFlag();
bool ConvertType(EPropertyType NewType, IProperty* pNewArchetype = nullptr);
bool UsesNameMap(); bool UsesNameMap();
bool HasAccurateName(); bool HasAccurateName();
@ -228,6 +230,7 @@ public:
inline bool IsIntrinsic() const { return mFlags.HasFlag(EPropertyFlag::IsIntrinsic); } inline bool IsIntrinsic() const { return mFlags.HasFlag(EPropertyFlag::IsIntrinsic); }
inline bool IsDirty() const { return mFlags.HasFlag(EPropertyFlag::IsDirty); } inline bool IsDirty() const { return mFlags.HasFlag(EPropertyFlag::IsDirty); }
inline bool IsRootParent() const { return mpParent == nullptr; } inline bool IsRootParent() const { return mpParent == nullptr; }
inline bool IsRootArchetype() const { return mpArchetype == nullptr; }
/** Create */ /** Create */
static IProperty* Create(EPropertyType Type, static IProperty* Create(EPropertyType Type,
@ -375,6 +378,16 @@ public:
mDefaultValue = static_cast<TTypedProperty*>(pOther)->mDefaultValue; mDefaultValue = static_cast<TTypedProperty*>(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<TTypedProperty*>(pOtherProperty);
pTypedOther->mDefaultValue = mDefaultValue;
}
inline PropType* ValuePtr(void* pData) const inline PropType* ValuePtr(void* pData) const
{ {
return (PropType*) RawValuePtr(pData); return (PropType*) RawValuePtr(pData);

View File

@ -53,7 +53,7 @@ QWidget* CPropertyDelegate::createEditor(QWidget *pParent, const QStyleOptionVie
if (pProp) if (pProp)
{ {
EPropertyType Type = GetEffectiveFieldType(pProp); EPropertyType Type = mpModel->GetEffectiveFieldType(pProp);
switch (Type) switch (Type)
{ {
@ -168,7 +168,7 @@ QWidget* CPropertyDelegate::createEditor(QWidget *pParent, const QStyleOptionVie
else if (rkIndex.internalId() & 0x80000000) else if (rkIndex.internalId() & 0x80000000)
{ {
pProp = mpModel->PropertyForIndex(rkIndex, true); pProp = mpModel->PropertyForIndex(rkIndex, true);
EPropertyType Type = GetEffectiveFieldType(pProp); EPropertyType Type = mpModel->GetEffectiveFieldType(pProp);
// Handle character // Handle character
if (Type == EPropertyType::AnimationSet) if (Type == EPropertyType::AnimationSet)
@ -208,7 +208,7 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd
{ {
if (!mEditInProgress) if (!mEditInProgress)
{ {
EPropertyType Type = pProp->Type(); EPropertyType Type = mpModel->GetEffectiveFieldType(pProp);
switch (Type) switch (Type)
{ {
@ -240,7 +240,8 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd
if (!pSpinBox->hasFocus()) if (!pSpinBox->hasFocus())
{ {
CIntProperty *pInt = TPropCast<CIntProperty>(pProp); // Ints use static_cast since sometimes we treat other property types as ints
CIntProperty *pInt = static_cast<CIntProperty*>(pProp);
pSpinBox->setValue( pInt->Value(pData) ); pSpinBox->setValue( pInt->Value(pData) );
} }
@ -334,7 +335,7 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd
else if (rkIndex.internalId() & 0x80000000) else if (rkIndex.internalId() & 0x80000000)
{ {
pProp = mpModel->PropertyForIndex(rkIndex, true); pProp = mpModel->PropertyForIndex(rkIndex, true);
EPropertyType Type = GetEffectiveFieldType(pProp); EPropertyType Type = mpModel->GetEffectiveFieldType(pProp);
if (Type == EPropertyType::AnimationSet) if (Type == EPropertyType::AnimationSet)
SetCharacterEditorData(pEditor, rkIndex); SetCharacterEditorData(pEditor, rkIndex);
@ -364,7 +365,7 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
if (pProp) if (pProp)
{ {
EPropertyType Type = GetEffectiveFieldType(pProp); EPropertyType Type = mpModel->GetEffectiveFieldType(pProp);
QVector<CScriptObject*> Objects; QVector<CScriptObject*> Objects;
Objects << mpModel->GetScriptObject(); Objects << mpModel->GetScriptObject();
@ -417,6 +418,7 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
case EPropertyType::Int: case EPropertyType::Int:
{ {
// Ints use static_cast since sometimes we treat other property types as ints
WIntegralSpinBox* pSpinBox = static_cast<WIntegralSpinBox*>(pEditor); WIntegralSpinBox* pSpinBox = static_cast<WIntegralSpinBox*>(pEditor);
CIntProperty* pInt = static_cast<CIntProperty*>(pProp); CIntProperty* pInt = static_cast<CIntProperty*>(pProp);
pInt->ValueRef(pData) = pSpinBox->value(); pInt->ValueRef(pData) = pSpinBox->value();
@ -675,46 +677,6 @@ EPropertyType CPropertyDelegate::DetermineCharacterPropType(EGame Game, const QM
return EPropertyType::Invalid; 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<CChoiceProperty>(pProperty);
if (pChoice->NumPossibleValues() == 0)
{
Out = EPropertyType::Int;
}
break;
}
// Same deal with Flag properties
case EPropertyType::Flags:
{
CFlagsProperty* pFlags = TPropCast<CFlagsProperty>(pProperty);
if (pFlags->NumFlags() == 0)
{
Out = EPropertyType::Int;
}
break;
}
}
return Out;
}
// ************ PUBLIC SLOTS ************ // ************ PUBLIC SLOTS ************
void CPropertyDelegate::WidgetEdited(QWidget *pWidget, const QModelIndex& rkIndex) void CPropertyDelegate::WidgetEdited(QWidget *pWidget, const QModelIndex& rkIndex)
{ {

View File

@ -29,7 +29,6 @@ public:
void SetCharacterEditorData(QWidget *pEditor, const QModelIndex& rkIndex) const; void SetCharacterEditorData(QWidget *pEditor, const QModelIndex& rkIndex) const;
void SetCharacterModelData(QWidget *pEditor, const QModelIndex& rkIndex) const; void SetCharacterModelData(QWidget *pEditor, const QModelIndex& rkIndex) const;
EPropertyType DetermineCharacterPropType(EGame Game, const QModelIndex& rkIndex) const; EPropertyType DetermineCharacterPropType(EGame Game, const QModelIndex& rkIndex) const;
EPropertyType GetEffectiveFieldType(IProperty* pProperty) const;
public slots: public slots:
void WidgetEdited(QWidget *pWidget, const QModelIndex& rkIndex); void WidgetEdited(QWidget *pWidget, const QModelIndex& rkIndex);

View File

@ -321,8 +321,9 @@ QVariant CPropertyModel::data(const QModelIndex& rkIndex, int Role) const
if (rkIndex.column() == 1) if (rkIndex.column() == 1)
{ {
void* pData = DataPointerForIndex(rkIndex); void* pData = DataPointerForIndex(rkIndex);
EPropertyType Type = GetEffectiveFieldType(pProp);
switch (pProp->Type()) switch (Type)
{ {
// Enclose vector property text in parentheses // Enclose vector property text in parentheses
case EPropertyType::Vector: case EPropertyType::Vector:
@ -620,6 +621,46 @@ void CPropertyModel::ClearSlot(int ID)
mFirstUnusedID = 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<CChoiceProperty>(pProperty);
if (pChoice->NumPossibleValues() == 0)
{
Out = EPropertyType::Int;
}
break;
}
// Same deal with Flag properties
case EPropertyType::Flags:
{
CFlagsProperty* pFlags = TPropCast<CFlagsProperty>(pProperty);
if (pFlags->NumFlags() == 0)
{
Out = EPropertyType::Int;
}
break;
}
}
return Out;
}
void CPropertyModel::SetShowPropertyNameValidity(bool Enable) void CPropertyModel::SetShowPropertyNameValidity(bool Enable)
{ {
mShowNameValidity = Enable; mShowNameValidity = Enable;

View File

@ -52,6 +52,7 @@ public:
void ResizeArray(const QModelIndex& rkIndex, u32 NewSize); void ResizeArray(const QModelIndex& rkIndex, u32 NewSize);
void ClearSlot(int ID); void ClearSlot(int ID);
EPropertyType GetEffectiveFieldType(IProperty* pProperty) const;
void SetShowPropertyNameValidity(bool Enable); void SetShowPropertyNameValidity(bool Enable);
inline void SetFont(QFont Font) { mFont = Font; } inline void SetFont(QFont Font) { mFont = Font; }

View File

@ -91,7 +91,7 @@ void CPropertyView::SetEditor(CWorldEditor *pEditor)
connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), mpModel, SLOT(NotifyPropertyModified(CScriptObject*,IProperty*))); connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), mpModel, SLOT(NotifyPropertyModified(CScriptObject*,IProperty*)));
} }
void CPropertyView::SetProperties(CStructRef InProperties) void CPropertyView::SetIntrinsicProperties(CStructRef InProperties)
{ {
mpObject = nullptr; mpObject = nullptr;
mpModel->SetBoldModifiedProperties(false); // todo, we prob want this, but can't set default properties on non script yet 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) switch (Type)
{ {
case EPropertyType::Bool: case EPropertyType::Bool:
case EPropertyType::Enum:
case EPropertyType::Choice:
case EPropertyType::Color: case EPropertyType::Color:
case EPropertyType::Asset: case EPropertyType::Asset:
openPersistentEditor(ChildIndex); openPersistentEditor(ChildIndex);
break; break;
case EPropertyType::Enum:
case EPropertyType::Choice:
if (TPropCast<CEnumProperty>(pProp)->NumPossibleValues() > 0)
openPersistentEditor(ChildIndex);
break;
case EPropertyType::Struct: case EPropertyType::Struct:
setFirstColumnSpanned(iChild, rkParent, true); setFirstColumnSpanned(iChild, rkParent, true);
break; break;
@ -232,6 +237,11 @@ void CPropertyView::OnPropertyModified(const QModelIndex& rkIndex)
} }
} }
void CPropertyView::RefreshView()
{
SetInstance(mpObject);
}
void CPropertyView::CreateContextMenu(const QPoint& rkPos) void CPropertyView::CreateContextMenu(const QPoint& rkPos)
{ {
QModelIndex Index = indexAt(rkPos); QModelIndex Index = indexAt(rkPos);
@ -286,6 +296,7 @@ void CPropertyView::ToggleShowNameValidity(bool ShouldShow)
void CPropertyView::EditPropertyTemplate() void CPropertyView::EditPropertyTemplate()
{ {
CTemplateEditDialog Dialog(mpMenuProperty, mpEditor); CTemplateEditDialog Dialog(mpMenuProperty, mpEditor);
connect(&Dialog, SIGNAL(PerformedTypeConversion()), this, SLOT(RefreshView()));
Dialog.exec(); Dialog.exec();
} }

View File

@ -27,7 +27,7 @@ public:
void setModel(QAbstractItemModel *pModel); void setModel(QAbstractItemModel *pModel);
bool event(QEvent *pEvent); bool event(QEvent *pEvent);
void SetEditor(CWorldEditor *pEditor); void SetEditor(CWorldEditor *pEditor);
void SetProperties(CStructRef InProperties); void SetIntrinsicProperties(CStructRef InProperties);
void SetInstance(CScriptObject *pObj); void SetInstance(CScriptObject *pObj);
void UpdateEditorProperties(const QModelIndex& rkParent); void UpdateEditorProperties(const QModelIndex& rkParent);
@ -38,6 +38,7 @@ public slots:
void ClosePersistentEditors(const QModelIndex& rkIndex); void ClosePersistentEditors(const QModelIndex& rkIndex);
void OnPropertyModified(const QModelIndex& rkIndex); void OnPropertyModified(const QModelIndex& rkIndex);
void RefreshView();
void CreateContextMenu(const QPoint& rkPos); void CreateContextMenu(const QPoint& rkPos);
void ToggleShowNameValidity(bool ShouldShow); void ToggleShowNameValidity(bool ShouldShow);
void EditPropertyTemplate(); void EditPropertyTemplate();

View File

@ -7,6 +7,8 @@
#include <Core/Resource/Script/NGameList.h> #include <Core/Resource/Script/NGameList.h>
#include <Core/Resource/Script/NPropertyMap.h> #include <Core/Resource/Script/NPropertyMap.h>
#include <QMenu>
CTemplateEditDialog::CTemplateEditDialog(IProperty *pProperty, QWidget *pParent) CTemplateEditDialog::CTemplateEditDialog(IProperty *pProperty, QWidget *pParent)
: QDialog(pParent) : QDialog(pParent)
, mpUI(new Ui::CTemplateEditDialog) , mpUI(new Ui::CTemplateEditDialog)
@ -55,6 +57,21 @@ CTemplateEditDialog::CTemplateEditDialog(IProperty *pProperty, QWidget *pParent)
} }
RefreshTypeNameOverride(); 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 // Hide templates list for MP1
if (mGame <= EGame::Prime) if (mGame <= EGame::Prime)
{ {
@ -156,6 +173,52 @@ void CTemplateEditDialog::RefreshTypeNameOverride()
} }
} }
void CTemplateEditDialog::ConvertPropertyType(EPropertyType Type)
{
const char* pkCurType = TEnumReflection<EPropertyType>::ConvertValueToString(mpProperty->Type());
const char* pkNewType = TEnumReflection<EPropertyType>::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 ************ // ************ PROTECTED ************
void CTemplateEditDialog::UpdateDescription(const TString& rkNewDesc) void CTemplateEditDialog::UpdateDescription(const TString& rkNewDesc)
{ {

View File

@ -32,10 +32,20 @@ public:
CTemplateEditDialog(IProperty* pProperty, QWidget *pParent = 0); CTemplateEditDialog(IProperty* pProperty, QWidget *pParent = 0);
~CTemplateEditDialog(); ~CTemplateEditDialog();
signals:
void PerformedTypeConversion();
public slots: public slots:
void ApplyChanges(); void ApplyChanges();
void RefreshTypeNameOverride(); void RefreshTypeNameOverride();
protected slots:
void ConvertPropertyType(EPropertyType Type);
void ConvertToInt();
void ConvertToChoice();
void ConvertToSound();
void ConvertToFlags();
protected: protected:
void UpdateDescription(const TString& rkNewDesc); void UpdateDescription(const TString& rkNewDesc);
void UpdateTypeName(const TString& kNewTypeName, bool AllowOverride); void UpdateTypeName(const TString& kNewTypeName, bool AllowOverride);

View File

@ -170,6 +170,44 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QWidget" name="TypeConversionWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>221</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="TypeConversionButton">
<property name="text">
<string>Convert to...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="TemplatesGroupBox"> <widget class="QGroupBox" name="TemplatesGroupBox">
<property name="sizePolicy"> <property name="sizePolicy">

View File

@ -6045,6 +6045,10 @@
<Key ID="0x28F49A53" Type="VolcanoBossBodyPartStructA"/> <Key ID="0x28F49A53" Type="VolcanoBossBodyPartStructA"/>
<Value Name="VolcanoBossBodyPartStructA"/> <Value Name="VolcanoBossBodyPartStructA"/>
</Element> </Element>
<Element>
<Key ID="0x28F82261" Type="choice"/>
<Value Name="SoftwareChannel"/>
</Element>
<Element> <Element>
<Key ID="0x28F82261" Type="int"/> <Key ID="0x28F82261" Type="int"/>
<Value Name="SoftwareChannel"/> <Value Name="SoftwareChannel"/>
@ -7617,6 +7621,10 @@
<Key ID="0x3343ADE6" Type="int"/> <Key ID="0x3343ADE6" Type="int"/>
<Value Name="CounterCondition7"/> <Value Name="CounterCondition7"/>
</Element> </Element>
<Element>
<Key ID="0x334A34BB" Type="choice"/>
<Value Name="ParticleSystem1Orientation"/>
</Element>
<Element> <Element>
<Key ID="0x334A34BB" Type="int"/> <Key ID="0x334A34BB" Type="int"/>
<Value Name="ParticleSystem1Orientation"/> <Value Name="ParticleSystem1Orientation"/>
@ -11329,6 +11337,10 @@
<Key ID="0x4B106481" Type="float"/> <Key ID="0x4B106481" Type="float"/>
<Value Name="Unknown"/> <Value Name="Unknown"/>
</Element> </Element>
<Element>
<Key ID="0x4B15EB9A" Type="choice"/>
<Value Name="ChargeUpgrade"/>
</Element>
<Element> <Element>
<Key ID="0x4B15EB9A" Type="int"/> <Key ID="0x4B15EB9A" Type="int"/>
<Value Name="ChargeUpgrade"/> <Value Name="ChargeUpgrade"/>
@ -13917,6 +13929,10 @@
<Key ID="0x5D28CCE5" Type="float"/> <Key ID="0x5D28CCE5" Type="float"/>
<Value Name="Unknown"/> <Value Name="Unknown"/>
</Element> </Element>
<Element>
<Key ID="0x5D298A43" Type="choice"/>
<Value Name="Unknown"/>
</Element>
<Element> <Element>
<Key ID="0x5D298A43" Type="int"/> <Key ID="0x5D298A43" Type="int"/>
<Value Name="Unknown"/> <Value Name="Unknown"/>
@ -20217,6 +20233,10 @@
<Key ID="0x86C887A5" Type="bool"/> <Key ID="0x86C887A5" Type="bool"/>
<Value Name="Unknown"/> <Value Name="Unknown"/>
</Element> </Element>
<Element>
<Key ID="0x86CF23F4" Type="choice"/>
<Value Name="Priority"/>
</Element>
<Element> <Element>
<Key ID="0x86CF23F4" Type="int"/> <Key ID="0x86CF23F4" Type="int"/>
<Value Name="Priority"/> <Value Name="Priority"/>
@ -23617,9 +23637,13 @@
<Key ID="0x9DEE347A" Type="int"/> <Key ID="0x9DEE347A" Type="int"/>
<Value Name="CenterPlank"/> <Value Name="CenterPlank"/>
</Element> </Element>
<Element>
<Key ID="0x9DFADEE0" Type="choice"/>
<Value Name="DeathParticleSystemOrientation"/>
</Element>
<Element> <Element>
<Key ID="0x9DFADEE0" Type="int"/> <Key ID="0x9DFADEE0" Type="int"/>
<Value Name="Unknown"/> <Value Name="DeathParticleSystemOrientation"/>
</Element> </Element>
<Element> <Element>
<Key ID="0x9E02691C" Type="sound"/> <Key ID="0x9E02691C" Type="sound"/>
@ -24729,6 +24753,10 @@
<Key ID="0xA4EE16BF" Type="sound"/> <Key ID="0xA4EE16BF" Type="sound"/>
<Value Name="Sound1"/> <Value Name="Sound1"/>
</Element> </Element>
<Element>
<Key ID="0xA4EF7B42" Type="sound"/>
<Value Name="WarpOutSound"/>
</Element>
<Element> <Element>
<Key ID="0xA4EF7B42" Type="int"/> <Key ID="0xA4EF7B42" Type="int"/>
<Value Name="WarpOutSound"/> <Value Name="WarpOutSound"/>
@ -28541,6 +28569,10 @@
<Key ID="0xBE5F118D" Type="asset"/> <Key ID="0xBE5F118D" Type="asset"/>
<Value Name="SwoopInterruptedSound"/> <Value Name="SwoopInterruptedSound"/>
</Element> </Element>
<Element>
<Key ID="0xBE73724A" Type="choice"/>
<Value Name="Flavor"/>
</Element>
<Element> <Element>
<Key ID="0xBE73724A" Type="int"/> <Key ID="0xBE73724A" Type="int"/>
<Value Name="Flavor"/> <Value Name="Flavor"/>
@ -32729,6 +32761,10 @@
<Key ID="0xD9CA50C2" Type="spline"/> <Key ID="0xD9CA50C2" Type="spline"/>
<Value Name="SlideSoundLowPassFilter"/> <Value Name="SlideSoundLowPassFilter"/>
</Element> </Element>
<Element>
<Key ID="0xD9CCE9D9" Type="choice"/>
<Value Name="ParticleSystem2Orientation"/>
</Element>
<Element> <Element>
<Key ID="0xD9CCE9D9" Type="int"/> <Key ID="0xD9CCE9D9" Type="int"/>
<Value Name="ParticleSystem2Orientation"/> <Value Name="ParticleSystem2Orientation"/>
@ -35259,7 +35295,7 @@
</Element> </Element>
<Element> <Element>
<Key ID="0xEA86A2B9" Type="int"/> <Key ID="0xEA86A2B9" Type="int"/>
<Value Name="Charge Combo"/> <Value Name="Unknown"/>
</Element> </Element>
<Element> <Element>
<Key ID="0xEA870072" Type="UnknownStruct66"/> <Key ID="0xEA870072" Type="UnknownStruct66"/>

View File

@ -121,8 +121,8 @@
<Element Type="Bool" ID="0x3BDD2FED"> <Element Type="Bool" ID="0x3BDD2FED">
<DefaultValue>false</DefaultValue> <DefaultValue>false</DefaultValue>
</Element> </Element>
<Element Type="Int" ID="0x334A34BB"> <Element Type="Choice" ID="0x334A34BB">
<DefaultValue>0</DefaultValue> <DefaultValue>0x0</DefaultValue>
</Element> </Element>
<Element Type="Asset" ID="0xC7493FEE"> <Element Type="Asset" ID="0xC7493FEE">
<TypeFilter> <TypeFilter>
@ -142,8 +142,8 @@
<Element Type="Bool" ID="0xC98AC215"> <Element Type="Bool" ID="0xC98AC215">
<DefaultValue>false</DefaultValue> <DefaultValue>false</DefaultValue>
</Element> </Element>
<Element Type="Int" ID="0xD9CCE9D9"> <Element Type="Choice" ID="0xD9CCE9D9">
<DefaultValue>0</DefaultValue> <DefaultValue>0x0</DefaultValue>
</Element> </Element>
<Element Type="Asset" ID="0x979042C8"> <Element Type="Asset" ID="0x979042C8">
<TypeFilter> <TypeFilter>
@ -157,8 +157,8 @@
<Z>1.0</Z> <Z>1.0</Z>
</DefaultValue> </DefaultValue>
</Element> </Element>
<Element Type="Int" ID="0x9DFADEE0"> <Element Type="Choice" ID="0x9DFADEE0">
<DefaultValue>0</DefaultValue> <DefaultValue>0x0</DefaultValue>
</Element> </Element>
<Element Type="Bool" ID="0x2C7B18DD"> <Element Type="Bool" ID="0x2C7B18DD">
<DefaultValue>true</DefaultValue> <DefaultValue>true</DefaultValue>

View File

@ -8,8 +8,8 @@
<Name>Kralee</Name> <Name>Kralee</Name>
<SubProperties> <SubProperties>
<Element Type="Struct" ID="0x255A4580" Archetype="EditorProperties"/> <Element Type="Struct" ID="0x255A4580" Archetype="EditorProperties"/>
<Element Type="Int" ID="0xBE73724A"> <Element Type="Choice" ID="0xBE73724A">
<DefaultValue>0</DefaultValue> <DefaultValue>0x0</DefaultValue>
</Element> </Element>
<Element Type="Struct" ID="0xB3774750" Archetype="PatternedAITypedef"> <Element Type="Struct" ID="0xB3774750" Archetype="PatternedAITypedef">
<SubProperties> <SubProperties>
@ -135,7 +135,7 @@
<Element Type="Sound" ID="0x80B58324"> <Element Type="Sound" ID="0x80B58324">
<DefaultValue>0</DefaultValue> <DefaultValue>0</DefaultValue>
</Element> </Element>
<Element Type="Int" ID="0xA4EF7B42"> <Element Type="Sound" ID="0xA4EF7B42">
<DefaultValue>-1</DefaultValue> <DefaultValue>-1</DefaultValue>
</Element> </Element>
<Element Type="Bool" ID="0xC3CC437F"> <Element Type="Bool" ID="0xC3CC437F">

View File

@ -7,8 +7,8 @@
<Element Type="Int" ID="0xBD9EA266"> <Element Type="Int" ID="0xBD9EA266">
<DefaultValue>117</DefaultValue> <DefaultValue>117</DefaultValue>
</Element> </Element>
<Element Type="Int" ID="0x86CF23F4"> <Element Type="Choice" ID="0x86CF23F4">
<DefaultValue>1</DefaultValue> <DefaultValue>0x1</DefaultValue>
</Element> </Element>
<Element Type="Bool" ID="0xA00360CC"> <Element Type="Bool" ID="0xA00360CC">
<DefaultValue>false</DefaultValue> <DefaultValue>false</DefaultValue>

View File

@ -8,7 +8,7 @@
<DefaultValue>20.0</DefaultValue> <DefaultValue>20.0</DefaultValue>
</Element> </Element>
<Element Type="Choice" ID="0x68ACBD86"> <Element Type="Choice" ID="0x68ACBD86">
<DefaultValue>0</DefaultValue> <DefaultValue>0x0</DefaultValue>
</Element> </Element>
<Element Type="Flags" ID="0x4F7FEC39"> <Element Type="Flags" ID="0x4F7FEC39">
<DefaultValue>0</DefaultValue> <DefaultValue>0</DefaultValue>

View File

@ -17,8 +17,8 @@
<Element Type="Int" ID="0x80C66C37"> <Element Type="Int" ID="0x80C66C37">
<DefaultValue>127</DefaultValue> <DefaultValue>127</DefaultValue>
</Element> </Element>
<Element Type="Int" ID="0x28F82261"> <Element Type="Choice" ID="0x28F82261">
<DefaultValue>0</DefaultValue> <DefaultValue>0x0</DefaultValue>
</Element> </Element>
<Element Type="Bool" ID="0xD3356FE7"> <Element Type="Bool" ID="0xD3356FE7">
<DefaultValue>true</DefaultValue> <DefaultValue>true</DefaultValue>

View File

@ -72,8 +72,8 @@
<Element Type="Int" ID="0xFB33F2A1"> <Element Type="Int" ID="0xFB33F2A1">
<DefaultValue>1</DefaultValue> <DefaultValue>1</DefaultValue>
</Element> </Element>
<Element Type="Int" ID="0x4B15EB9A"> <Element Type="Choice" ID="0x4B15EB9A">
<DefaultValue>1</DefaultValue> <DefaultValue>0x1</DefaultValue>
</Element> </Element>
<Element Type="Int" ID="0x44FBB19C"> <Element Type="Int" ID="0x44FBB19C">
<DefaultValue>0</DefaultValue> <DefaultValue>0</DefaultValue>