From 1a07a9c0835786664a6869f6de4afe3b9bd2b45f Mon Sep 17 00:00:00 2001 From: parax0 Date: Mon, 1 Feb 2016 16:42:12 -0700 Subject: [PATCH] Added TPropCast property casting function, added support for clean/dirty state in the world editor + check for unsaved changes when the window is closed --- src/Core/CLightParameters.h | 10 +- src/Core/Resource/Script/IProperty.h | 13 ++ src/Core/Scene/CScriptNode.cpp | 7 +- .../ScriptExtra/CDamageableTriggerExtra.cpp | 36 ++--- .../ScriptExtra/CDamageableTriggerExtra.h | 1 + src/Core/ScriptExtra/CDoorExtra.cpp | 24 +--- .../ScriptExtra/CPointOfInterestExtra.cpp | 29 +--- src/Core/ScriptExtra/CRadiusSphereExtra.cpp | 6 +- src/Core/ScriptExtra/CSpacePirateExtra.cpp | 19 +-- src/Core/ScriptExtra/CSpacePirateExtra.h | 8 +- src/Editor/CStartWindow.cpp | 4 +- src/Editor/Editor.pro | 3 +- src/Editor/Undo/CBasicPropertyCommand.cpp | 2 +- src/Editor/Undo/CBasicPropertyCommand.h | 5 +- src/Editor/Undo/CClearSelectionCommand.cpp | 2 +- src/Editor/Undo/CClearSelectionCommand.h | 6 +- src/Editor/Undo/CDeselectNodeCommand.cpp | 2 +- src/Editor/Undo/CDeselectNodeCommand.h | 6 +- src/Editor/Undo/CInvertSelectionCommand.cpp | 2 +- src/Editor/Undo/CInvertSelectionCommand.h | 6 +- src/Editor/Undo/CRotateNodeCommand.cpp | 4 +- src/Editor/Undo/CRotateNodeCommand.h | 6 +- src/Editor/Undo/CScaleNodeCommand.cpp | 4 +- src/Editor/Undo/CScaleNodeCommand.h | 6 +- src/Editor/Undo/CSelectAllCommand.cpp | 2 +- src/Editor/Undo/CSelectAllCommand.h | 6 +- src/Editor/Undo/CSelectNodeCommand.cpp | 2 +- src/Editor/Undo/CSelectNodeCommand.h | 6 +- src/Editor/Undo/CTranslateNodeCommand.cpp | 4 +- src/Editor/Undo/CTranslateNodeCommand.h | 6 +- src/Editor/Undo/IUndoCommand.h | 19 +++ src/Editor/WorldEditor/CWorldEditor.cpp | 124 ++++++++++++++++-- src/Editor/WorldEditor/CWorldEditor.h | 8 +- templates/mp1/Script/Water.xml | 17 ++- 34 files changed, 242 insertions(+), 163 deletions(-) create mode 100644 src/Editor/Undo/IUndoCommand.h diff --git a/src/Core/CLightParameters.h b/src/Core/CLightParameters.h index 3f77ed3b..e908e544 100644 --- a/src/Core/CLightParameters.h +++ b/src/Core/CLightParameters.h @@ -18,7 +18,7 @@ class CLightParameters EGame mGame; TLongProperty *mpLightLayer; - TLongProperty *mpWorldLightingOptions; + TEnumProperty *mpWorldLightingOptions; public: CLightParameters(CPropertyStruct *pStruct, EGame Game) @@ -31,13 +31,13 @@ public: { if (mGame <= ePrime) { - mpWorldLightingOptions = (TLongProperty*) mpStruct->PropertyByIndex(0x7); - mpLightLayer = (TLongProperty*) mpStruct->PropertyByIndex(0xD); + mpWorldLightingOptions = TPropCast(mpStruct->PropertyByIndex(0x7)); + mpLightLayer = TPropCast(mpStruct->PropertyByIndex(0xD)); } else { - mpWorldLightingOptions = (TLongProperty*) mpStruct->PropertyByIndex(0x6B5E7509); - mpLightLayer = (TLongProperty*) mpStruct->PropertyByID(0x1F715FD3); + mpWorldLightingOptions = TPropCast(mpStruct->PropertyByID(0x6B5E7509)); + mpLightLayer = TPropCast(mpStruct->PropertyByID(0x1F715FD3)); } } } diff --git a/src/Core/Resource/Script/IProperty.h b/src/Core/Resource/Script/IProperty.h index 18e1433e..44c4e15f 100644 --- a/src/Core/Resource/Script/IProperty.h +++ b/src/Core/Resource/Script/IProperty.h @@ -71,6 +71,8 @@ public: ~TTypedProperty() {} virtual EPropertyType Type() const { return TypeEnum; } + static inline EPropertyType StaticType() { return TypeEnum; } + virtual TString ToString() const { return mValue.ToString(); } virtual IPropertyValue* RawValue() { return &mValue; } @@ -124,6 +126,7 @@ public: } EPropertyType Type() const { return eStructProperty; } + static inline EPropertyType StaticType() { return eStructProperty; } virtual void Copy(const IProperty *pkProp); @@ -161,6 +164,7 @@ public: : CPropertyStruct(pTemp, pParent) {} EPropertyType Type() const { return eArrayProperty; } + static inline EPropertyType StaticType() { return eArrayProperty; } virtual IProperty* Clone(CPropertyStruct *pParent) const { @@ -179,5 +183,14 @@ public: TString ElementName() const; }; +/* + * Function for casting properties. Returns null if the property is not actually the requested type. + */ +template +PropertyClass* TPropCast(IProperty *pProp) +{ + return (pProp && pProp->Type() == PropertyClass::StaticType() ? static_cast(pProp) : nullptr); +} + #endif // IPROPERTY diff --git a/src/Core/Scene/CScriptNode.cpp b/src/Core/Scene/CScriptNode.cpp index ab0330b4..2d3e5acf 100644 --- a/src/Core/Scene/CScriptNode.cpp +++ b/src/Core/Scene/CScriptNode.cpp @@ -76,9 +76,6 @@ ENodeType CScriptNode::NodeType() void CScriptNode::OnTransformed() { - if (mpExtra) - mpExtra->OnTransformed(); - if (mpInstance) { CScriptTemplate *pTemplate = Template(); @@ -92,6 +89,9 @@ void CScriptNode::OnTransformed() if (pTemplate->HasScale() && LocalScale() != mpInstance->Scale()) mpInstance->SetScale(LocalScale()); } + + if (mpExtra) + mpExtra->OnTransformed(); } void CScriptNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo) @@ -167,7 +167,6 @@ void CScriptNode::Draw(FRenderOptions Options, int ComponentIndex, const SViewIn else { LoadLights(ViewInfo); - CGraphics::UpdateLightBlock(); } LoadModelMatrix(); diff --git a/src/Core/ScriptExtra/CDamageableTriggerExtra.cpp b/src/Core/ScriptExtra/CDamageableTriggerExtra.cpp index 87a00a52..5229b7b5 100644 --- a/src/Core/ScriptExtra/CDamageableTriggerExtra.cpp +++ b/src/Core/ScriptExtra/CDamageableTriggerExtra.cpp @@ -16,36 +16,18 @@ CDamageableTriggerExtra::CDamageableTriggerExtra(CScriptObject *pInstance, CScen CPropertyStruct *pBaseStruct = pInstance->Properties(); // Fetch render side - mpRenderSideProp = (TEnumProperty*) pBaseStruct->PropertyByIndex(0x5); - - if (mpRenderSideProp && mpRenderSideProp->Type() != eEnumProperty) - mpRenderSideProp = nullptr; - + mpRenderSideProp = TPropCast(pBaseStruct->PropertyByIndex(0x5)); if (mpRenderSideProp) PropertyModified(mpRenderSideProp); // Fetch scale - mpSizeProp = (TVector3Property*) pBaseStruct->PropertyByIndex(0x2); - - if (mpSizeProp && mpSizeProp->Type() != eVector3Property) - mpSizeProp = nullptr; - - if (mpSizeProp) PropertyModified (mpSizeProp); + mpSizeProp = TPropCast(pBaseStruct->PropertyByIndex(0x2)); + if (mpSizeProp) PropertyModified(mpSizeProp); // Fetch textures for (u32 iTex = 0; iTex < 3; iTex++) { - mpTextureProps[iTex] = (TFileProperty*) pBaseStruct->PropertyByIndex(0x6 + iTex); - - if (mpTextureProps[iTex]) - { - if (mpTextureProps[iTex]->Type() == eFileProperty) - PropertyModified(mpTextureProps[iTex]); - else - mpTextureProps[iTex] = nullptr; - } - - else - mpTextures[iTex] = nullptr; + mpTextureProps[iTex] = TPropCast(pBaseStruct->PropertyByIndex(0x6 + iTex)); + if (mpTextureProps[iTex]) PropertyModified(mpTextureProps[iTex]); } } @@ -147,6 +129,12 @@ void CDamageableTriggerExtra::UpdatePlaneTransform() MarkTransformChanged(); } +void CDamageableTriggerExtra::OnTransformed() +{ + mPlaneSize = mpSizeProp->Get(); + UpdatePlaneTransform(); +} + void CDamageableTriggerExtra::PropertyModified(IProperty *pProperty) { if (pProperty == mpRenderSideProp) @@ -209,7 +197,7 @@ void CDamageableTriggerExtra::Draw(FRenderOptions Options, int /*ComponentIndex* // Note: The plane the game renders this onto is 5x4.5, which is why we divide the tex coords by this value CVector2f TexUL(0.f, mCoordScale.y / 4.5f); - CVector2f TexUR(mCoordScale.x / 5.f, mCoordScale.y / 4.5f); + CVector2f TexUR(mCoordScale.x / 5.f, mCoordScale.y / 4.5f); CVector2f TexBR(mCoordScale.x / 5.f, 0.f); CVector2f TexBL(0.f, 0.f); CDrawUtil::DrawSquare(TexUL, TexUR, TexBR, TexBL); diff --git a/src/Core/ScriptExtra/CDamageableTriggerExtra.h b/src/Core/ScriptExtra/CDamageableTriggerExtra.h index 7f33f4fb..e0d3191a 100644 --- a/src/Core/ScriptExtra/CDamageableTriggerExtra.h +++ b/src/Core/ScriptExtra/CDamageableTriggerExtra.h @@ -35,6 +35,7 @@ public: ~CDamageableTriggerExtra(); void CreateMaterial(); void UpdatePlaneTransform(); + void OnTransformed(); void PropertyModified(IProperty *pProperty); bool ShouldDrawNormalAssets(); void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo); diff --git a/src/Core/ScriptExtra/CDoorExtra.cpp b/src/Core/ScriptExtra/CDoorExtra.cpp index 7b48787c..c59b7e5d 100644 --- a/src/Core/ScriptExtra/CDoorExtra.cpp +++ b/src/Core/ScriptExtra/CDoorExtra.cpp @@ -9,31 +9,19 @@ CDoorExtra::CDoorExtra(CScriptObject *pInstance, CScene *pScene, CSceneNode *pPa { CPropertyStruct *pBaseStruct = pInstance->Properties(); - mpShieldModelProp = (TFileProperty*) pBaseStruct->PropertyByID(0xB20CC271); - if (mpShieldModelProp && (mpShieldModelProp->Type() != eFileProperty)) - mpShieldModelProp = nullptr; - - if (mpShieldModelProp) - PropertyModified(mpShieldModelProp); + mpShieldModelProp = TPropCast(pBaseStruct->PropertyByID(0xB20CC271)); + if (mpShieldModelProp) PropertyModified(mpShieldModelProp); if (mGame >= eEchoes) { - mpShieldColorProp = (TColorProperty*) pBaseStruct->PropertyByID(0x47B4E863); - if (mpShieldColorProp && (mpShieldColorProp->Type() != eColorProperty)) - mpShieldColorProp = nullptr; - - if (mpShieldColorProp) - PropertyModified(mpShieldColorProp); + mpShieldColorProp = TPropCast(pBaseStruct->PropertyByID(0x47B4E863)); + if (mpShieldColorProp) PropertyModified(mpShieldColorProp); } else { - mpDisabledProp = (TBoolProperty*) pBaseStruct->PropertyByID(0xDEE730F5); - if (mpDisabledProp && (mpDisabledProp->Type() != eBoolProperty)) - mpDisabledProp = nullptr; - - if (mpDisabledProp) - PropertyModified(mpDisabledProp); + mpDisabledProp = TPropCast(pBaseStruct->PropertyByID(0xDEE730F5)); + if (mpDisabledProp) PropertyModified(mpDisabledProp); } } diff --git a/src/Core/ScriptExtra/CPointOfInterestExtra.cpp b/src/Core/ScriptExtra/CPointOfInterestExtra.cpp index d01e8c83..7d77d2c0 100644 --- a/src/Core/ScriptExtra/CPointOfInterestExtra.cpp +++ b/src/Core/ScriptExtra/CPointOfInterestExtra.cpp @@ -11,32 +11,9 @@ CPointOfInterestExtra::CPointOfInterestExtra(CScriptObject *pInstance, CScene *p // Fetch scan data property CPropertyStruct *pBaseProp = pInstance->Properties(); - switch (mGame) - { - case ePrimeDemo: - case ePrime: - mpScanProperty = (TFileProperty*) pBaseProp->PropertyByIDString("0x04:0x00"); - break; - - case eEchoesDemo: - case eEchoes: - case eCorruptionProto: - case eCorruption: - mpScanProperty = (TFileProperty*) pBaseProp->PropertyByIDString("0xBDBEC295:0xB94E9BE7"); - break; - - default: - mpScanProperty = nullptr; - break; - } - - if (mpScanProperty) - { - if (mpScanProperty->Type() == eFileProperty) - PropertyModified(mpScanProperty); - else - mpScanProperty = nullptr; - } + if (mGame <= ePrime) mpScanProperty = TPropCast(pBaseProp->PropertyByIDString("0x04:0x00")); + else mpScanProperty = (TFileProperty*) pBaseProp->PropertyByIDString("0xBDBEC295:0xB94E9BE7"); + if (mpScanProperty) PropertyModified(mpScanProperty); } void CPointOfInterestExtra::PropertyModified(IProperty* pProperty) diff --git a/src/Core/ScriptExtra/CRadiusSphereExtra.cpp b/src/Core/ScriptExtra/CRadiusSphereExtra.cpp index fce173a8..3ce81c25 100644 --- a/src/Core/ScriptExtra/CRadiusSphereExtra.cpp +++ b/src/Core/ScriptExtra/CRadiusSphereExtra.cpp @@ -11,16 +11,16 @@ CRadiusSphereExtra::CRadiusSphereExtra(CScriptObject *pInstance, CScene *pScene, switch (mObjectType) { case 0x63: // Repulsor (MP1) - mpRadius = (TFloatProperty*) pInstance->Properties()->PropertyByID(0x3); + mpRadius = TPropCast(pInstance->Properties()->PropertyByID(0x3)); break; case 0x68: // RadialDamage (MP1) - mpRadius = (TFloatProperty*) pInstance->Properties()->PropertyByID(0x4); + mpRadius = TPropCast(pInstance->Properties()->PropertyByID(0x4)); break; case 0x5245504C: // "REPL" Repulsor (MP2/MP3) case 0x52414444: // "RADD" RadialDamage (MP2/MP3/DKCR) - mpRadius = (TFloatProperty*) pInstance->Properties()->PropertyByID(0x78C507EB); + mpRadius = TPropCast(pInstance->Properties()->PropertyByID(0x78C507EB)); break; } } diff --git a/src/Core/ScriptExtra/CSpacePirateExtra.cpp b/src/Core/ScriptExtra/CSpacePirateExtra.cpp index cf81c454..8958ac27 100644 --- a/src/Core/ScriptExtra/CSpacePirateExtra.cpp +++ b/src/Core/ScriptExtra/CSpacePirateExtra.cpp @@ -12,21 +12,10 @@ CSpacePirateExtra::CSpacePirateExtra(CScriptObject *pInstance, CScene *pScene, C if (pVulns && pVulns->Type() == eStructProperty) { - mpPowerVuln = (TLongProperty*) pVulns->PropertyByID(0x0); - if (mpPowerVuln && mpPowerVuln->Type() != eLongProperty && mpPowerVuln->Type() != eEnumProperty) - mpPowerVuln = nullptr; - - mpWaveVuln = (TLongProperty*) pVulns->PropertyByID(0x2); - if (mpWaveVuln && mpWaveVuln->Type() != eLongProperty && mpWaveVuln->Type() != eEnumProperty) - mpWaveVuln = nullptr; - - mpIceVuln = (TLongProperty*) pVulns->PropertyByID(0x1); - if (mpIceVuln && mpIceVuln->Type() != eLongProperty && mpIceVuln->Type() != eEnumProperty) - mpIceVuln = nullptr; - - mpPlasmaVuln = (TLongProperty*) pVulns->PropertyByID(0x3); - if (mpPlasmaVuln && mpPlasmaVuln->Type() != eLongProperty && mpPlasmaVuln->Type() != eEnumProperty) - mpPlasmaVuln = nullptr; + mpPowerVuln = TPropCast(pVulns->PropertyByID(0x0)); + mpWaveVuln = TPropCast(pVulns->PropertyByID(0x2)); + mpIceVuln = TPropCast(pVulns->PropertyByID(0x1)); + mpPlasmaVuln = TPropCast(pVulns->PropertyByID(0x3)); } } diff --git a/src/Core/ScriptExtra/CSpacePirateExtra.h b/src/Core/ScriptExtra/CSpacePirateExtra.h index 805f31f3..333a51c2 100644 --- a/src/Core/ScriptExtra/CSpacePirateExtra.h +++ b/src/Core/ScriptExtra/CSpacePirateExtra.h @@ -7,10 +7,10 @@ class CSpacePirateExtra : public CScriptExtra { // Render beam troopers with the correct color - TLongProperty *mpPowerVuln; - TLongProperty *mpWaveVuln; - TLongProperty *mpIceVuln; - TLongProperty *mpPlasmaVuln; + TEnumProperty *mpPowerVuln; + TEnumProperty *mpWaveVuln; + TEnumProperty *mpIceVuln; + TEnumProperty *mpPlasmaVuln; public: explicit CSpacePirateExtra(CScriptObject *pInstance, CScene *pScene, CSceneNode *pParent = 0); diff --git a/src/Editor/CStartWindow.cpp b/src/Editor/CStartWindow.cpp index 88fbaa63..1f9baae1 100644 --- a/src/Editor/CStartWindow.cpp +++ b/src/Editor/CStartWindow.cpp @@ -134,7 +134,7 @@ void CStartWindow::FillAreaUI() if (AttachedAreaSTRG) AttachedStr = TO_QSTRING(AttachedAreaSTRG->GetString("ENGL", 0)); else - AttachedStr = QString("!!") + TO_QSTRING(mpWorld->GetAreaInternalName(AttachedAreaIndex)); + AttachedStr = QString("!") + TO_QSTRING(mpWorld->GetAreaInternalName(AttachedAreaIndex)); ui->AttachedAreasList->addItem(AttachedStr); } @@ -168,7 +168,7 @@ void CStartWindow::on_LaunchWorldEditorButton_clicked() else { mpWorld->SetAreaLayerInfo(pArea, mSelectedAreaIndex); - mpWorldEditor->SetArea(mpWorld, pArea); + mpWorldEditor->SetArea(mpWorld, pArea, mSelectedAreaIndex); mpWorldEditor->setWindowModality(Qt::WindowModal); mpWorldEditor->showMaximized(); diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index 2652e005..5731c88f 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -136,7 +136,8 @@ HEADERS += \ WorldEditor/CInstancesModel.h \ Undo/CEditScriptPropertyCommand.h \ Undo/CResizeScriptArrayCommand.h \ - Undo/CBasicPropertyCommand.h + Undo/CBasicPropertyCommand.h \ + Undo/IUndoCommand.h # Source Files SOURCES += \ diff --git a/src/Editor/Undo/CBasicPropertyCommand.cpp b/src/Editor/Undo/CBasicPropertyCommand.cpp index 0b964b54..bcb5b483 100644 --- a/src/Editor/Undo/CBasicPropertyCommand.cpp +++ b/src/Editor/Undo/CBasicPropertyCommand.cpp @@ -2,7 +2,7 @@ #include CBasicPropertyCommand::CBasicPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex) - : QUndoCommand("Edit Property") + : IUndoCommand("Edit Property") , mpModel(pModel) , mpProperty(pModel->PropertyForIndex(rkIndex, true)) , mpTemplate(mpProperty->Template()) diff --git a/src/Editor/Undo/CBasicPropertyCommand.h b/src/Editor/Undo/CBasicPropertyCommand.h index 738e93d9..c0a186bf 100644 --- a/src/Editor/Undo/CBasicPropertyCommand.h +++ b/src/Editor/Undo/CBasicPropertyCommand.h @@ -1,10 +1,10 @@ #ifndef CBASICPROPERTYCOMMAND_H #define CBASICPROPERTYCOMMAND_H +#include "IUndoCommand.h" #include "Editor/PropertyEdit/CPropertyModel.h" -#include -class CBasicPropertyCommand : public QUndoCommand +class CBasicPropertyCommand : public IUndoCommand { protected: CPropertyModel *mpModel; @@ -18,6 +18,7 @@ protected: public: CBasicPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex); virtual void UpdateArraySubProperty(); + virtual bool AffectsCleanState() const { return true; } }; #endif // CBASICPROPERTYCOMMAND_H diff --git a/src/Editor/Undo/CClearSelectionCommand.cpp b/src/Editor/Undo/CClearSelectionCommand.cpp index cca11c31..794a43c8 100644 --- a/src/Editor/Undo/CClearSelectionCommand.cpp +++ b/src/Editor/Undo/CClearSelectionCommand.cpp @@ -2,7 +2,7 @@ #include "Editor/INodeEditor.h" CClearSelectionCommand::CClearSelectionCommand(INodeEditor *pEditor, QList& selection) - : QUndoCommand("Clear Selection"), + : IUndoCommand("Clear Selection"), mpEditor(pEditor), mSelectionState(selection), mpSelection(&selection) diff --git a/src/Editor/Undo/CClearSelectionCommand.h b/src/Editor/Undo/CClearSelectionCommand.h index d9520bb3..2bc8c642 100644 --- a/src/Editor/Undo/CClearSelectionCommand.h +++ b/src/Editor/Undo/CClearSelectionCommand.h @@ -1,12 +1,11 @@ #ifndef CCLEARSELECTIONCOMMAND_H #define CCLEARSELECTIONCOMMAND_H +#include "IUndoCommand.h" #include "Editor/INodeEditor.h" #include -#include - -class CClearSelectionCommand : public QUndoCommand +class CClearSelectionCommand : public IUndoCommand { INodeEditor *mpEditor; QList mSelectionState; @@ -16,6 +15,7 @@ public: ~CClearSelectionCommand(); void undo(); void redo(); + bool AffectsCleanState() const { return false; } }; #endif // CCLEARSELECTIONCOMMAND_H diff --git a/src/Editor/Undo/CDeselectNodeCommand.cpp b/src/Editor/Undo/CDeselectNodeCommand.cpp index 92641209..545184ba 100644 --- a/src/Editor/Undo/CDeselectNodeCommand.cpp +++ b/src/Editor/Undo/CDeselectNodeCommand.cpp @@ -2,7 +2,7 @@ #include "Editor/INodeEditor.h" CDeselectNodeCommand::CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection) - : QUndoCommand("Deselect"), + : IUndoCommand("Deselect"), mpEditor(pEditor), mpNode(pNode), mpSelection(&selection) diff --git a/src/Editor/Undo/CDeselectNodeCommand.h b/src/Editor/Undo/CDeselectNodeCommand.h index e0b286c3..f8035cc2 100644 --- a/src/Editor/Undo/CDeselectNodeCommand.h +++ b/src/Editor/Undo/CDeselectNodeCommand.h @@ -1,12 +1,11 @@ #ifndef CDESELECTNODECOMMAND_H #define CDESELECTNODECOMMAND_H +#include "IUndoCommand.h" #include "Editor/INodeEditor.h" #include -#include - -class CDeselectNodeCommand : public QUndoCommand +class CDeselectNodeCommand : public IUndoCommand { INodeEditor *mpEditor; CSceneNode *mpNode; @@ -15,6 +14,7 @@ public: CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection); void undo(); void redo(); + bool AffectsCleanState() const { return false; } }; #endif // CDESELECTNODECOMMAND_H diff --git a/src/Editor/Undo/CInvertSelectionCommand.cpp b/src/Editor/Undo/CInvertSelectionCommand.cpp index b0e3aedb..a85202ae 100644 --- a/src/Editor/Undo/CInvertSelectionCommand.cpp +++ b/src/Editor/Undo/CInvertSelectionCommand.cpp @@ -2,7 +2,7 @@ #include CInvertSelectionCommand::CInvertSelectionCommand(INodeEditor *pEditor, QList& rSelection, CScene *pScene, FNodeFlags NodeFlags) - : QUndoCommand("Invert Selection") + : IUndoCommand("Invert Selection") , mpEditor(pEditor) , mOldSelection(rSelection) , mpSelection(&rSelection) diff --git a/src/Editor/Undo/CInvertSelectionCommand.h b/src/Editor/Undo/CInvertSelectionCommand.h index 20d303c9..d010d7ee 100644 --- a/src/Editor/Undo/CInvertSelectionCommand.h +++ b/src/Editor/Undo/CInvertSelectionCommand.h @@ -1,12 +1,11 @@ #ifndef CINVERTSELECTIONCOMMAND_H #define CINVERTSELECTIONCOMMAND_H +#include "IUndoCommand.h" #include "Editor/INodeEditor.h" #include -#include - -class CInvertSelectionCommand : public QUndoCommand +class CInvertSelectionCommand : public IUndoCommand { INodeEditor *mpEditor; QList mOldSelection; @@ -18,6 +17,7 @@ public: ~CInvertSelectionCommand(); void undo(); void redo(); + virtual bool AffectsCleanState() const { return false; } }; #endif // CINVERTSELECTIONCOMMAND_H diff --git a/src/Editor/Undo/CRotateNodeCommand.cpp b/src/Editor/Undo/CRotateNodeCommand.cpp index 1060ece5..7da64cfb 100644 --- a/src/Editor/Undo/CRotateNodeCommand.cpp +++ b/src/Editor/Undo/CRotateNodeCommand.cpp @@ -3,14 +3,14 @@ #include "Editor/INodeEditor.h" CRotateNodeCommand::CRotateNodeCommand() - : QUndoCommand("Rotate"), + : IUndoCommand("Rotate"), mpEditor(nullptr), mCommandEnded(false) { } CRotateNodeCommand::CRotateNodeCommand(INodeEditor *pEditor, const QList& nodes, const CVector3f& /*pivot*/, const CQuaternion& delta, ETransformSpace transformSpace) - : QUndoCommand("Rotate"), + : IUndoCommand("Rotate"), mpEditor(pEditor), mCommandEnded(false) { diff --git a/src/Editor/Undo/CRotateNodeCommand.h b/src/Editor/Undo/CRotateNodeCommand.h index 688d5c18..e6e4cdf6 100644 --- a/src/Editor/Undo/CRotateNodeCommand.h +++ b/src/Editor/Undo/CRotateNodeCommand.h @@ -1,13 +1,12 @@ #ifndef CROTATENODECOMMAND_H #define CROTATENODECOMMAND_H +#include "IUndoCommand.h" #include "Editor/INodeEditor.h" #include - -#include #include -class CRotateNodeCommand : public QUndoCommand +class CRotateNodeCommand : public IUndoCommand { struct SNodeRotate { @@ -29,6 +28,7 @@ public: bool mergeWith(const QUndoCommand *other); void undo(); void redo(); + bool AffectsCleanState() const { return true; } static CRotateNodeCommand* End(); }; diff --git a/src/Editor/Undo/CScaleNodeCommand.cpp b/src/Editor/Undo/CScaleNodeCommand.cpp index 8b07bb20..23b86cfc 100644 --- a/src/Editor/Undo/CScaleNodeCommand.cpp +++ b/src/Editor/Undo/CScaleNodeCommand.cpp @@ -3,14 +3,14 @@ #include "Editor/INodeEditor.h" CScaleNodeCommand::CScaleNodeCommand() - : QUndoCommand("Scale"), + : IUndoCommand("Scale"), mpEditor(nullptr), mCommandEnded(false) { } CScaleNodeCommand::CScaleNodeCommand(INodeEditor *pEditor, const QList& nodes, const CVector3f& /*pivot*/, const CVector3f& delta) - : QUndoCommand("Scale"), + : IUndoCommand("Scale"), mpEditor(pEditor), mCommandEnded(false) { diff --git a/src/Editor/Undo/CScaleNodeCommand.h b/src/Editor/Undo/CScaleNodeCommand.h index 141db820..dae68c26 100644 --- a/src/Editor/Undo/CScaleNodeCommand.h +++ b/src/Editor/Undo/CScaleNodeCommand.h @@ -1,13 +1,12 @@ #ifndef CSCALENODECOMMAND_H #define CSCALENODECOMMAND_H +#include "IUndoCommand.h" #include "Editor/INodeEditor.h" #include - -#include #include -class CScaleNodeCommand : public QUndoCommand +class CScaleNodeCommand : public IUndoCommand { struct SNodeScale { @@ -29,6 +28,7 @@ public: bool mergeWith(const QUndoCommand *other); void undo(); void redo(); + bool AffectsCleanState() const { return true; } static CScaleNodeCommand* End(); }; diff --git a/src/Editor/Undo/CSelectAllCommand.cpp b/src/Editor/Undo/CSelectAllCommand.cpp index fa4d256a..a3e057c4 100644 --- a/src/Editor/Undo/CSelectAllCommand.cpp +++ b/src/Editor/Undo/CSelectAllCommand.cpp @@ -2,7 +2,7 @@ #include CSelectAllCommand::CSelectAllCommand(INodeEditor *pEditor, QList &rSelection, CScene *pScene, FNodeFlags NodeFlags) - : QUndoCommand("Select All") + : IUndoCommand("Select All") , mpEditor(pEditor) , mOldSelection(rSelection) , mpSelection(&rSelection) diff --git a/src/Editor/Undo/CSelectAllCommand.h b/src/Editor/Undo/CSelectAllCommand.h index 9127aa80..3f5844a8 100644 --- a/src/Editor/Undo/CSelectAllCommand.h +++ b/src/Editor/Undo/CSelectAllCommand.h @@ -1,12 +1,11 @@ #ifndef CSELECTALLCOMMAND_H #define CSELECTALLCOMMAND_H +#include "IUndoCommand.h" #include "Editor/INodeEditor.h" #include -#include - -class CSelectAllCommand : public QUndoCommand +class CSelectAllCommand : public IUndoCommand { INodeEditor *mpEditor; QList mOldSelection; @@ -18,6 +17,7 @@ public: ~CSelectAllCommand(); void undo(); void redo(); + bool AffectsCleanState() const { return false; } }; #endif // CSELECTALLCOMMAND_H diff --git a/src/Editor/Undo/CSelectNodeCommand.cpp b/src/Editor/Undo/CSelectNodeCommand.cpp index e6ad44c0..7813b2dc 100644 --- a/src/Editor/Undo/CSelectNodeCommand.cpp +++ b/src/Editor/Undo/CSelectNodeCommand.cpp @@ -2,7 +2,7 @@ #include "Editor/INodeEditor.h" CSelectNodeCommand::CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection) - : QUndoCommand("Select"), + : IUndoCommand("Select"), mpEditor(pEditor), mpNode(pNode), mpSelection(&selection) diff --git a/src/Editor/Undo/CSelectNodeCommand.h b/src/Editor/Undo/CSelectNodeCommand.h index f5cb87c3..eb27f20f 100644 --- a/src/Editor/Undo/CSelectNodeCommand.h +++ b/src/Editor/Undo/CSelectNodeCommand.h @@ -1,12 +1,11 @@ #ifndef CSELECTNODECOMMAND_H #define CSELECTNODECOMMAND_H +#include "IUndoCommand.h" #include "Editor/INodeEditor.h" #include -#include - -class CSelectNodeCommand : public QUndoCommand +class CSelectNodeCommand : public IUndoCommand { INodeEditor *mpEditor; CSceneNode *mpNode; @@ -15,6 +14,7 @@ public: CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection); void undo(); void redo(); + bool AffectsCleanState() const { return false; } }; #endif // CSELECTNODECOMMAND_H diff --git a/src/Editor/Undo/CTranslateNodeCommand.cpp b/src/Editor/Undo/CTranslateNodeCommand.cpp index 3c5eed36..734a24dd 100644 --- a/src/Editor/Undo/CTranslateNodeCommand.cpp +++ b/src/Editor/Undo/CTranslateNodeCommand.cpp @@ -3,14 +3,14 @@ #include "Editor/INodeEditor.h" CTranslateNodeCommand::CTranslateNodeCommand() - : QUndoCommand("Translate"), + : IUndoCommand("Translate"), mpEditor(nullptr), mCommandEnded(false) { } CTranslateNodeCommand::CTranslateNodeCommand(INodeEditor *pEditor, const QList& nodes, const CVector3f& delta, ETransformSpace transformSpace) - : QUndoCommand("Translate"), + : IUndoCommand("Translate"), mpEditor(pEditor), mCommandEnded(false) { diff --git a/src/Editor/Undo/CTranslateNodeCommand.h b/src/Editor/Undo/CTranslateNodeCommand.h index 6fb86af8..1cc7c00d 100644 --- a/src/Editor/Undo/CTranslateNodeCommand.h +++ b/src/Editor/Undo/CTranslateNodeCommand.h @@ -1,13 +1,12 @@ #ifndef CTRANSLATENODECOMMAND_H #define CTRANSLATENODECOMMAND_H +#include "IUndoCommand.h" #include #include "Editor/INodeEditor.h" - -#include #include -class CTranslateNodeCommand : public QUndoCommand +class CTranslateNodeCommand : public IUndoCommand { struct SNodeTranslate { @@ -27,6 +26,7 @@ public: bool mergeWith(const QUndoCommand *other); void undo(); void redo(); + bool AffectsCleanState() const { return true; } static CTranslateNodeCommand* End(); }; diff --git a/src/Editor/Undo/IUndoCommand.h b/src/Editor/Undo/IUndoCommand.h new file mode 100644 index 00000000..5458c93f --- /dev/null +++ b/src/Editor/Undo/IUndoCommand.h @@ -0,0 +1,19 @@ +#ifndef IUNDOCOMMAND +#define IUNDOCOMMAND + +#include + +class IUndoCommand : public QUndoCommand +{ +public: + IUndoCommand(QUndoCommand *pParent = 0) + : QUndoCommand(pParent) {} + + IUndoCommand(const QString& rkText, QUndoCommand *pParent = 0) + : QUndoCommand(rkText, pParent) {} + + virtual bool AffectsCleanState() const = 0; +}; + +#endif // IUNDOCOMMAND + diff --git a/src/Editor/WorldEditor/CWorldEditor.cpp b/src/Editor/WorldEditor/CWorldEditor.cpp index b9335e64..211e1602 100644 --- a/src/Editor/WorldEditor/CWorldEditor.cpp +++ b/src/Editor/WorldEditor/CWorldEditor.cpp @@ -77,6 +77,9 @@ CWorldEditor::CWorldEditor(QWidget *parent) : connect(ui->TransformSpinBox, SIGNAL(ValueChanged(CVector3f)), this, SLOT(OnTransformSpinBoxModified(CVector3f))); connect(ui->TransformSpinBox, SIGNAL(EditingDone(CVector3f)), this, SLOT(OnTransformSpinBoxEdited(CVector3f))); connect(ui->CamSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnCameraSpeedChange(double))); + connect(&mUndoStack, SIGNAL(indexChanged(int)), this, SLOT(OnUndoStackIndexChanged())); + + connect(ui->ActionSave, SIGNAL(triggered()), this, SLOT(Save())); } CWorldEditor::~CWorldEditor() @@ -84,10 +87,33 @@ CWorldEditor::~CWorldEditor() delete ui; } -void CWorldEditor::closeEvent(QCloseEvent *) +void CWorldEditor::closeEvent(QCloseEvent *pEvent) { - if (mpPoiDialog) - mpPoiDialog->close(); + bool ShouldClose = true; + + if (isWindowModified()) + { + int Result = QMessageBox::warning(this, "Save", "You have unsaved changes. Save?", QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel); + + if (Result == QMessageBox::Yes) + ShouldClose = Save(); + + else if (Result == QMessageBox::No) + ShouldClose = true; + + else if (Result == QMessageBox::Cancel) + ShouldClose = false; + } + + if (ShouldClose) + { + if (mpPoiDialog) + mpPoiDialog->close(); + } + else + { + pEvent->ignore(); + } } bool CWorldEditor::eventFilter(QObject * /*pObj*/, QEvent * /*pEvent*/) @@ -95,7 +121,7 @@ bool CWorldEditor::eventFilter(QObject * /*pObj*/, QEvent * /*pEvent*/) return false; } -void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea) +void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex) { ExitPickMode(); ui->MainViewport->ResetHover(); @@ -153,6 +179,18 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea) // Set up sidebar tabs CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpArea->Version()); ui->InstancesTabContents->SetMaster(pMaster); + + // Set window title + CStringTable *pWorldNameTable = mpWorld->GetWorldName(); + TWideString WorldName = pWorldNameTable ? pWorldNameTable->GetString("ENGL", 0) : "[Untitled World]"; + + CStringTable *pAreaNameTable = mpWorld->GetAreaName(AreaIndex); + TWideString AreaName = pAreaNameTable ? pAreaNameTable->GetString("ENGL", 0) : (TWideString("!") + mpWorld->GetAreaInternalName(AreaIndex).ToUTF16()); + + if (AreaName.IsEmpty()) + AreaName = "[Untitled Area]"; + + setWindowTitle(QString("Prime World Editor - %1 - %2[*]").arg(TO_QSTRING(WorldName)).arg(TO_QSTRING(AreaName))); } CGameArea* CWorldEditor::ActiveArea() @@ -160,7 +198,26 @@ CGameArea* CWorldEditor::ActiveArea() return mpArea; } -// ************ PROTECTED SLOTS ************ +// ************ PUBLIC SLOTS ************ +bool CWorldEditor::Save() +{ + TString Out = mpArea->FullSource(); + CFileOutStream MREA(Out.ToStdString(), IOUtil::eBigEndian); + + if (MREA.IsValid()) + { + CAreaCooker::WriteCookedArea(mpArea, MREA); + mUndoStack.setClean(); + setWindowModified(false); + return true; + } + else + { + QMessageBox::warning(this, "Error", "Unable to save error; couldn't open output file " + TO_QSTRING(Out)); + return false; + } +} + void CWorldEditor::UpdateStatusBar() { // Would be cool to do more frequent status bar updates with more info. Unfortunately, this causes lag. @@ -281,6 +338,55 @@ void CWorldEditor::GizmoModeChanged(CGizmo::EGizmoMode mode) } // ************ PRIVATE SLOTS ************ +void CWorldEditor::OnUndoStackIndexChanged() +{ + // Check the commands that have been executed on the undo stack and find out whether any of them affect the clean state. + // This is to prevent commands like select/deselect from altering the clean state. + int CurrentIndex = mUndoStack.index(); + int CleanIndex = mUndoStack.cleanIndex(); + + if (CurrentIndex == CleanIndex) + setWindowModified(false); + + else + { + bool IsClean = true; + int LowIndex = (CurrentIndex > CleanIndex ? CleanIndex + 1 : CurrentIndex); + int HighIndex = (CurrentIndex > CleanIndex ? CurrentIndex - 1 : CleanIndex); + + for (int iIdx = LowIndex; iIdx <= HighIndex; iIdx++) + { + const QUndoCommand *pkQCmd = mUndoStack.command(iIdx); + + if (pkQCmd->childCount() > 0) + { + for (int iChild = 0; iChild < pkQCmd->childCount(); iChild++) + { + const IUndoCommand *pkCmd = static_cast(pkQCmd->child(iChild)); + + if (pkCmd->AffectsCleanState()) + { + IsClean = false; + break; + } + } + } + + else + { + const IUndoCommand *pkCmd = static_cast(pkQCmd); + + if (pkCmd->AffectsCleanState()) + IsClean = false; + } + + if (!IsClean) break; + } + + setWindowModified(!IsClean); + } +} + void CWorldEditor::OnPickModeEnter(QCursor Cursor) { ui->MainViewport->SetCursorState(Cursor); @@ -530,11 +636,3 @@ void CWorldEditor::on_ActionEditPoiToWorldMap_triggered() mpPoiDialog->show(); } } - -void CWorldEditor::on_ActionSave_triggered() -{ - TString Out = mpArea->FullSource(); - CFileOutStream MREA(Out.ToStdString(), IOUtil::eBigEndian); - CAreaCooker::WriteCookedArea(mpArea, MREA); - QMessageBox::information(this, "Success", "Successfully saved area to " + TO_QSTRING(Out)); -} diff --git a/src/Editor/WorldEditor/CWorldEditor.h b/src/Editor/WorldEditor/CWorldEditor.h index 598f8d99..d5edf4a5 100644 --- a/src/Editor/WorldEditor/CWorldEditor.h +++ b/src/Editor/WorldEditor/CWorldEditor.h @@ -40,12 +40,14 @@ class CWorldEditor : public INodeEditor public: explicit CWorldEditor(QWidget *parent = 0); ~CWorldEditor(); - void closeEvent(QCloseEvent *); + void closeEvent(QCloseEvent *pEvent); bool eventFilter(QObject *pObj, QEvent *pEvent); - void SetArea(CWorld *pWorld, CGameArea *pArea); + void SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex); CGameArea* ActiveArea(); public slots: + bool Save(); + void UpdateStatusBar(); void UpdateGizmoUI(); void UpdateSelectionUI(); @@ -55,6 +57,7 @@ protected: void GizmoModeChanged(CGizmo::EGizmoMode mode); private slots: + void OnUndoStackIndexChanged(); void OnPickModeEnter(QCursor Cursor); void OnPickModeExit(); void RefreshViewport(); @@ -85,7 +88,6 @@ private slots: void on_ActionSelectAll_triggered(); void on_ActionInvertSelection_triggered(); void on_ActionEditPoiToWorldMap_triggered(); - void on_ActionSave_triggered(); }; #endif // CWORLDEDITOR_H diff --git a/templates/mp1/Script/Water.xml b/templates/mp1/Script/Water.xml index ee1b10e1..f8fab3c1 100644 --- a/templates/mp1/Script/Water.xml +++ b/templates/mp1/Script/Water.xml @@ -9,7 +9,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -55,7 +55,9 @@ - + + This particle plays when an actor/projectile enters the water. It also plays when the morph ball is rolling in it at surface level. + @@ -73,10 +75,10 @@ - - - - + + + + @@ -91,6 +93,7 @@ + disabled