Added TPropCast property casting function, added support for clean/dirty state in the world editor + check for unsaved changes when the window is closed

This commit is contained in:
parax0 2016-02-01 16:42:12 -07:00
parent 6669dd4367
commit 1a07a9c083
34 changed files with 242 additions and 163 deletions

View File

@ -18,7 +18,7 @@ class CLightParameters
EGame mGame; EGame mGame;
TLongProperty *mpLightLayer; TLongProperty *mpLightLayer;
TLongProperty *mpWorldLightingOptions; TEnumProperty *mpWorldLightingOptions;
public: public:
CLightParameters(CPropertyStruct *pStruct, EGame Game) CLightParameters(CPropertyStruct *pStruct, EGame Game)
@ -31,13 +31,13 @@ public:
{ {
if (mGame <= ePrime) if (mGame <= ePrime)
{ {
mpWorldLightingOptions = (TLongProperty*) mpStruct->PropertyByIndex(0x7); mpWorldLightingOptions = TPropCast<TEnumProperty>(mpStruct->PropertyByIndex(0x7));
mpLightLayer = (TLongProperty*) mpStruct->PropertyByIndex(0xD); mpLightLayer = TPropCast<TLongProperty>(mpStruct->PropertyByIndex(0xD));
} }
else else
{ {
mpWorldLightingOptions = (TLongProperty*) mpStruct->PropertyByIndex(0x6B5E7509); mpWorldLightingOptions = TPropCast<TEnumProperty>(mpStruct->PropertyByID(0x6B5E7509));
mpLightLayer = (TLongProperty*) mpStruct->PropertyByID(0x1F715FD3); mpLightLayer = TPropCast<TLongProperty>(mpStruct->PropertyByID(0x1F715FD3));
} }
} }
} }

View File

@ -71,6 +71,8 @@ public:
~TTypedProperty() {} ~TTypedProperty() {}
virtual EPropertyType Type() const { return TypeEnum; } virtual EPropertyType Type() const { return TypeEnum; }
static inline EPropertyType StaticType() { return TypeEnum; }
virtual TString ToString() const { return mValue.ToString(); } virtual TString ToString() const { return mValue.ToString(); }
virtual IPropertyValue* RawValue() { return &mValue; } virtual IPropertyValue* RawValue() { return &mValue; }
@ -124,6 +126,7 @@ public:
} }
EPropertyType Type() const { return eStructProperty; } EPropertyType Type() const { return eStructProperty; }
static inline EPropertyType StaticType() { return eStructProperty; }
virtual void Copy(const IProperty *pkProp); virtual void Copy(const IProperty *pkProp);
@ -161,6 +164,7 @@ public:
: CPropertyStruct(pTemp, pParent) {} : CPropertyStruct(pTemp, pParent) {}
EPropertyType Type() const { return eArrayProperty; } EPropertyType Type() const { return eArrayProperty; }
static inline EPropertyType StaticType() { return eArrayProperty; }
virtual IProperty* Clone(CPropertyStruct *pParent) const virtual IProperty* Clone(CPropertyStruct *pParent) const
{ {
@ -179,5 +183,14 @@ public:
TString ElementName() const; TString ElementName() const;
}; };
/*
* Function for casting properties. Returns null if the property is not actually the requested type.
*/
template <class PropertyClass>
PropertyClass* TPropCast(IProperty *pProp)
{
return (pProp && pProp->Type() == PropertyClass::StaticType() ? static_cast<PropertyClass*>(pProp) : nullptr);
}
#endif // IPROPERTY #endif // IPROPERTY

View File

