mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-05-26 01:01:24 +00:00
Added support for script instance cloning
This commit is contained in:
parent
3d6993a1ec
commit
99a64342e9
@ -113,15 +113,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
u32 State() const { return mStateID; }
|
inline CGameArea* Area() const { return mpArea; }
|
||||||
u32 Message() const { return mMessageID; }
|
inline u32 State() const { return mStateID; }
|
||||||
u32 SenderID() const { return mSenderID; }
|
inline u32 Message() const { return mMessageID; }
|
||||||
u32 ReceiverID() const { return mReceiverID; }
|
inline u32 SenderID() const { return mSenderID; }
|
||||||
CScriptObject* Sender() const { return mpArea->InstanceByID(mSenderID); }
|
inline u32 ReceiverID() const { return mReceiverID; }
|
||||||
CScriptObject* Receiver() const { return mpArea->InstanceByID(mReceiverID); }
|
inline CScriptObject* Sender() const { return mpArea->InstanceByID(mSenderID); }
|
||||||
|
inline CScriptObject* Receiver() const { return mpArea->InstanceByID(mReceiverID); }
|
||||||
|
|
||||||
void SetState(u32 StateID) { mStateID = StateID; }
|
inline void SetState(u32 StateID) { mStateID = StateID; }
|
||||||
void SetMessage(u32 MessageID) { mMessageID = MessageID; }
|
inline void SetMessage(u32 MessageID) { mMessageID = MessageID; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,13 +99,8 @@ void CPropertyStruct::Copy(const IProperty *pkProp)
|
|||||||
{
|
{
|
||||||
const CPropertyStruct *pkSource = static_cast<const CPropertyStruct*>(pkProp);
|
const CPropertyStruct *pkSource = static_cast<const CPropertyStruct*>(pkProp);
|
||||||
|
|
||||||
for (auto it = mProperties.begin(); it != mProperties.end(); it++)
|
|
||||||
delete *it;
|
|
||||||
|
|
||||||
mProperties.resize(pkSource->mProperties.size());
|
|
||||||
|
|
||||||
for (u32 iSub = 0; iSub < mProperties.size(); iSub++)
|
for (u32 iSub = 0; iSub < mProperties.size(); iSub++)
|
||||||
mProperties[iSub] = pkSource->mProperties[iSub]->Clone(Instance(), this);
|
mProperties[iSub]->Copy(pkSource->mProperties[iSub]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPropertyStruct::ShouldCook()
|
bool CPropertyStruct::ShouldCook()
|
||||||
@ -196,6 +191,15 @@ CPropertyStruct* CPropertyStruct::StructByIDString(const TIDString& rkStr) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ************ CArrayProperty ************
|
// ************ CArrayProperty ************
|
||||||
|
void CArrayProperty::Copy(const IProperty *pkProp)
|
||||||
|
{
|
||||||
|
const CArrayProperty *pkSource = static_cast<const CArrayProperty*>(pkProp);
|
||||||
|
Resize(pkSource->Count());
|
||||||
|
|
||||||
|
for (u32 iSub = 0; iSub < mProperties.size(); iSub++)
|
||||||
|
mProperties[iSub]->Copy(pkSource->mProperties[iSub]);
|
||||||
|
}
|
||||||
|
|
||||||
bool CArrayProperty::ShouldCook()
|
bool CArrayProperty::ShouldCook()
|
||||||
{
|
{
|
||||||
return (mpTemplate->CookPreference() == eNeverCook ? false : true);
|
return (mpTemplate->CookPreference() == eNeverCook ? false : true);
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
#ifndef IPROPERTY
|
#ifndef IPROPERTY
|
||||||
#define IPROPERTY
|
#define IPROPERTY
|
||||||
|
|
||||||
/* This header file declares some classes used to track script object properties
|
|
||||||
* IProperty, TTypedProperty (and typedefs), CPropertyStruct, and CArrayProperty */
|
|
||||||
#include "EPropertyType.h"
|
#include "EPropertyType.h"
|
||||||
#include "IPropertyValue.h"
|
#include "IPropertyValue.h"
|
||||||
#include "Core/Resource/CResource.h"
|
#include "Core/Resource/CResource.h"
|
||||||
@ -21,7 +19,6 @@ typedef TString TIDString;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* IProperty is the base class, containing just some virtual function definitions
|
* IProperty is the base class, containing just some virtual function definitions
|
||||||
* Virtual destructor is mainly there to make cleanup easy; don't need to cast to delete
|
|
||||||
*/
|
*/
|
||||||
class IProperty
|
class IProperty
|
||||||
{
|
{
|
||||||
@ -69,6 +66,15 @@ public:
|
|||||||
/*
|
/*
|
||||||
* TTypedProperty is a template subclass for actual properties.
|
* TTypedProperty is a template subclass for actual properties.
|
||||||
*/
|
*/
|
||||||
|
#define IMPLEMENT_PROPERTY_CLONE(ClassName) \
|
||||||
|
virtual IProperty* Clone(CScriptObject *pInstance, CPropertyStruct *pParent) const \
|
||||||
|
{ \
|
||||||
|
if (!pParent) pParent = mpParent; \
|
||||||
|
ClassName *pOut = new ClassName(mpTemplate, pInstance, pParent); \
|
||||||
|
pOut->Copy(this); \
|
||||||
|
return pOut; \
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ValueType, EPropertyType TypeEnum, class ValueClass>
|
template <typename ValueType, EPropertyType TypeEnum, class ValueClass>
|
||||||
class TTypedProperty : public IProperty
|
class TTypedProperty : public IProperty
|
||||||
{
|
{
|
||||||
@ -94,14 +100,7 @@ public:
|
|||||||
mValue.Set(pkCast->mValue.Get());
|
mValue.Set(pkCast->mValue.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual TTypedProperty* Clone(class CScriptObject *pInstance, CPropertyStruct *pParent) const
|
IMPLEMENT_PROPERTY_CLONE(TTypedProperty)
|
||||||
{
|
|
||||||
if (!pParent) pParent = mpParent;
|
|
||||||
|
|
||||||
TTypedProperty *pOut = new TTypedProperty(mpTemplate, pInstance, pParent);
|
|
||||||
pOut->Copy(this);
|
|
||||||
return pOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool Matches(const IProperty *pkProp) const
|
virtual bool Matches(const IProperty *pkProp) const
|
||||||
{
|
{
|
||||||
@ -138,6 +137,7 @@ class TStringProperty : public TTypedProperty<TString, eStringProperty, CStringV
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IMPLEMENT_PROPERTY_CTORS(TStringProperty, TString)
|
IMPLEMENT_PROPERTY_CTORS(TStringProperty, TString)
|
||||||
|
IMPLEMENT_PROPERTY_CLONE(TStringProperty)
|
||||||
virtual bool MatchesDefault() { return Get().IsEmpty(); }
|
virtual bool MatchesDefault() { return Get().IsEmpty(); }
|
||||||
virtual bool ShouldCook() { return true; }
|
virtual bool ShouldCook() { return true; }
|
||||||
};
|
};
|
||||||
@ -146,6 +146,7 @@ class TFileProperty : public TTypedProperty<CResourceInfo, eFileProperty, CFileV
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IMPLEMENT_PROPERTY_CTORS(TFileProperty, CResourceInfo)
|
IMPLEMENT_PROPERTY_CTORS(TFileProperty, CResourceInfo)
|
||||||
|
IMPLEMENT_PROPERTY_CLONE(TFileProperty)
|
||||||
virtual bool MatchesDefault() { return !Get().IsValid(); }
|
virtual bool MatchesDefault() { return !Get().IsValid(); }
|
||||||
virtual bool ShouldCook() { return true; }
|
virtual bool ShouldCook() { return true; }
|
||||||
};
|
};
|
||||||
@ -154,6 +155,7 @@ class TCharacterProperty : public TTypedProperty<CAnimationParameters, eCharacte
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IMPLEMENT_PROPERTY_CTORS(TCharacterProperty, CAnimationParameters)
|
IMPLEMENT_PROPERTY_CTORS(TCharacterProperty, CAnimationParameters)
|
||||||
|
IMPLEMENT_PROPERTY_CLONE(TCharacterProperty)
|
||||||
virtual bool MatchesDefault() { return Get().AnimSet() == nullptr; }
|
virtual bool MatchesDefault() { return Get().AnimSet() == nullptr; }
|
||||||
virtual bool ShouldCook() { return true; }
|
virtual bool ShouldCook() { return true; }
|
||||||
};
|
};
|
||||||
@ -162,6 +164,7 @@ class TMayaSplineProperty : public TTypedProperty<std::vector<u8>, eMayaSplinePr
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IMPLEMENT_PROPERTY_CTORS(TMayaSplineProperty, std::vector<u8>)
|
IMPLEMENT_PROPERTY_CTORS(TMayaSplineProperty, std::vector<u8>)
|
||||||
|
IMPLEMENT_PROPERTY_CLONE(TMayaSplineProperty)
|
||||||
virtual bool MatchesDefault() { return Get().empty(); }
|
virtual bool MatchesDefault() { return Get().empty(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -256,6 +259,8 @@ public:
|
|||||||
EPropertyType Type() const { return eArrayProperty; }
|
EPropertyType Type() const { return eArrayProperty; }
|
||||||
static inline EPropertyType StaticType() { return eArrayProperty; }
|
static inline EPropertyType StaticType() { return eArrayProperty; }
|
||||||
|
|
||||||
|
virtual void Copy(const IProperty *pkProp);
|
||||||
|
|
||||||
virtual IProperty* Clone(CScriptObject *pInstance, CPropertyStruct *pParent) const
|
virtual IProperty* Clone(CScriptObject *pInstance, CPropertyStruct *pParent) const
|
||||||
{
|
{
|
||||||
if (!pParent) pParent = mpParent;
|
if (!pParent) pParent = mpParent;
|
||||||
|
@ -362,7 +362,7 @@ bool CGizmo::TransformFromInput(const CRay& ray, CCamera& camera)
|
|||||||
if (!mHasTransformed && (mDeltaTranslation != CVector3f::skZero))
|
if (!mHasTransformed && (mDeltaTranslation != CVector3f::skZero))
|
||||||
mHasTransformed = true;
|
mHasTransformed = true;
|
||||||
|
|
||||||
return true;
|
return mHasTransformed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,7 +415,7 @@ bool CGizmo::TransformFromInput(const CRay& ray, CCamera& camera)
|
|||||||
if (!mHasTransformed && (rotAmount != 0.f))
|
if (!mHasTransformed && (rotAmount != 0.f))
|
||||||
mHasTransformed = true;
|
mHasTransformed = true;
|
||||||
|
|
||||||
return true;
|
return mHasTransformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale
|
// Scale
|
||||||
@ -484,7 +484,7 @@ bool CGizmo::TransformFromInput(const CRay& ray, CCamera& camera)
|
|||||||
if (!mHasTransformed && (scaleAmount != 1.f))
|
if (!mHasTransformed && (scaleAmount != 1.f))
|
||||||
mHasTransformed = true;
|
mHasTransformed = true;
|
||||||
|
|
||||||
return true;
|
return mHasTransformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -154,7 +154,8 @@ HEADERS += \
|
|||||||
WorldEditor/CTemplateMimeData.h \
|
WorldEditor/CTemplateMimeData.h \
|
||||||
WorldEditor/CTemplateListView.h \
|
WorldEditor/CTemplateListView.h \
|
||||||
CSelectionIterator.h \
|
CSelectionIterator.h \
|
||||||
Undo/ObjReferences.h
|
Undo/ObjReferences.h \
|
||||||
|
Undo/CCloneSelectionCommand.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
@ -211,7 +212,8 @@ SOURCES += \
|
|||||||
Undo/CDeleteLinksCommand.cpp \
|
Undo/CDeleteLinksCommand.cpp \
|
||||||
Undo/CEditLinkCommand.cpp \
|
Undo/CEditLinkCommand.cpp \
|
||||||
Undo/CDeleteSelectionCommand.cpp \
|
Undo/CDeleteSelectionCommand.cpp \
|
||||||
Undo/CCreateInstanceCommand.cpp
|
Undo/CCreateInstanceCommand.cpp \
|
||||||
|
Undo/CCloneSelectionCommand.cpp
|
||||||
|
|
||||||
# UI Files
|
# UI Files
|
||||||
FORMS += \
|
FORMS += \
|
||||||
|
@ -13,6 +13,7 @@ INodeEditor::INodeEditor(QWidget *pParent)
|
|||||||
, mGizmoTransforming(false)
|
, mGizmoTransforming(false)
|
||||||
, mTranslateSpace(eWorldTransform)
|
, mTranslateSpace(eWorldTransform)
|
||||||
, mRotateSpace(eWorldTransform)
|
, mRotateSpace(eWorldTransform)
|
||||||
|
, mCloneState(eNotCloning)
|
||||||
{
|
{
|
||||||
// Create undo actions
|
// Create undo actions
|
||||||
QAction *pUndoAction = mUndoStack.createUndoAction(this);
|
QAction *pUndoAction = mUndoStack.createUndoAction(this);
|
||||||
@ -85,6 +86,7 @@ bool INodeEditor::IsGizmoVisible()
|
|||||||
void INodeEditor::BeginGizmoTransform()
|
void INodeEditor::BeginGizmoTransform()
|
||||||
{
|
{
|
||||||
mGizmoTransforming = true;
|
mGizmoTransforming = true;
|
||||||
|
if ((qApp->keyboardModifiers() & Qt::ShiftModifier) != 0) mCloneState = eReadyToClone;
|
||||||
|
|
||||||
foreach (QAction *pAction, mGizmoActions)
|
foreach (QAction *pAction, mGizmoActions)
|
||||||
pAction->setEnabled(false);
|
pAction->setEnabled(false);
|
||||||
@ -97,12 +99,20 @@ void INodeEditor::EndGizmoTransform()
|
|||||||
foreach (QAction *pAction, mGizmoActions)
|
foreach (QAction *pAction, mGizmoActions)
|
||||||
pAction->setEnabled(true);
|
pAction->setEnabled(true);
|
||||||
|
|
||||||
if (mGizmo.Mode() == CGizmo::eTranslate)
|
if (mGizmo.HasTransformed())
|
||||||
mUndoStack.push(CTranslateNodeCommand::End());
|
{
|
||||||
else if (mGizmo.Mode() == CGizmo::eRotate)
|
if (mGizmo.Mode() == CGizmo::eTranslate)
|
||||||
mUndoStack.push(CRotateNodeCommand::End());
|
mUndoStack.push(CTranslateNodeCommand::End());
|
||||||
else if (mGizmo.Mode() == CGizmo::eScale)
|
else if (mGizmo.Mode() == CGizmo::eRotate)
|
||||||
mUndoStack.push(CScaleNodeCommand::End());
|
mUndoStack.push(CRotateNodeCommand::End());
|
||||||
|
else if (mGizmo.Mode() == CGizmo::eScale)
|
||||||
|
mUndoStack.push(CScaleNodeCommand::End());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCloneState == eCloning)
|
||||||
|
mUndoStack.endMacro();
|
||||||
|
|
||||||
|
mCloneState = eNotCloning;
|
||||||
}
|
}
|
||||||
|
|
||||||
ETransformSpace INodeEditor::CurrentTransformSpace()
|
ETransformSpace INodeEditor::CurrentTransformSpace()
|
||||||
@ -251,6 +261,13 @@ void INodeEditor::OnSelectionModified()
|
|||||||
|
|
||||||
void INodeEditor::OnGizmoMoved()
|
void INodeEditor::OnGizmoMoved()
|
||||||
{
|
{
|
||||||
|
if (mCloneState == eReadyToClone)
|
||||||
|
{
|
||||||
|
mUndoStack.beginMacro("Clone");
|
||||||
|
mUndoStack.push(new CCloneSelectionCommand(this));
|
||||||
|
mCloneState = eCloning;
|
||||||
|
}
|
||||||
|
|
||||||
switch (mGizmo.Mode())
|
switch (mGizmo.Mode())
|
||||||
{
|
{
|
||||||
case CGizmo::eTranslate:
|
case CGizmo::eTranslate:
|
||||||
|
@ -34,6 +34,7 @@ protected:
|
|||||||
bool mGizmoTransforming;
|
bool mGizmoTransforming;
|
||||||
ETransformSpace mTranslateSpace;
|
ETransformSpace mTranslateSpace;
|
||||||
ETransformSpace mRotateSpace;
|
ETransformSpace mRotateSpace;
|
||||||
|
enum { eNotCloning, eReadyToClone, eCloning } mCloneState;
|
||||||
|
|
||||||
// Gizmo widgets
|
// Gizmo widgets
|
||||||
QActionGroup *mpGizmoGroup;
|
QActionGroup *mpGizmoGroup;
|
||||||
|
97
src/Editor/Undo/CCloneSelectionCommand.cpp
Normal file
97
src/Editor/Undo/CCloneSelectionCommand.cpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#include "CCloneSelectionCommand.h"
|
||||||
|
#include "Editor/CSelectionIterator.h"
|
||||||
|
|
||||||
|
CCloneSelectionCommand::CCloneSelectionCommand(INodeEditor *pEditor)
|
||||||
|
: IUndoCommand("Clone")
|
||||||
|
, mpEditor(qobject_cast<CWorldEditor*>(pEditor)) // todo: fix this! bad assumption! (clone handling code is in INodeEditor but active area is in CWorldEditor)
|
||||||
|
{
|
||||||
|
mOriginalSelection = mpEditor->Selection()->SelectedNodeList();
|
||||||
|
|
||||||
|
for (CSelectionIterator It(mpEditor->Selection()); It; ++It)
|
||||||
|
{
|
||||||
|
if (It->NodeType() == eScriptNode)
|
||||||
|
mNodesToClone << *It;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCloneSelectionCommand::undo()
|
||||||
|
{
|
||||||
|
QList<CSceneNode*> ClonedNodes = mClonedNodes.DereferenceList();
|
||||||
|
mpEditor->Selection()->Clear();
|
||||||
|
|
||||||
|
foreach (CSceneNode *pNode, ClonedNodes)
|
||||||
|
{
|
||||||
|
CScriptObject *pInst = static_cast<CScriptNode*>(pNode)->Object();
|
||||||
|
|
||||||
|
mpEditor->NotifyNodeAboutToBeDeleted(pNode);
|
||||||
|
mpEditor->Scene()->DeleteNode(pNode);
|
||||||
|
mpEditor->ActiveArea()->DeleteInstance(pInst);
|
||||||
|
mpEditor->NotifyNodeDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
mClonedNodes.clear();
|
||||||
|
mpEditor->Selection()->SetSelectedNodes(mOriginalSelection.DereferenceList());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCloneSelectionCommand::redo()
|
||||||
|
{
|
||||||
|
QList<CSceneNode*> ToClone = mNodesToClone.DereferenceList();
|
||||||
|
QList<CSceneNode*> ClonedNodes;
|
||||||
|
QList<u32> ToCloneInstanceIDs;
|
||||||
|
QList<u32> ClonedInstanceIDs;
|
||||||
|
|
||||||
|
foreach (CSceneNode *pNode, ToClone)
|
||||||
|
{
|
||||||
|
mpEditor->NotifyNodeAboutToBeSpawned();
|
||||||
|
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
|
||||||
|
CScriptObject *pInstance = pScript->Object();
|
||||||
|
|
||||||
|
CScriptObject *pCloneInst = mpEditor->ActiveArea()->SpawnInstance(pInstance->Template(), pInstance->Layer());
|
||||||
|
pCloneInst->Properties()->Copy(pInstance->Properties());
|
||||||
|
pCloneInst->EvaluateProperties();
|
||||||
|
|
||||||
|
CScriptNode *pCloneNode = mpEditor->Scene()->CreateScriptNode(pCloneInst);
|
||||||
|
pCloneNode->SetName(pScript->Name());
|
||||||
|
pCloneNode->SetPosition(pScript->LocalPosition());
|
||||||
|
pCloneNode->SetRotation(pScript->LocalRotation());
|
||||||
|
pCloneNode->SetScale(pScript->LocalScale());
|
||||||
|
pCloneNode->OnLoadFinished();
|
||||||
|
|
||||||
|
ToCloneInstanceIDs << pInstance->InstanceID();
|
||||||
|
ClonedInstanceIDs << pCloneInst->InstanceID();
|
||||||
|
ClonedNodes << pCloneNode;
|
||||||
|
mClonedNodes << pCloneNode;
|
||||||
|
mpEditor->NotifyNodeSpawned(pCloneNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone outgoing links from source object; incoming ones are discarded
|
||||||
|
QList<CScriptObject*> LinkedInstances;
|
||||||
|
|
||||||
|
for (int iNode = 0; iNode < ClonedNodes.size(); iNode++)
|
||||||
|
{
|
||||||
|
CScriptObject *pSrc = static_cast<CScriptNode*>(ToClone[iNode])->Object();
|
||||||
|
CScriptObject *pClone = static_cast<CScriptNode*>(ClonedNodes[iNode])->Object();
|
||||||
|
|
||||||
|
for (u32 iLink = 0; iLink < pSrc->NumLinks(eOutgoing); iLink++)
|
||||||
|
{
|
||||||
|
CLink *pSrcLink = pSrc->Link(eOutgoing, iLink);
|
||||||
|
|
||||||
|
// If we're cloning the receiver then target the cloned receiver instead of the original one.
|
||||||
|
u32 ReceiverID = pSrcLink->ReceiverID();
|
||||||
|
if (ToCloneInstanceIDs.contains(ReceiverID))
|
||||||
|
ReceiverID = ClonedInstanceIDs[ToCloneInstanceIDs.indexOf(ReceiverID)];
|
||||||
|
|
||||||
|
CLink *pCloneLink = new CLink(pSrcLink->Area(), pSrcLink->State(), pSrcLink->Message(), pClone->InstanceID(), ReceiverID);
|
||||||
|
pCloneLink->Sender()->AddLink(eOutgoing, pCloneLink);
|
||||||
|
pCloneLink->Receiver()->AddLink(eIncoming, pCloneLink);
|
||||||
|
|
||||||
|
if (!LinkedInstances.contains(pCloneLink->Sender()))
|
||||||
|
LinkedInstances << pCloneLink->Sender();
|
||||||
|
if (!LinkedInstances.contains(pCloneLink->Receiver()))
|
||||||
|
LinkedInstances << pCloneLink->Receiver();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mpEditor->OnLinksModified(LinkedInstances);
|
||||||
|
mpEditor->Selection()->SetSelectedNodes(mClonedNodes.DereferenceList());
|
||||||
|
}
|
22
src/Editor/Undo/CCloneSelectionCommand.h
Normal file
22
src/Editor/Undo/CCloneSelectionCommand.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef CCLONESELECTIONCOMMAND_H
|
||||||
|
#define CCLONESELECTIONCOMMAND_H
|
||||||
|
|
||||||
|
#include "IUndoCommand.h"
|
||||||
|
#include "ObjReferences.h"
|
||||||
|
#include "Editor/WorldEditor/CWorldEditor.h"
|
||||||
|
|
||||||
|
class CCloneSelectionCommand : public IUndoCommand
|
||||||
|
{
|
||||||
|
CWorldEditor *mpEditor;
|
||||||
|
CNodePtrList mOriginalSelection;
|
||||||
|
CNodePtrList mNodesToClone;
|
||||||
|
CNodePtrList mClonedNodes;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CCloneSelectionCommand(INodeEditor *pEditor);
|
||||||
|
void undo();
|
||||||
|
void redo();
|
||||||
|
bool AffectsCleanState() const { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CCLONESELECTIONCOMMAND_H
|
@ -2,6 +2,7 @@
|
|||||||
#define UNDOCOMMANDS
|
#define UNDOCOMMANDS
|
||||||
|
|
||||||
#include "CCreateInstanceCommand.h"
|
#include "CCreateInstanceCommand.h"
|
||||||
|
#include "CCloneSelectionCommand.h"
|
||||||
|
|
||||||
#include "CTranslateNodeCommand.h"
|
#include "CTranslateNodeCommand.h"
|
||||||
#include "CRotateNodeCommand.h"
|
#include "CRotateNodeCommand.h"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user