@ -76,9 +76,6 @@ ENodeType CScriptNode::NodeType()
void CScriptNode::OnTransformed() void CScriptNode::OnTransformed()
{ {
if (mpExtra)
mpExtra->OnTransformed();
if (mpInstance) if (mpInstance)
{ {
CScriptTemplate *pTemplate = Template(); CScriptTemplate *pTemplate = Template();
@ -92,6 +89,9 @@ void CScriptNode::OnTransformed()
if (pTemplate->HasScale() && LocalScale() != mpInstance->Scale()) if (pTemplate->HasScale() && LocalScale() != mpInstance->Scale())
mpInstance->SetScale(LocalScale()); mpInstance->SetScale(LocalScale());
} }
if (mpExtra)
mpExtra->OnTransformed();
} }
void CScriptNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo) void CScriptNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
@ -167,7 +167,6 @@ void CScriptNode::Draw(FRenderOptions Options, int ComponentIndex, const SViewIn
else else
{ {
LoadLights(ViewInfo); LoadLights(ViewInfo);
CGraphics::UpdateLightBlock();
} }
LoadModelMatrix(); LoadModelMatrix();

View File

@ -16,36 +16,18 @@ CDamageableTriggerExtra::CDamageableTriggerExtra(CScriptObject *pInstance, CScen
CPropertyStruct *pBaseStruct = pInstance->Properties(); CPropertyStruct *pBaseStruct = pInstance->Properties();
// Fetch render side // Fetch render side
mpRenderSideProp = (TEnumProperty*) pBaseStruct->PropertyByIndex(0x5); mpRenderSideProp = TPropCast<TEnumProperty>(pBaseStruct->PropertyByIndex(0x5));
if (mpRenderSideProp && mpRenderSideProp->Type() != eEnumProperty)
mpRenderSideProp = nullptr;
if (mpRenderSideProp) PropertyModified(mpRenderSideProp); if (mpRenderSideProp) PropertyModified(mpRenderSideProp);
// Fetch scale // Fetch scale
mpSizeProp = (TVector3Property*) pBaseStruct->PropertyByIndex(0x2); mpSizeProp = TPropCast<TVector3Property>(pBaseStruct->PropertyByIndex(0x2));
if (mpSizeProp) PropertyModified(mpSizeProp);
if (mpSizeProp && mpSizeProp->Type() != eVector3Property)
mpSizeProp = nullptr;
if (mpSizeProp) PropertyModified (mpSizeProp);
// Fetch textures // Fetch textures
for (u32 iTex = 0; iTex < 3; iTex++) for (u32 iTex = 0; iTex < 3; iTex++)
{ {
mpTextureProps[iTex] = (TFileProperty*) pBaseStruct->PropertyByIndex(0x6 + iTex); mpTextureProps[iTex] = TPropCast<TFileProperty>(pBaseStruct->PropertyByIndex(0x6 + iTex));
if (mpTextureProps[iTex]) PropertyModified(mpTextureProps[iTex]);
if (mpTextureProps[iTex])
{
if (mpTextureProps[iTex]->Type() == eFileProperty)
PropertyModified(mpTextureProps[iTex]);
else
mpTextureProps[iTex] = nullptr;
}
else
mpTextures[iTex] = nullptr;
} }
} }
@ -147,6 +129,12 @@ void CDamageableTriggerExtra::UpdatePlaneTransform()
MarkTransformChanged(); MarkTransformChanged();
} }
void CDamageableTriggerExtra::OnTransformed()
{
mPlaneSize = mpSizeProp->Get();
UpdatePlaneTransform();
}
void CDamageableTriggerExtra::PropertyModified(IProperty *pProperty) void CDamageableTriggerExtra::PropertyModified(IProperty *pProperty)
{ {
if (pProperty == mpRenderSideProp) 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 // 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 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 TexBR(mCoordScale.x / 5.f, 0.f);
CVector2f TexBL(0.f, 0.f); CVector2f TexBL(0.f, 0.f);
CDrawUtil::DrawSquare(TexUL, TexUR, TexBR, TexBL); CDrawUtil::DrawSquare(TexUL, TexUR, TexBR, TexBL);

View File

@ -35,6 +35,7 @@ public:
~CDamageableTriggerExtra(); ~CDamageableTriggerExtra();
void CreateMaterial(); void CreateMaterial();
void UpdatePlaneTransform(); void UpdatePlaneTransform();
void OnTransformed();
void PropertyModified(IProperty *pProperty); void PropertyModified(IProperty *pProperty);
bool ShouldDrawNormalAssets(); bool ShouldDrawNormalAssets();
void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo); void AddToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo);

View File

@ -9,31 +9,19 @@ CDoorExtra::CDoorExtra(CScriptObject *pInstance, CScene *pScene, CSceneNode *pPa
{ {
CPropertyStruct *pBaseStruct = pInstance->Properties(); CPropertyStruct *pBaseStruct = pInstance->Properties();
mpShieldModelProp = (TFileProperty*) pBaseStruct->PropertyByID(0xB20CC271); mpShieldModelProp = TPropCast<TFileProperty>(pBaseStruct->PropertyByID(0xB20CC271));
if (mpShieldModelProp && (mpShieldModelProp->Type() != eFileProperty)) if (mpShieldModelProp) PropertyModified(mpShieldModelProp);
mpShieldModelProp = nullptr;
if (mpShieldModelProp)
PropertyModified(mpShieldModelProp);
if (mGame >= eEchoes) if (mGame >= eEchoes)
{ {
mpShieldColorProp = (TColorProperty*) pBaseStruct->PropertyByID(0x47B4E863); mpShieldColorProp = TPropCast<TColorProperty>(pBaseStruct->PropertyByID(0x47B4E863));
if (mpShieldColorProp && (mpShieldColorProp->Type() != eColorProperty)) if (mpShieldColorProp) PropertyModified(mpShieldColorProp);
mpShieldColorProp = nullptr;
if (mpShieldColorProp)
PropertyModified(mpShieldColorProp);
} }
else else
{ {
mpDisabledProp = (TBoolProperty*) pBaseStruct->PropertyByID(0xDEE730F5); mpDisabledProp = TPropCast<TBoolProperty>(pBaseStruct->PropertyByID(0xDEE730F5));
if (mpDisabledProp && (mpDisabledProp->Type() != eBoolProperty)) if (mpDisabledProp) PropertyModified(mpDisabledProp);
mpDisabledProp = nullptr;
if (mpDisabledProp)
PropertyModified(mpDisabledProp);
} }
} }

View File

@ -11,32 +11,9 @@ CPointOfInterestExtra::CPointOfInterestExtra(CScriptObject *pInstance, CScene *p
// Fetch scan data property // Fetch scan data property
CPropertyStruct *pBaseProp = pInstance->Properties(); CPropertyStruct *pBaseProp = pInstance->Properties();
switch (mGame) if (mGame <= ePrime) mpScanProperty = TPropCast<TFileProperty>(pBaseProp->PropertyByIDString("0x04:0x00"));
{ else mpScanProperty = (TFileProperty*) pBaseProp->PropertyByIDString("0xBDBEC295:0xB94E9BE7");
case ePrimeDemo: if (mpScanProperty) PropertyModified(mpScanProperty);
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;
}
} }
void CPointOfInterestExtra::PropertyModified(IProperty* pProperty) void CPointOfInterestExtra::PropertyModified(IProperty* pProperty)

View File

@ -11,16 +11,16 @@ CRadiusSphereExtra::CRadiusSphereExtra(CScriptObject *pInstance, CScene *pScene,
switch (mObjectType) switch (mObjectType)
{ {
case 0x63: // Repulsor (MP1) case 0x63: // Repulsor (MP1)
mpRadius = (TFloatProperty*) pInstance->Properties()->PropertyByID(0x3); mpRadius = TPropCast<TFloatProperty>(pInstance->Properties()->PropertyByID(0x3));
break; break;
case 0x68: // RadialDamage (MP1) case 0x68: // RadialDamage (MP1)
mpRadius = (TFloatProperty*) pInstance->Properties()->PropertyByID(0x4); mpRadius = TPropCast<TFloatProperty>(pInstance->Properties()->PropertyByID(0x4));
break; break;
case 0x5245504C: // "REPL" Repulsor (MP2/MP3) case 0x5245504C: // "REPL" Repulsor (MP2/MP3)
case 0x52414444: // "RADD" RadialDamage (MP2/MP3/DKCR) case 0x52414444: // "RADD" RadialDamage (MP2/MP3/DKCR)
mpRadius = (TFloatProperty*) pInstance->Properties()->PropertyByID(0x78C507EB); mpRadius = TPropCast<TFloatProperty>(pInstance->Properties()->PropertyByID(0x78C507EB));
break; break;
} }
} }

View File

@ -12,21 +12,10 @@ CSpacePirateExtra::CSpacePirateExtra(CScriptObject *pInstance, CScene *pScene, C
if (pVulns && pVulns->Type() == eStructProperty) if (pVulns && pVulns->Type() == eStructProperty)
{ {
mpPowerVuln = (TLongProperty*) pVulns->PropertyByID(0x0); mpPowerVuln = TPropCast<TEnumProperty>(pVulns->PropertyByID(0x0));
if (mpPowerVuln && mpPowerVuln->Type() != eLongProperty && mpPowerVuln->Type() != eEnumProperty) mpWaveVuln = TPropCast<TEnumProperty>(pVulns->PropertyByID(0x2));
mpPowerVuln = nullptr; mpIceVuln = TPropCast<TEnumProperty>(pVulns->PropertyByID(0x1));
mpPlasmaVuln = TPropCast<TEnumProperty>(pVulns->PropertyByID(0x3));
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;
} }
} }

View File

@ -7,10 +7,10 @@
class CSpacePirateExtra : public CScriptExtra class CSpacePirateExtra : public CScriptExtra
{ {
// Render beam troopers with the correct color // Render beam troopers with the correct color
TLongProperty *mpPowerVuln; TEnumProperty *mpPowerVuln;
TLongProperty *mpWaveVuln; TEnumProperty *mpWaveVuln;
TLongProperty *mpIceVuln; TEnumProperty *mpIceVuln;
TLongProperty *mpPlasmaVuln; TEnumProperty *mpPlasmaVuln;
public: public:
explicit CSpacePirateExtra(CScriptObject *pInstance, CScene *pScene, CSceneNode *pParent = 0); explicit CSpacePirateExtra(CScriptObject *pInstance, CScene *pScene, CSceneNode *pParent = 0);

View File

@ -134,7 +134,7 @@ void CStartWindow::FillAreaUI()
if (AttachedAreaSTRG) if (AttachedAreaSTRG)
AttachedStr = TO_QSTRING(AttachedAreaSTRG->GetString("ENGL", 0)); AttachedStr = TO_QSTRING(AttachedAreaSTRG->GetString("ENGL", 0));
else else
AttachedStr = QString("!!") + TO_QSTRING(mpWorld->GetAreaInternalName(AttachedAreaIndex)); AttachedStr = QString("!") + TO_QSTRING(mpWorld->GetAreaInternalName(AttachedAreaIndex));
ui->AttachedAreasList->addItem(AttachedStr); ui->AttachedAreasList->addItem(AttachedStr);
} }
@ -168,7 +168,7 @@ void CStartWindow::on_LaunchWorldEditorButton_clicked()
else else
{ {
mpWorld->SetAreaLayerInfo(pArea, mSelectedAreaIndex); mpWorld->SetAreaLayerInfo(pArea, mSelectedAreaIndex);
mpWorldEditor->SetArea(mpWorld, pArea); mpWorldEditor->SetArea(mpWorld, pArea, mSelectedAreaIndex);
mpWorldEditor->setWindowModality(Qt::WindowModal); mpWorldEditor->setWindowModality(Qt::WindowModal);
mpWorldEditor->showMaximized(); mpWorldEditor->showMaximized();

View File

@ -136,7 +136,8 @@ HEADERS += \
WorldEditor/CInstancesModel.h \ WorldEditor/CInstancesModel.h \
Undo/CEditScriptPropertyCommand.h \ Undo/CEditScriptPropertyCommand.h \
Undo/CResizeScriptArrayCommand.h \ Undo/CResizeScriptArrayCommand.h \
Undo/CBasicPropertyCommand.h Undo/CBasicPropertyCommand.h \
Undo/IUndoCommand.h
# Source Files # Source Files
SOURCES += \ SOURCES += \

View File

@ -2,7 +2,7 @@
#include <Core/Resource/Script/IPropertyTemplate.h> #include <Core/Resource/Script/IPropertyTemplate.h>
CBasicPropertyCommand::CBasicPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex) CBasicPropertyCommand::CBasicPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex)
: QUndoCommand("Edit Property") : IUndoCommand("Edit Property")
, mpModel(pModel) , mpModel(pModel)
, mpProperty(pModel->PropertyForIndex(rkIndex, true)) , mpProperty(pModel->PropertyForIndex(rkIndex, true))
, mpTemplate(mpProperty->Template()) , mpTemplate(mpProperty->Template())

View File

@ -1,10 +1,10 @@
#ifndef CBASICPROPERTYCOMMAND_H #ifndef CBASICPROPERTYCOMMAND_H
#define CBASICPROPERTYCOMMAND_H #define CBASICPROPERTYCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/PropertyEdit/CPropertyModel.h" #include "Editor/PropertyEdit/CPropertyModel.h"
#include <QUndoCommand>
class CBasicPropertyCommand : public QUndoCommand class CBasicPropertyCommand : public IUndoCommand
{ {
protected: protected:
CPropertyModel *mpModel; CPropertyModel *mpModel;
@ -18,6 +18,7 @@ protected:
public: public:
CBasicPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex); CBasicPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex);
virtual void UpdateArraySubProperty(); virtual void UpdateArraySubProperty();
virtual bool AffectsCleanState() const { return true; }
}; };
#endif // CBASICPROPERTYCOMMAND_H #endif // CBASICPROPERTYCOMMAND_H

View File

@ -2,7 +2,7 @@
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
CClearSelectionCommand::CClearSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& selection) CClearSelectionCommand::CClearSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& selection)
: QUndoCommand("Clear Selection"), : IUndoCommand("Clear Selection"),
mpEditor(pEditor), mpEditor(pEditor),
mSelectionState(selection), mSelectionState(selection),
mpSelection(&selection) mpSelection(&selection)

View File

@ -1,12 +1,11 @@
#ifndef CCLEARSELECTIONCOMMAND_H #ifndef CCLEARSELECTIONCOMMAND_H
#define CCLEARSELECTIONCOMMAND_H #define CCLEARSELECTIONCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include <QUndoCommand> class CClearSelectionCommand : public IUndoCommand
class CClearSelectionCommand : public QUndoCommand
{ {
INodeEditor *mpEditor; INodeEditor *mpEditor;
QList<CSceneNode*> mSelectionState; QList<CSceneNode*> mSelectionState;
@ -16,6 +15,7 @@ public:
~CClearSelectionCommand(); ~CClearSelectionCommand();
void undo(); void undo();
void redo(); void redo();
bool AffectsCleanState() const { return false; }
}; };
#endif // CCLEARSELECTIONCOMMAND_H #endif // CCLEARSELECTIONCOMMAND_H

View File

@ -2,7 +2,7 @@
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
CDeselectNodeCommand::CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection) CDeselectNodeCommand::CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection)
: QUndoCommand("Deselect"), : IUndoCommand("Deselect"),
mpEditor(pEditor), mpEditor(pEditor),
mpNode(pNode), mpNode(pNode),
mpSelection(&selection) mpSelection(&selection)

View File

@ -1,12 +1,11 @@
#ifndef CDESELECTNODECOMMAND_H #ifndef CDESELECTNODECOMMAND_H
#define CDESELECTNODECOMMAND_H #define CDESELECTNODECOMMAND_H
#include "IUndoCommand.h"
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include <QUndoCommand> class CDeselectNodeCommand : public IUndoCommand
class CDeselectNodeCommand : public QUndoCommand
{ {
INodeEditor *mpEditor; INodeEditor *mpEditor;
CSceneNode *mpNode; CSceneNode *mpNode;
@ -15,6 +14,7 @@ public:
CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection); CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection);
void undo(); void undo();
void redo(); void redo();
bool AffectsCleanState() const { return false; }
}; };
#endif // CDESELECTNODECOMMAND_H #endif // CDESELECTNODECOMMAND_H

View File

@ -2,7 +2,7 @@
#include <Core/Scene/CSceneIterator.h> #include <Core/Scene/CSceneIterator.h>
CInvertSelectionCommand::CInvertSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& rSelection, CScene *pScene, FNodeFlags NodeFlags) CInvertSelectionCommand::CInvertSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& rSelection, CScene *pScene, FNodeFlags NodeFlags)
: QUndoCommand("Invert Selection") : IUndoCommand("Invert Selection")
, mpEditor(pEditor) , mpEditor(pEditor)
, mOldSelection(rSelection) , mOldSelection(rSelection)
, mpSelection(&rSelection) , mpSelection(&rSelection)

View File

@ -1,12 +1,11 @@
#ifndef CINVERTSELECTIONCOMMAND_H #ifndef CINVERTSELECTIONCOMMAND_H
#define CINVERTSELECTIONCOMMAND_H #define CINVERTSELECTIONCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include <QUndoCommand> class CInvertSelectionCommand : public IUndoCommand
class CInvertSelectionCommand : public QUndoCommand
{ {
INodeEditor *mpEditor; INodeEditor *mpEditor;
QList<CSceneNode*> mOldSelection; QList<CSceneNode*> mOldSelection;
@ -18,6 +17,7 @@ public:
~CInvertSelectionCommand(); ~CInvertSelectionCommand();
void undo(); void undo();
void redo(); void redo();
virtual bool AffectsCleanState() const { return false; }
}; };
#endif // CINVERTSELECTIONCOMMAND_H #endif // CINVERTSELECTIONCOMMAND_H

View File

@ -3,14 +3,14 @@
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
CRotateNodeCommand::CRotateNodeCommand() CRotateNodeCommand::CRotateNodeCommand()
: QUndoCommand("Rotate"), : IUndoCommand("Rotate"),
mpEditor(nullptr), mpEditor(nullptr),
mCommandEnded(false) mCommandEnded(false)
{ {
} }
CRotateNodeCommand::CRotateNodeCommand(INodeEditor *pEditor, const QList<CSceneNode*>& nodes, const CVector3f& /*pivot*/, const CQuaternion& delta, ETransformSpace transformSpace) CRotateNodeCommand::CRotateNodeCommand(INodeEditor *pEditor, const QList<CSceneNode*>& nodes, const CVector3f& /*pivot*/, const CQuaternion& delta, ETransformSpace transformSpace)
: QUndoCommand("Rotate"), : IUndoCommand("Rotate"),
mpEditor(pEditor), mpEditor(pEditor),
mCommandEnded(false) mCommandEnded(false)
{ {

View File

@ -1,13 +1,12 @@
#ifndef CROTATENODECOMMAND_H #ifndef CROTATENODECOMMAND_H
#define CROTATENODECOMMAND_H #define CROTATENODECOMMAND_H
#include "IUndoCommand.h"
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include <QUndoCommand>
#include <QList> #include <QList>
class CRotateNodeCommand : public QUndoCommand class CRotateNodeCommand : public IUndoCommand
{ {
struct SNodeRotate struct SNodeRotate
{ {
@ -29,6 +28,7 @@ public:
bool mergeWith(const QUndoCommand *other); bool mergeWith(const QUndoCommand *other);
void undo(); void undo();
void redo(); void redo();
bool AffectsCleanState() const { return true; }
static CRotateNodeCommand* End(); static CRotateNodeCommand* End();
}; };

View File

@ -3,14 +3,14 @@
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
CScaleNodeCommand::CScaleNodeCommand() CScaleNodeCommand::CScaleNodeCommand()
: QUndoCommand("Scale"), : IUndoCommand("Scale"),
mpEditor(nullptr), mpEditor(nullptr),
mCommandEnded(false) mCommandEnded(false)
{ {
} }
CScaleNodeCommand::CScaleNodeCommand(INodeEditor *pEditor, const QList<CSceneNode*>& nodes, const CVector3f& /*pivot*/, const CVector3f& delta) CScaleNodeCommand::CScaleNodeCommand(INodeEditor *pEditor, const QList<CSceneNode*>& nodes, const CVector3f& /*pivot*/, const CVector3f& delta)
: QUndoCommand("Scale"), : IUndoCommand("Scale"),
mpEditor(pEditor), mpEditor(pEditor),
mCommandEnded(false) mCommandEnded(false)
{ {

View File

@ -1,13 +1,12 @@
#ifndef CSCALENODECOMMAND_H #ifndef CSCALENODECOMMAND_H
#define CSCALENODECOMMAND_H #define CSCALENODECOMMAND_H
#include "IUndoCommand.h"
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include <QUndoCommand>
#include <QList> #include <QList>
class CScaleNodeCommand : public QUndoCommand class CScaleNodeCommand : public IUndoCommand
{ {
struct SNodeScale struct SNodeScale
{ {
@ -29,6 +28,7 @@ public:
bool mergeWith(const QUndoCommand *other); bool mergeWith(const QUndoCommand *other);
void undo(); void undo();
void redo(); void redo();
bool AffectsCleanState() const { return true; }
static CScaleNodeCommand* End(); static CScaleNodeCommand* End();
}; };

View File

@ -2,7 +2,7 @@
#include <Core/Scene/CSceneIterator.h> #include <Core/Scene/CSceneIterator.h>
CSelectAllCommand::CSelectAllCommand(INodeEditor *pEditor, QList<CSceneNode *> &rSelection, CScene *pScene, FNodeFlags NodeFlags) CSelectAllCommand::CSelectAllCommand(INodeEditor *pEditor, QList<CSceneNode *> &rSelection, CScene *pScene, FNodeFlags NodeFlags)
: QUndoCommand("Select All") : IUndoCommand("Select All")
, mpEditor(pEditor) , mpEditor(pEditor)
, mOldSelection(rSelection) , mOldSelection(rSelection)
, mpSelection(&rSelection) , mpSelection(&rSelection)

View File

@ -1,12 +1,11 @@
#ifndef CSELECTALLCOMMAND_H #ifndef CSELECTALLCOMMAND_H
#define CSELECTALLCOMMAND_H #define CSELECTALLCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include <QUndoCommand> class CSelectAllCommand : public IUndoCommand
class CSelectAllCommand : public QUndoCommand
{ {
INodeEditor *mpEditor; INodeEditor *mpEditor;
QList<CSceneNode*> mOldSelection; QList<CSceneNode*> mOldSelection;
@ -18,6 +17,7 @@ public:
~CSelectAllCommand(); ~CSelectAllCommand();
void undo(); void undo();
void redo(); void redo();
bool AffectsCleanState() const { return false; }
}; };
#endif // CSELECTALLCOMMAND_H #endif // CSELECTALLCOMMAND_H

View File

@ -2,7 +2,7 @@
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
CSelectNodeCommand::CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection) CSelectNodeCommand::CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection)
: QUndoCommand("Select"), : IUndoCommand("Select"),
mpEditor(pEditor), mpEditor(pEditor),
mpNode(pNode), mpNode(pNode),
mpSelection(&selection) mpSelection(&selection)

View File

@ -1,12 +1,11 @@
#ifndef CSELECTNODECOMMAND_H #ifndef CSELECTNODECOMMAND_H
#define CSELECTNODECOMMAND_H #define CSELECTNODECOMMAND_H
#include "IUndoCommand.h"
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include <QUndoCommand> class CSelectNodeCommand : public IUndoCommand
class CSelectNodeCommand : public QUndoCommand
{ {
INodeEditor *mpEditor; INodeEditor *mpEditor;
CSceneNode *mpNode; CSceneNode *mpNode;
@ -15,6 +14,7 @@ public:
CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection); CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection);
void undo(); void undo();
void redo(); void redo();
bool AffectsCleanState() const { return false; }
}; };
#endif // CSELECTNODECOMMAND_H #endif // CSELECTNODECOMMAND_H

View File

@ -3,14 +3,14 @@
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
CTranslateNodeCommand::CTranslateNodeCommand() CTranslateNodeCommand::CTranslateNodeCommand()
: QUndoCommand("Translate"), : IUndoCommand("Translate"),
mpEditor(nullptr), mpEditor(nullptr),
mCommandEnded(false) mCommandEnded(false)
{ {
} }
CTranslateNodeCommand::CTranslateNodeCommand(INodeEditor *pEditor, const QList<CSceneNode*>& nodes, const CVector3f& delta, ETransformSpace transformSpace) CTranslateNodeCommand::CTranslateNodeCommand(INodeEditor *pEditor, const QList<CSceneNode*>& nodes, const CVector3f& delta, ETransformSpace transformSpace)
: QUndoCommand("Translate"), : IUndoCommand("Translate"),
mpEditor(pEditor), mpEditor(pEditor),
mCommandEnded(false) mCommandEnded(false)
{ {

View File

@ -1,13 +1,12 @@
#ifndef CTRANSLATENODECOMMAND_H #ifndef CTRANSLATENODECOMMAND_H
#define CTRANSLATENODECOMMAND_H #define CTRANSLATENODECOMMAND_H
#include "IUndoCommand.h"
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include <QUndoCommand>
#include <QList> #include <QList>
class CTranslateNodeCommand : public QUndoCommand class CTranslateNodeCommand : public IUndoCommand
{ {
struct SNodeTranslate struct SNodeTranslate
{ {
@ -27,6 +26,7 @@ public:
bool mergeWith(const QUndoCommand *other); bool mergeWith(const QUndoCommand *other);
void undo(); void undo();
void redo(); void redo();
bool AffectsCleanState() const { return true; }
static CTranslateNodeCommand* End(); static CTranslateNodeCommand* End();
}; };

View File

@ -0,0 +1,19 @@
#ifndef IUNDOCOMMAND
#define IUNDOCOMMAND
#include <QUndoCommand>
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

View File

@ -77,6 +77,9 @@ CWorldEditor::CWorldEditor(QWidget *parent) :
connect(ui->TransformSpinBox, SIGNAL(ValueChanged(CVector3f)), this, SLOT(OnTransformSpinBoxModified(CVector3f))); connect(ui->TransformSpinBox, SIGNAL(ValueChanged(CVector3f)), this, SLOT(OnTransformSpinBoxModified(CVector3f)));
connect(ui->TransformSpinBox, SIGNAL(EditingDone(CVector3f)), this, SLOT(OnTransformSpinBoxEdited(CVector3f))); connect(ui->TransformSpinBox, SIGNAL(EditingDone(CVector3f)), this, SLOT(OnTransformSpinBoxEdited(CVector3f)));
connect(ui->CamSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnCameraSpeedChange(double))); 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() CWorldEditor::~CWorldEditor()
@ -84,10 +87,33 @@ CWorldEditor::~CWorldEditor()
delete ui; delete ui;
} }
void CWorldEditor::closeEvent(QCloseEvent *) void CWorldEditor::closeEvent(QCloseEvent *pEvent)
{ {
if (mpPoiDialog) bool ShouldClose = true;
mpPoiDialog->close();
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*/) bool CWorldEditor::eventFilter(QObject * /*pObj*/, QEvent * /*pEvent*/)
@ -95,7 +121,7 @@ bool CWorldEditor::eventFilter(QObject * /*pObj*/, QEvent * /*pEvent*/)
return false; return false;
} }
void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea) void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex)
{ {
ExitPickMode(); ExitPickMode();
ui->MainViewport->ResetHover(); ui->MainViewport->ResetHover();
@ -153,6 +179,18 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea)
// Set up sidebar tabs // Set up sidebar tabs
CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpArea->Version()); CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpArea->Version());
ui->InstancesTabContents->SetMaster(pMaster); 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() CGameArea* CWorldEditor::ActiveArea()
@ -160,7 +198,26 @@ CGameArea* CWorldEditor::ActiveArea()
return mpArea; 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() void CWorldEditor::UpdateStatusBar()
{ {
// Would be cool to do more frequent status bar updates with more info. Unfortunately, this causes lag. // 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 ************ // ************ 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<const IUndoCommand*>(pkQCmd->child(iChild));
if (pkCmd->AffectsCleanState())
{
IsClean = false;
break;
}
}
}
else
{
const IUndoCommand *pkCmd = static_cast<const IUndoCommand*>(pkQCmd);
if (pkCmd->AffectsCleanState())
IsClean = false;
}
if (!IsClean) break;
}
setWindowModified(!IsClean);
}
}
void CWorldEditor::OnPickModeEnter(QCursor Cursor) void CWorldEditor::OnPickModeEnter(QCursor Cursor)
{ {
ui->MainViewport->SetCursorState(Cursor); ui->MainViewport->SetCursorState(Cursor);
@ -530,11 +636,3 @@ void CWorldEditor::on_ActionEditPoiToWorldMap_triggered()
mpPoiDialog->show(); 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));
}

View File

@ -40,12 +40,14 @@ class CWorldEditor : public INodeEditor
public: public:
explicit CWorldEditor(QWidget *parent = 0); explicit CWorldEditor(QWidget *parent = 0);
~CWorldEditor(); ~CWorldEditor();
void closeEvent(QCloseEvent *); void closeEvent(QCloseEvent *pEvent);
bool eventFilter(QObject *pObj, QEvent *pEvent); bool eventFilter(QObject *pObj, QEvent *pEvent);
void SetArea(CWorld *pWorld, CGameArea *pArea); void SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex);
CGameArea* ActiveArea(); CGameArea* ActiveArea();
public slots: public slots:
bool Save();
void UpdateStatusBar(); void UpdateStatusBar();
void UpdateGizmoUI(); void UpdateGizmoUI();
void UpdateSelectionUI(); void UpdateSelectionUI();
@ -55,6 +57,7 @@ protected:
void GizmoModeChanged(CGizmo::EGizmoMode mode); void GizmoModeChanged(CGizmo::EGizmoMode mode);
private slots: private slots:
void OnUndoStackIndexChanged();
void OnPickModeEnter(QCursor Cursor); void OnPickModeEnter(QCursor Cursor);
void OnPickModeExit(); void OnPickModeExit();
void RefreshViewport(); void RefreshViewport();
@ -85,7 +88,6 @@ private slots:
void on_ActionSelectAll_triggered(); void on_ActionSelectAll_triggered();
void on_ActionInvertSelection_triggered(); void on_ActionInvertSelection_triggered();
void on_ActionEditPoiToWorldMap_triggered(); void on_ActionEditPoiToWorldMap_triggered();
void on_ActionSave_triggered();
}; };
#endif // CWORLDEDITOR_H #endif // CWORLDEDITOR_H

View File

@ -9,7 +9,7 @@
<property ID="0x04" name="Unknown 1" type="vector3f"/> <property ID="0x04" name="Unknown 1" type="vector3f"/>
<property ID="0x05" name="Unknown 2" type="long"/> <property ID="0x05" name="Unknown 2" type="long"/>
<property ID="0x06" name="Unknown 3" type="bool"/> <property ID="0x06" name="Unknown 3" type="bool"/>
<property ID="0x07" name="Unknown 4" type="bool"/> <property ID="0x07" name="Display Fluid Surface" type="bool"/>
<property ID="0x08" name="Texture 1" type="file" extensions="TXTR"/> <property ID="0x08" name="Texture 1" type="file" extensions="TXTR"/>
<property ID="0x09" name="Texture 2" type="file" extensions="TXTR"/> <property ID="0x09" name="Texture 2" type="file" extensions="TXTR"/>
<property ID="0x0A" name="Texture 3" type="file" extensions="TXTR"/> <property ID="0x0A" name="Texture 3" type="file" extensions="TXTR"/>
@ -20,7 +20,7 @@
<property ID="0x0F" name="Unknown 6" type="float"/> <property ID="0x0F" name="Unknown 6" type="float"/>
<property ID="0x10" name="Unknown 7" type="float"/> <property ID="0x10" name="Unknown 7" type="float"/>
<property ID="0x11" name="Unknown 8" type="float"/> <property ID="0x11" name="Unknown 8" type="float"/>
<property ID="0x12" name="Unknown 9" type="bool"/> <property ID="0x12" name="Active" type="bool"/>
<property ID="0x13" name="Unknown 10" type="long"/> <property ID="0x13" name="Unknown 10" type="long"/>
<property ID="0x14" name="Unknown 11" type="bool"/> <property ID="0x14" name="Unknown 11" type="bool"/>
<property ID="0x15" name="Unknown 12" type="float"/> <property ID="0x15" name="Unknown 12" type="float"/>
@ -55,7 +55,9 @@
<property ID="0x1E" name="Unknown 37" type="float"/> <property ID="0x1E" name="Unknown 37" type="float"/>
<property ID="0x1F" name="Unknown 38" type="color"/> <property ID="0x1F" name="Unknown 38" type="color"/>
<property ID="0x20" name="Unknown 39" type="color"/> <property ID="0x20" name="Unknown 39" type="color"/>
<property ID="0x21" name="Particle 1" type="file" extensions="PART"/> <property ID="0x21" name="Enter Particle" type="file" extensions="PART">
<description>This particle plays when an actor/projectile enters the water. It also plays when the morph ball is rolling in it at surface level.</description>
</property>
<property ID="0x22" name="Particle 2" type="file" extensions="PART"/> <property ID="0x22" name="Particle 2" type="file" extensions="PART"/>
<property ID="0x23" name="Particle 3" type="file" extensions="PART"/> <property ID="0x23" name="Particle 3" type="file" extensions="PART"/>
<property ID="0x24" name="Particle 4" type="file" extensions="PART"/> <property ID="0x24" name="Particle 4" type="file" extensions="PART"/>
@ -73,10 +75,10 @@
<property ID="0x30" name="Unknown 45" type="float"/> <property ID="0x30" name="Unknown 45" type="float"/>
<property ID="0x31" name="Unknown 46" type="float"/> <property ID="0x31" name="Unknown 46" type="float"/>
<property ID="0x32" name="Unknown 47" type="float"/> <property ID="0x32" name="Unknown 47" type="float"/>
<property ID="0x33" name="Unknown 48" type="float"/> <property ID="0x33" name="Heat Wave Height" type="float"/>
<property ID="0x34" name="Unknown 49" type="float"/> <property ID="0x34" name="Heat Wave Speed" type="float"/>
<property ID="0x35" name="Unknown 50" type="color"/> <property ID="0x35" name="Heat Wave Color" type="color"/>
<property ID="0x36" name="Texture 7" type="file" extensions="TXTR"/> <property ID="0x36" name="Lightmap Texture" type="file" extensions="TXTR"/>
<property ID="0x37" name="Unknown 51" type="float"/> <property ID="0x37" name="Unknown 51" type="float"/>
<property ID="0x38" name="Unknown 52" type="float"/> <property ID="0x38" name="Unknown 52" type="float"/>
<property ID="0x39" name="Unknown 53" type="float"/> <property ID="0x39" name="Unknown 53" type="float"/>
@ -91,6 +93,7 @@
<property name="InstanceName" ID="0x00"/> <property name="InstanceName" ID="0x00"/>
<property name="Position" ID="0x01"/> <property name="Position" ID="0x01"/>
<property name="Scale" ID="0x02"/> <property name="Scale" ID="0x02"/>
<property name="Active" ID="0x09"/>
</properties> </properties>
<assets/> <assets/>
<rotation_type>disabled</rotation_type> <rotation_type>disabled</rotation_type>