From 8f82dcbdd9a51340a23352ab009a9d06a96805b8 Mon Sep 17 00:00:00 2001 From: parax0 Date: Sun, 6 Mar 2016 17:44:51 -0700 Subject: [PATCH] Implemented functionality for editing, creating, and deleting script object links --- src/Core/Core.pro | 2 +- src/Core/Resource/Cooker/CScriptCooker.cpp | 25 +-- src/Core/Resource/Factory/CAreaLoader.cpp | 13 +- src/Core/Resource/Factory/CAreaLoader.h | 4 +- src/Core/Resource/Factory/CScriptLoader.cpp | 26 ++-- src/Core/Resource/Script/CLink.h | 128 +++++++++++++++ src/Core/Resource/Script/CMasterTemplate.h | 2 +- src/Core/Resource/Script/CScriptObject.cpp | 58 ++++--- src/Core/Resource/Script/CScriptObject.h | 21 ++- src/Core/Resource/Script/SLink.h | 32 ---- src/Core/Scene/CScriptNode.cpp | 31 ++-- src/Core/Scene/CScriptNode.h | 1 + src/Core/ScriptExtra/CSplinePathExtra.cpp | 11 +- src/Core/ScriptExtra/CWaypointExtra.cpp | 21 +-- src/Core/ScriptExtra/CWaypointExtra.h | 2 +- src/Editor/Editor.pro | 10 +- src/Editor/Undo/CAddLinkCommand.cpp | 35 +++++ src/Editor/Undo/CAddLinkCommand.h | 21 +++ src/Editor/Undo/CDeleteLinksCommand.cpp | 97 ++++++++++++ src/Editor/Undo/CDeleteLinksCommand.h | 30 ++++ src/Editor/Undo/CEditLinkCommand.cpp | 50 ++++++ src/Editor/Undo/CEditLinkCommand.h | 27 ++++ src/Editor/Undo/UndoCommands.h | 4 + src/Editor/WorldEditor/CLinkDialog.cpp | 48 +++++- src/Editor/WorldEditor/CLinkDialog.h | 8 +- src/Editor/WorldEditor/CLinkModel.cpp | 26 ++-- src/Editor/WorldEditor/CLinkModel.h | 10 +- src/Editor/WorldEditor/CStateMessageModel.h | 28 +++- src/Editor/WorldEditor/CWorldEditor.cpp | 16 ++ src/Editor/WorldEditor/CWorldEditor.h | 5 + src/Editor/WorldEditor/WModifyTab.cpp | 164 +++++++++++++------- src/Editor/WorldEditor/WModifyTab.h | 13 +- src/Editor/WorldEditor/WModifyTab.ui | 55 ++++++- 33 files changed, 805 insertions(+), 219 deletions(-) create mode 100644 src/Core/Resource/Script/CLink.h delete mode 100644 src/Core/Resource/Script/SLink.h create mode 100644 src/Editor/Undo/CAddLinkCommand.cpp create mode 100644 src/Editor/Undo/CAddLinkCommand.h create mode 100644 src/Editor/Undo/CDeleteLinksCommand.cpp create mode 100644 src/Editor/Undo/CDeleteLinksCommand.h create mode 100644 src/Editor/Undo/CEditLinkCommand.cpp create mode 100644 src/Editor/Undo/CEditLinkCommand.h diff --git a/src/Core/Core.pro b/src/Core/Core.pro index 4e0f1dea..6beac335 100644 --- a/src/Core/Core.pro +++ b/src/Core/Core.pro @@ -186,7 +186,7 @@ HEADERS += \ Resource/Factory/CSectionMgrIn.h \ Resource/Cooker/CScriptCooker.h \ ScriptExtra/CSplinePathExtra.h \ - Resource/Script/SLink.h + Resource/Script/CLink.h # Source Files SOURCES += \ diff --git a/src/Core/Resource/Cooker/CScriptCooker.cpp b/src/Core/Resource/Cooker/CScriptCooker.cpp index d97451e4..e1818414 100644 --- a/src/Core/Resource/Cooker/CScriptCooker.cpp +++ b/src/Core/Resource/Cooker/CScriptCooker.cpp @@ -1,4 +1,5 @@ #include "CScriptCooker.h" +#include "Core/Resource/Script/CLink.h" void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) { @@ -211,14 +212,14 @@ void CScriptCooker::WriteInstanceMP1(CScriptObject *pInstance) u32 InstanceStart = mpSCLY->Tell(); mpSCLY->WriteLong(pInstance->InstanceID()); - mpSCLY->WriteLong(pInstance->NumOutLinks()); + mpSCLY->WriteLong(pInstance->NumLinks(eOutgoing)); - for (u32 iLink = 0; iLink < pInstance->NumOutLinks(); iLink++) + for (u32 iLink = 0; iLink < pInstance->NumLinks(eOutgoing); iLink++) { - const SLink& rkLink = pInstance->OutLink(iLink); - mpSCLY->WriteLong(rkLink.State); - mpSCLY->WriteLong(rkLink.Message); - mpSCLY->WriteLong(rkLink.ObjectID); + CLink *pLink = pInstance->Link(eOutgoing, iLink); + mpSCLY->WriteLong(pLink->State()); + mpSCLY->WriteLong(pLink->Message()); + mpSCLY->WriteLong(pLink->ReceiverID()); } WriteProperty(pInstance->Properties(), false); @@ -261,14 +262,14 @@ void CScriptCooker::WriteInstanceMP2(CScriptObject *pInstance) u32 InstanceStart = mpSCLY->Tell(); mpSCLY->WriteLong(pInstance->InstanceID()); - mpSCLY->WriteShort((u16) pInstance->NumOutLinks()); + mpSCLY->WriteShort((u16) pInstance->NumLinks(eOutgoing)); - for (u32 iLink = 0; iLink < pInstance->NumOutLinks(); iLink++) + for (u32 iLink = 0; iLink < pInstance->NumLinks(eOutgoing); iLink++) { - const SLink& rkLink = pInstance->OutLink(iLink); - mpSCLY->WriteLong(rkLink.State); - mpSCLY->WriteLong(rkLink.Message); - mpSCLY->WriteLong(rkLink.ObjectID); + CLink *pLink = pInstance->Link(eOutgoing, iLink); + mpSCLY->WriteLong(pLink->State()); + mpSCLY->WriteLong(pLink->Message()); + mpSCLY->WriteLong(pLink->ReceiverID()); } WriteProperty(pInstance->Properties(), false); diff --git a/src/Core/Resource/Factory/CAreaLoader.cpp b/src/Core/Resource/Factory/CAreaLoader.cpp index f2516d01..2457df53 100644 --- a/src/Core/Resource/Factory/CAreaLoader.cpp +++ b/src/Core/Resource/Factory/CAreaLoader.cpp @@ -627,15 +627,10 @@ void CAreaLoader::SetUpObjects() mpArea->mObjectMap[pObj->InstanceID()] = pObj; // Store outgoing connections - for (u32 iCon = 0; iCon < pObj->NumOutLinks(); iCon++) + for (u32 iCon = 0; iCon < pObj->NumLinks(eOutgoing); iCon++) { - SLink Connection = pObj->OutLink(iCon); - - SLink NewConnection; - NewConnection.State = Connection.State; - NewConnection.Message = Connection.Message; - NewConnection.ObjectID = pObj->InstanceID(); - mConnectionMap[Connection.ObjectID].push_back(NewConnection); + CLink *pLink = pObj->Link(eOutgoing, iCon); + mConnectionMap[pLink->ReceiverID()].push_back(pLink); } } } @@ -649,7 +644,7 @@ void CAreaLoader::SetUpObjects() if (iConMap != mConnectionMap.end()) { CScriptObject *pObj = mpArea->GetInstanceByID(InstanceID); - pObj->mInConnections = iConMap->second; + pObj->mInLinks = iConMap->second; } } } diff --git a/src/Core/Resource/Factory/CAreaLoader.h b/src/Core/Resource/Factory/CAreaLoader.h index ab3db78e..db924fe3 100644 --- a/src/Core/Resource/Factory/CAreaLoader.h +++ b/src/Core/Resource/Factory/CAreaLoader.h @@ -2,7 +2,7 @@ #define CAREALOADER_H #include "CSectionMgrIn.h" -#include "Core/Resource/Script/SLink.h" +#include "Core/Resource/Script/CLink.h" #include "Core/Resource/CGameArea.h" #include "Core/Resource/EGame.h" #include "Core/Resource/CResCache.h" @@ -22,7 +22,7 @@ class CAreaLoader u32 mNumLayers; // Object connections - std::unordered_map> mConnectionMap; + std::unordered_map> mConnectionMap; // Compression u8 *mDecmpBuffer; diff --git a/src/Core/Resource/Factory/CScriptLoader.cpp b/src/Core/Resource/Factory/CScriptLoader.cpp index 35f3e9d1..fed03cbb 100644 --- a/src/Core/Resource/Factory/CScriptLoader.cpp +++ b/src/Core/Resource/Factory/CScriptLoader.cpp @@ -219,15 +219,16 @@ CScriptObject* CScriptLoader::LoadObjectMP1(IInputStream& SCLY) // Load connections u32 NumLinks = SCLY.ReadLong(); - mpObj->mOutConnections.reserve(NumLinks); + mpObj->mOutLinks.reserve(NumLinks); for (u32 iLink = 0; iLink < NumLinks; iLink++) { - SLink Link; - Link.State = SCLY.ReadLong(); - Link.Message = SCLY.ReadLong(); - Link.ObjectID = SCLY.ReadLong(); - mpObj->mOutConnections.push_back(Link); + u32 State = SCLY.ReadLong(); + u32 Message = SCLY.ReadLong(); + u32 ReceiverID = SCLY.ReadLong(); + + CLink *pLink = new CLink(mpArea, State, Message, mpObj->mInstanceID, ReceiverID); + mpObj->mOutLinks.push_back(pLink); } // Load object... @@ -331,15 +332,16 @@ CScriptObject* CScriptLoader::LoadObjectMP2(IInputStream& SCLY) // Load connections u32 NumConnections = SCLY.ReadShort(); - mpObj->mOutConnections.reserve(NumConnections); + mpObj->mOutLinks.reserve(NumConnections); for (u32 iCon = 0; iCon < NumConnections; iCon++) { - SLink Link; - Link.State = SCLY.ReadLong(); - Link.Message = SCLY.ReadLong(); - Link.ObjectID = SCLY.ReadLong(); - mpObj->mOutConnections.push_back(Link); + u32 State = SCLY.ReadLong(); + u32 Message = SCLY.ReadLong(); + u32 ReceiverID = SCLY.ReadLong(); + + CLink *pLink = new CLink(mpArea, State, Message, mpObj->mInstanceID, ReceiverID); + mpObj->mOutLinks.push_back(pLink); } // Load object diff --git a/src/Core/Resource/Script/CLink.h b/src/Core/Resource/Script/CLink.h new file mode 100644 index 00000000..3709e8bd --- /dev/null +++ b/src/Core/Resource/Script/CLink.h @@ -0,0 +1,128 @@ +#ifndef CLINK_H +#define CLINK_H + +#include "CScriptObject.h" +#include "Core/Resource/CGameArea.h" +#include +#include + +struct SState +{ + u32 ID; + TString Name; + + SState() {} + SState(u32 _ID, const TString& rkName) : ID(_ID), Name(rkName) {} +}; + +struct SMessage +{ + u32 ID; + TString Name; + + SMessage() {} + SMessage(u32 _ID, const TString& rkName) : ID(_ID), Name(rkName) {} +}; + +class CLink +{ + CGameArea *mpArea; + u32 mStateID; + u32 mMessageID; + u32 mSenderID; + u32 mReceiverID; + +public: + CLink(CGameArea *pArea) + : mpArea(pArea) + , mStateID(-1) + , mMessageID(-1) + , mSenderID(-1) + , mReceiverID(-1) + {} + + CLink(CGameArea *pArea, u32 StateID, u32 MessageID, u32 SenderID, u32 ReceiverID) + : mpArea(pArea) + , mStateID(StateID) + , mMessageID(MessageID) + , mSenderID(SenderID) + , mReceiverID(ReceiverID) + {} + + void SetSender(u32 NewSenderID, u32 Index = -1) + { + u32 OldSenderID = mSenderID; + CScriptObject *pOldSender = mpArea->GetInstanceByID(OldSenderID); + CScriptObject *pNewSender = mpArea->GetInstanceByID(NewSenderID); + + mSenderID = NewSenderID; + pOldSender->RemoveLink(eOutgoing, this); + pNewSender->AddLink(eOutgoing, this, Index); + } + + void SetReceiver(u32 NewReceiverID, u32 Index = -1) + { + u32 OldReceiverID = mSenderID; + CScriptObject *pOldReceiver = mpArea->GetInstanceByID(OldReceiverID); + CScriptObject *pNewReceiver = mpArea->GetInstanceByID(NewReceiverID); + + mReceiverID = NewReceiverID; + pOldReceiver->RemoveLink(eIncoming, this); + pNewReceiver->AddLink(eIncoming, this, Index); + } + + u32 SenderIndex() const + { + CScriptObject *pSender = mpArea->GetInstanceByID(mSenderID); + + for (u32 iLink = 0; iLink < pSender->NumLinks(eOutgoing); iLink++) + { + if (pSender->Link(eOutgoing, iLink) == this) + return iLink; + } + + return -1; + } + + u32 ReceiverIndex() const + { + CScriptObject *pReceiver = mpArea->GetInstanceByID(mReceiverID); + + for (u32 iLink = 0; iLink < pReceiver->NumLinks(eIncoming); iLink++) + { + if (pReceiver->Link(eIncoming, iLink) == this) + return iLink; + } + + return -1; + } + + // Operators + bool operator==(const CLink& rkOther) + { + return ( (mpArea == rkOther.mpArea) && + (mStateID == rkOther.mStateID) && + (mMessageID == rkOther.mMessageID) && + (mSenderID == rkOther.mSenderID) && + (mReceiverID == rkOther.mReceiverID) ); + } + + bool operator!=(const CLink& rkOther) + { + return (!(*this == rkOther)); + } + + // Accessors + u32 State() const { return mStateID; } + u32 Message() const { return mMessageID; } + u32 SenderID() const { return mSenderID; } + u32 ReceiverID() const { return mReceiverID; } + CScriptObject* Sender() const { return mpArea->GetInstanceByID(mSenderID); } + CScriptObject* Receiver() const { return mpArea->GetInstanceByID(mReceiverID); } + + void SetState(u32 StateID) { mStateID = StateID; } + void SetMessage(u32 MessageID) { mMessageID = MessageID; } +}; + + +#endif // CLINK_H diff --git a/src/Core/Resource/Script/CMasterTemplate.h b/src/Core/Resource/Script/CMasterTemplate.h index d361ac48..ab57be5b 100644 --- a/src/Core/Resource/Script/CMasterTemplate.h +++ b/src/Core/Resource/Script/CMasterTemplate.h @@ -2,7 +2,7 @@ #define CMASTERTEMPLATE_H #include "CScriptTemplate.h" -#include "SLink.h" +#include "CLink.h" #include "Core/Resource/EGame.h" #include #include diff --git a/src/Core/Resource/Script/CScriptObject.cpp b/src/Core/Resource/Script/CScriptObject.cpp index 75f65b02..a0990e6c 100644 --- a/src/Core/Resource/Script/CScriptObject.cpp +++ b/src/Core/Resource/Script/CScriptObject.cpp @@ -21,6 +21,10 @@ CScriptObject::~CScriptObject() { if (mpProperties) delete mpProperties; mpTemplate->RemoveObject(this); + + // Note: Incoming links will be deleted by the sender. + for (u32 iLink = 0; iLink < mOutLinks.size(); iLink++) + delete mOutLinks[iLink]; } // ************ DATA MANIPULATION ************ @@ -94,17 +98,17 @@ bool CScriptObject::HasNearVisibleActivation() const if (mIsCheckingNearVisibleActivation) return false; mIsCheckingNearVisibleActivation = true; - for (u32 iLink = 0; iLink < mInConnections.size(); iLink++) + for (u32 iLink = 0; iLink < mInLinks.size(); iLink++) { - const SLink& rkLink = mInConnections[iLink]; + CLink *pLink = mInLinks[iLink]; // Check for trigger activation - if (rkLink.State == 0x49533034 || rkLink.State == 0x49533035 || rkLink.State == 0x49533036) // "IS04", "IS05", or "IS06" + if (pLink->State() == 0x49533034 || pLink->State() == 0x49533035 || pLink->State() == 0x49533036) // "IS04", "IS05", or "IS06" { - if ( (!IsRelay && rkLink.Message == 0x41435456) || // "ACTV" - (IsRelay && rkLink.Message == 0x4143544E) ) // "ACTN" + if ( (!IsRelay && pLink->Message() == 0x41435456) || // "ACTV" + (IsRelay && pLink->Message() == 0x4143544E) ) // "ACTN" { - CScriptObject *pObj = mpArea->GetInstanceByID(rkLink.ObjectID); + CScriptObject *pObj = pLink->Sender(); if (pObj->ObjectTypeID() == 0x54524752) // "TRGR" { @@ -115,12 +119,12 @@ bool CScriptObject::HasNearVisibleActivation() const } // Check for relay activation - else if (rkLink.State == 0x524C4159) // "RLAY" + else if (pLink->State() == 0x524C4159) // "RLAY" { - if ( (!IsRelay && rkLink.Message == 0x41435456) || // "ACTV" - (IsRelay && rkLink.Message == 0x4143544E) ) // "ACTN" + if ( (!IsRelay && pLink->Message() == 0x41435456) || // "ACTV" + (IsRelay && pLink->Message() == 0x4143544E) ) // "ACTN" { - CScriptObject *pObj = mpArea->GetInstanceByID(rkLink.ObjectID); + CScriptObject *pObj = pLink->Sender(); if (pObj->ObjectTypeID() == 0x53524C59) // "SRLY" Relays.push_back(pObj); @@ -198,24 +202,42 @@ u32 CScriptObject::InstanceID() const return mInstanceID; } -u32 CScriptObject::NumInLinks() const +u32 CScriptObject::NumLinks(ELinkType Type) const { - return mInConnections.size(); + return (Type == eIncoming ? mInLinks.size() : mOutLinks.size()); } -u32 CScriptObject::NumOutLinks() const +CLink* CScriptObject::Link(ELinkType Type, u32 Index) const { - return mOutConnections.size(); + return (Type == eIncoming ? mInLinks[Index] : mOutLinks[Index]); } -const SLink& CScriptObject::InLink(u32 index) const +void CScriptObject::AddLink(ELinkType Type, CLink *pLink, u32 Index /*= -1*/) { - return mInConnections[index]; + std::vector *pLinkVec = (Type == eIncoming ? &mInLinks : &mOutLinks); + + if (Index == -1 || Index == pLinkVec->size()) + pLinkVec->push_back(pLink); + else + { + auto it = pLinkVec->begin(); + std::advance(it, Index); + pLinkVec->insert(it, pLink); + } } -const SLink& CScriptObject::OutLink(u32 index) const +void CScriptObject::RemoveLink(ELinkType Type, CLink *pLink) { - return mOutConnections[index]; + std::vector *pLinkVec = (Type == eIncoming ? &mInLinks : &mOutLinks); + + for (auto it = pLinkVec->begin(); it != pLinkVec->end(); it++) + { + if (*it == pLink) + { + pLinkVec->erase(it); + break; + } + } } TString CScriptObject::InstanceName() const diff --git a/src/Core/Resource/Script/CScriptObject.h b/src/Core/Resource/Script/CScriptObject.h index 58a5a1e9..df08c38c 100644 --- a/src/Core/Resource/Script/CScriptObject.h +++ b/src/Core/Resource/Script/CScriptObject.h @@ -1,7 +1,6 @@ #ifndef CSCRIPTOBJECT_H #define CSCRIPTOBJECT_H -#include "SLink.h" #include "IProperty.h" #include "IPropertyTemplate.h" #include "CScriptTemplate.h" @@ -10,6 +9,13 @@ #include "Core/Resource/CGameArea.h" class CScriptLayer; +class CLink; + +enum ELinkType +{ + eIncoming, + eOutgoing +}; class CScriptObject { @@ -22,8 +28,8 @@ class CScriptObject u32 mVersion; u32 mInstanceID; - std::vector mOutConnections; - std::vector mInConnections; + std::vector mOutLinks; + std::vector mInLinks; CPropertyStruct *mpProperties; TStringProperty *mpInstanceName; @@ -67,10 +73,11 @@ public: IProperty* PropertyByIDString(const TIDString& str) const; u32 ObjectTypeID() const; u32 InstanceID() const; - u32 NumInLinks() const; - u32 NumOutLinks() const; - const SLink& InLink(u32 index) const; - const SLink& OutLink(u32 index) const; + + u32 NumLinks(ELinkType Type) const; + CLink* Link(ELinkType Type, u32 Index) const; + void AddLink(ELinkType Type, CLink *pLink, u32 Index = -1); + void RemoveLink(ELinkType Type, CLink *pLink); CVector3f Position() const; CVector3f Rotation() const; diff --git a/src/Core/Resource/Script/SLink.h b/src/Core/Resource/Script/SLink.h deleted file mode 100644 index f4943062..00000000 --- a/src/Core/Resource/Script/SLink.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SLINK_H -#define SLINK_H - -#include -#include - -struct SState -{ - u32 ID; - TString Name; - - SState() {} - SState(u32 _ID, const TString& rkName) : ID(_ID), Name(rkName) {} -}; - -struct SMessage -{ - u32 ID; - TString Name; - - SMessage() {} - SMessage(u32 _ID, const TString& rkName) : ID(_ID), Name(rkName) {} -}; - -struct SLink -{ - u32 State; - u32 Message; - u32 ObjectID; // not a pointer because it can refer to objects outside the current area -}; - -#endif // SLINK_H diff --git a/src/Core/Scene/CScriptNode.cpp b/src/Core/Scene/CScriptNode.cpp index c08d10cd..1b43445c 100644 --- a/src/Core/Scene/CScriptNode.cpp +++ b/src/Core/Scene/CScriptNode.cpp @@ -255,18 +255,18 @@ void CScriptNode::DrawSelection() CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity; CGraphics::UpdateMVPBlock(); - for (u32 iIn = 0; iIn < mpInstance->NumInLinks(); iIn++) + for (u32 iIn = 0; iIn < mpInstance->NumLinks(eIncoming); iIn++) { // Don't draw in links if the other object is selected. - const SLink& con = mpInstance->InLink(iIn); - CScriptNode *pLinkNode = mpScene->ScriptNodeByID(con.ObjectID); + CLink *pLink = mpInstance->Link(eIncoming, iIn); + CScriptNode *pLinkNode = mpScene->ScriptNodeByID(pLink->SenderID()); if (pLinkNode && !pLinkNode->IsSelected()) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentRed); } - for (u32 iOut = 0; iOut < mpInstance->NumOutLinks(); iOut++) + for (u32 iOut = 0; iOut < mpInstance->NumLinks(eOutgoing); iOut++) { - const SLink& con = mpInstance->OutLink(iOut); - CScriptNode *pLinkNode = mpScene->ScriptNodeByID(con.ObjectID); + CLink *pLink = mpInstance->Link(eOutgoing, iOut); + CScriptNode *pLinkNode = mpScene->ScriptNodeByID(pLink->ReceiverID()); if (pLinkNode) CDrawUtil::DrawLine(CenterPoint(), pLinkNode->CenterPoint(), CColor::skTransparentGreen); } } @@ -429,6 +429,11 @@ CColor CScriptNode::TintColor(const SViewInfo &ViewInfo) const return BaseColor; } +void CScriptNode::LinksModified() +{ + if (mpExtra) mpExtra->LinksModified(); +} + void CScriptNode::PropertyModified(IProperty *pProp) { // Update volume @@ -534,13 +539,13 @@ void CScriptNode::GeneratePosition() // Ideal way to generate the position is to find a spot close to where it's being used. // To do this I check the location of the objects that this one is linked to. - u32 NumLinks = mpInstance->NumInLinks() + mpInstance->NumOutLinks(); + u32 NumLinks = mpInstance->NumLinks(eIncoming) + mpInstance->NumLinks(eOutgoing); // In the case of one link, apply an offset so the new position isn't the same place as the object it's linked to if (NumLinks == 1) { - const SLink& link = (mpInstance->NumInLinks() > 0 ? mpInstance->InLink(0) : mpInstance->OutLink(0)); - CScriptNode *pNode = mpScene->ScriptNodeByID(link.ObjectID); + u32 LinkedID = (mpInstance->NumLinks(eIncoming) > 0 ? mpInstance->Link(eIncoming, 0)->SenderID() : mpInstance->Link(eOutgoing, 0)->ReceiverID()); + CScriptNode *pNode = mpScene->ScriptNodeByID(LinkedID); pNode->GeneratePosition(); mPosition = pNode->AbsolutePosition(); mPosition.z += (pNode->AABox().Size().z / 2.f); @@ -553,9 +558,9 @@ void CScriptNode::GeneratePosition() { CVector3f NewPos = CVector3f::skZero; - for (u32 iIn = 0; iIn < mpInstance->NumInLinks(); iIn++) + for (u32 iIn = 0; iIn < mpInstance->NumLinks(eIncoming); iIn++) { - CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->InLink(iIn).ObjectID); + CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->Link(eIncoming, iIn)->SenderID()); if (pNode) { @@ -564,9 +569,9 @@ void CScriptNode::GeneratePosition() } } - for (u32 iOut = 0; iOut < mpInstance->NumOutLinks(); iOut++) + for (u32 iOut = 0; iOut < mpInstance->NumLinks(eOutgoing); iOut++) { - CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->OutLink(iOut).ObjectID); + CScriptNode *pNode = mpScene->ScriptNodeByID(mpInstance->Link(eOutgoing, iOut)->ReceiverID()); if (pNode) { diff --git a/src/Core/Scene/CScriptNode.h b/src/Core/Scene/CScriptNode.h index 03a5aa80..e22c3996 100644 --- a/src/Core/Scene/CScriptNode.h +++ b/src/Core/Scene/CScriptNode.h @@ -45,6 +45,7 @@ public: CColor TintColor(const SViewInfo &ViewInfo) const; CColor WireframeColor() const; + void LinksModified(); void PropertyModified(IProperty *pProp); void UpdatePreviewVolume(); void GeneratePosition(); diff --git a/src/Core/ScriptExtra/CSplinePathExtra.cpp b/src/Core/ScriptExtra/CSplinePathExtra.cpp index ac60093a..f86e8452 100644 --- a/src/Core/ScriptExtra/CSplinePathExtra.cpp +++ b/src/Core/ScriptExtra/CSplinePathExtra.cpp @@ -1,5 +1,6 @@ #include "CSplinePathExtra.h" #include "CWaypointExtra.h" +#include "Core/Resource/Script/CLink.h" #include "Core/Scene/CScene.h" CSplinePathExtra::CSplinePathExtra(CScriptObject *pInstance, CScene *pScene, CSceneNode *pParent) @@ -45,14 +46,14 @@ void CSplinePathExtra::AddWaypoints() std::set CheckedWaypoints; - for (u32 iLink = 0; iLink < mpInstance->NumOutLinks(); iLink++) + for (u32 iLink = 0; iLink < mpInstance->NumLinks(eOutgoing); iLink++) { - const SLink& rkLink = mpInstance->OutLink(iLink); + CLink *pLink = mpInstance->Link(eOutgoing, iLink); - if ( (rkLink.State == 0x49533030 && rkLink.Message == 0x41544348) || // InternalState00/Attach - (rkLink.State == 0x4D4F5450 && rkLink.Message == 0x41544348) ) // MotionPath/Attach + if ( (pLink->State() == 0x49533030 && pLink->Message() == 0x41544348) || // InternalState00/Attach + (pLink->State() == 0x4D4F5450 && pLink->Message() == 0x41544348) ) // MotionPath/Attach { - CScriptNode *pNode = mpScene->ScriptNodeByID(rkLink.ObjectID); + CScriptNode *pNode = mpScene->ScriptNodeByID(pLink->ReceiverID()); if (pNode && pNode->Object()->ObjectTypeID() == 0x57415950) // Waypoint { diff --git a/src/Core/ScriptExtra/CWaypointExtra.cpp b/src/Core/ScriptExtra/CWaypointExtra.cpp index cd366c8f..bba91a53 100644 --- a/src/Core/ScriptExtra/CWaypointExtra.cpp +++ b/src/Core/ScriptExtra/CWaypointExtra.cpp @@ -1,4 +1,5 @@ #include "CWaypointExtra.h" +#include "Core/Resource/Script/CLink.h" #include "Core/Render/CDrawUtil.h" #include "Core/Render/CRenderer.h" #include "Core/Scene/CScene.h" @@ -78,13 +79,13 @@ void CWaypointExtra::BuildLinks() { mLinks.clear(); - for (u32 iLink = 0; iLink < mpInstance->NumOutLinks(); iLink++) + for (u32 iLink = 0; iLink < mpInstance->NumLinks(eOutgoing); iLink++) { - const SLink& rkLink = mpInstance->OutLink(iLink); + CLink *pLink = mpInstance->Link(eOutgoing, iLink); - if (IsPathLink(rkLink)) + if (IsPathLink(pLink)) { - CScriptNode *pNode = mpScene->ScriptNodeByID(rkLink.ObjectID); + CScriptNode *pNode = mpScene->ScriptNodeByID(pLink->ReceiverID()); SWaypointLink Link; Link.pWaypoint = pNode; @@ -97,26 +98,26 @@ void CWaypointExtra::BuildLinks() mLinksBuilt = true; } -bool CWaypointExtra::IsPathLink(const SLink& rkLink) +bool CWaypointExtra::IsPathLink(CLink *pLink) { bool Valid = false; - if (rkLink.State < 0xFF) + if (pLink->State() < 0xFF) { - if (rkLink.State == 0x1 && rkLink.Message == 0x8) Valid = true; // Arrived / Next (MP1) + if (pLink->State() == 0x1 && pLink->Message() == 0x8) Valid = true; // Arrived / Next (MP1) } else { - CFourCC State(rkLink.State); - CFourCC Message(rkLink.Message); + CFourCC State(pLink->State()); + CFourCC Message(pLink->Message()); if (State == "ARRV" && Message == "NEXT") Valid = true; // Arrived / Next (MP2) if (State == "NEXT" && Message == "ATCH") Valid = true; // Next / Attach (MP3/DKCR) } if (Valid) { - CScriptNode *pNode = mpScene->ScriptNodeByID(rkLink.ObjectID); + CScriptNode *pNode = mpScene->ScriptNodeByID(pLink->ReceiverID()); if (pNode) return pNode->Object()->ObjectTypeID() == mpInstance->ObjectTypeID(); diff --git a/src/Core/ScriptExtra/CWaypointExtra.h b/src/Core/ScriptExtra/CWaypointExtra.h index 81fca9ac..4cbaecee 100644 --- a/src/Core/ScriptExtra/CWaypointExtra.h +++ b/src/Core/ScriptExtra/CWaypointExtra.h @@ -26,7 +26,7 @@ public: void AddToSplinePath(CSplinePathExtra *pPath); void RemoveFromSplinePath(CSplinePathExtra *pPath); void BuildLinks(); - bool IsPathLink(const SLink& rkLink); + bool IsPathLink(CLink *pLink); void GetLinkedWaypoints(std::list& rOut); void LinksModified(); diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index d63995b2..8e1eacb2 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -145,7 +145,10 @@ HEADERS += \ WorldEditor/CTemplateEditDialog.h \ WorldEditor/CLinkDialog.h \ WorldEditor/CStateMessageModel.h \ - WorldEditor/CSelectInstanceDialog.h + WorldEditor/CSelectInstanceDialog.h \ + Undo/CAddLinkCommand.h \ + Undo/CDeleteLinksCommand.h \ + Undo/CEditLinkCommand.h # Source Files SOURCES += \ @@ -204,7 +207,10 @@ SOURCES += \ Undo/CChangeLayerCommand.cpp \ WorldEditor/CTemplateEditDialog.cpp \ WorldEditor/CLinkDialog.cpp \ - WorldEditor/CSelectInstanceDialog.cpp + WorldEditor/CSelectInstanceDialog.cpp \ + Undo/CAddLinkCommand.cpp \ + Undo/CDeleteLinksCommand.cpp \ + Undo/CEditLinkCommand.cpp # UI Files FORMS += \ diff --git a/src/Editor/Undo/CAddLinkCommand.cpp b/src/Editor/Undo/CAddLinkCommand.cpp new file mode 100644 index 00000000..4a93e9a0 --- /dev/null +++ b/src/Editor/Undo/CAddLinkCommand.cpp @@ -0,0 +1,35 @@ +#include "CAddLinkCommand.h" +#include + +CAddLinkCommand::CAddLinkCommand(CWorldEditor *pEditor, CLink Link) + : IUndoCommand("Add Link") + , mpEditor(pEditor) + , mLink(Link) +{ + mAffectedInstances << mLink.Sender(); + + if (mLink.Sender() != mLink.Receiver()) + mAffectedInstances << mLink.Receiver(); +} + +void CAddLinkCommand::undo() +{ + CScriptObject *pSender = mLink.Sender(); + CScriptObject *pReceiver = mLink.Receiver(); + u32 SenderIndex = pSender->NumLinks(eOutgoing) - 1; + CLink *pLink = pSender->Link(eOutgoing, SenderIndex); + pSender->RemoveLink(eOutgoing, pLink); + pReceiver->RemoveLink(eIncoming, pLink); + delete pLink; + + mpEditor->InstanceLinksModified(mAffectedInstances); +} + +void CAddLinkCommand::redo() +{ + CLink *pLink = new CLink(mLink); + pLink->Sender()->AddLink(eOutgoing, pLink, -1); + pLink->Receiver()->AddLink(eIncoming, pLink, -1); + + mpEditor->InstanceLinksModified(mAffectedInstances); +} diff --git a/src/Editor/Undo/CAddLinkCommand.h b/src/Editor/Undo/CAddLinkCommand.h new file mode 100644 index 00000000..23b479bd --- /dev/null +++ b/src/Editor/Undo/CAddLinkCommand.h @@ -0,0 +1,21 @@ +#ifndef CADDLINKCOMMAND_H +#define CADDLINKCOMMAND_H + +#include "IUndoCommand.h" +#include "Editor/WorldEditor/CWorldEditor.h" +#include + +class CAddLinkCommand : public IUndoCommand +{ + CWorldEditor *mpEditor; + CLink mLink; + QList mAffectedInstances; + +public: + CAddLinkCommand(CWorldEditor *pEditor, CLink Link); + void undo(); + void redo(); + bool AffectsCleanState() const { return true; } +}; + +#endif // CADDLINKCOMMAND_H diff --git a/src/Editor/Undo/CDeleteLinksCommand.cpp b/src/Editor/Undo/CDeleteLinksCommand.cpp new file mode 100644 index 00000000..668bc3e5 --- /dev/null +++ b/src/Editor/Undo/CDeleteLinksCommand.cpp @@ -0,0 +1,97 @@ +#include "CDeleteLinksCommand.h" +#include + +CDeleteLinksCommand::CDeleteLinksCommand(CWorldEditor *pEditor, CScriptObject *pObject, ELinkType Type, const QVector& rkIndices) + : IUndoCommand("Delete Links") + , mpEditor(pEditor) +{ + mAffectedInstances << pObject; + + for (int iIdx = 0; iIdx < rkIndices.size(); iIdx++) + { + CLink *pLink = pObject->Link(Type, rkIndices[iIdx]); + + SDeletedLink DelLink; + DelLink.State = pLink->State(); + DelLink.Message = pLink->Message(); + DelLink.pSender = pLink->Sender(); + DelLink.pReceiver = pLink->Receiver(); + DelLink.SenderIndex = pLink->SenderIndex(); + DelLink.ReceiverIndex = pLink->ReceiverIndex(); + mLinks << DelLink; + + if (Type == eOutgoing) + { + if (!mAffectedInstances.contains(DelLink.pReceiver)) + mAffectedInstances << DelLink.pReceiver; + } + else + { + if (!mAffectedInstances.contains(DelLink.pSender)) + mAffectedInstances << DelLink.pSender; + } + } +} + +void CDeleteLinksCommand::undo() +{ + struct SNewLink + { + SDeletedLink *pDelLink; + CLink *pLink; + }; + QVector NewLinks; + + for (int iLink = 0; iLink < mLinks.size(); iLink++) + { + SDeletedLink& rDelLink = mLinks[iLink]; + + SNewLink Link; + Link.pDelLink = &mLinks[iLink]; + Link.pLink = new CLink(mpEditor->ActiveArea(), rDelLink.State, rDelLink.Message, rDelLink.pSender->InstanceID(), rDelLink.pReceiver->InstanceID()); + NewLinks << Link; + } + + // Add to senders + qSort(NewLinks.begin(), NewLinks.end(), [](SNewLink& rLinkA, SNewLink& rLinkB) { return rLinkA.pDelLink->SenderIndex < rLinkB.pDelLink->SenderIndex; }); + + for (int iLink = 0; iLink < NewLinks.size(); iLink++) + { + SNewLink& rNew = NewLinks[iLink]; + rNew.pDelLink->pSender->AddLink(eOutgoing, rNew.pLink, rNew.pDelLink->SenderIndex); + } + + // Add to receivers + qSort(NewLinks.begin(), NewLinks.end(), [](SNewLink& rLinkA, SNewLink& rLinkB) { return rLinkA.pDelLink->ReceiverIndex < rLinkB.pDelLink->ReceiverIndex; }); + + for (int iLink = 0; iLink < NewLinks.size(); iLink++) + { + SNewLink& rNew = NewLinks[iLink]; + rNew.pDelLink->pReceiver->AddLink(eIncoming, rNew.pLink, rNew.pDelLink->ReceiverIndex); + } + + // Notify world editor + mpEditor->OnLinksModified(mAffectedInstances); +} + +void CDeleteLinksCommand::redo() +{ + QVector Links; + + for (int iLink = 0; iLink < mLinks.size(); iLink++) + { + SDeletedLink& rLink = mLinks[iLink]; + Links << rLink.pSender->Link(eOutgoing, rLink.SenderIndex); + } + + for (int iLink = 0; iLink < Links.size(); iLink++) + { + CLink *pLink = Links[iLink]; + pLink->Sender()->RemoveLink(eOutgoing, pLink); + pLink->Receiver()->RemoveLink(eIncoming, pLink); + delete pLink; + } + + // Notify world editor + mpEditor->OnLinksModified(mAffectedInstances); +} diff --git a/src/Editor/Undo/CDeleteLinksCommand.h b/src/Editor/Undo/CDeleteLinksCommand.h new file mode 100644 index 00000000..b599189a --- /dev/null +++ b/src/Editor/Undo/CDeleteLinksCommand.h @@ -0,0 +1,30 @@ +#ifndef CDELETELINKSCOMMAND_H +#define CDELETELINKSCOMMAND_H + +#include "IUndoCommand.h" +#include "Editor/WorldEditor/CWorldEditor.h" + +class CDeleteLinksCommand : public IUndoCommand +{ + CWorldEditor *mpEditor; + QList mAffectedInstances; + + struct SDeletedLink + { + u32 State; + u32 Message; + CScriptObject *pSender; + CScriptObject *pReceiver; + u32 SenderIndex; + u32 ReceiverIndex; + }; + QVector mLinks; + +public: + CDeleteLinksCommand(CWorldEditor *pEditor, CScriptObject *pObject, ELinkType Type, const QVector& rkIndices); + void undo(); + void redo(); + bool AffectsCleanState() const { return true; } +}; + +#endif // CDELETELINKSCOMMAND_H diff --git a/src/Editor/Undo/CEditLinkCommand.cpp b/src/Editor/Undo/CEditLinkCommand.cpp new file mode 100644 index 00000000..d584f0bb --- /dev/null +++ b/src/Editor/Undo/CEditLinkCommand.cpp @@ -0,0 +1,50 @@ +#include "CEditLinkCommand.h" + +CEditLinkCommand::CEditLinkCommand(CWorldEditor *pEditor, CLink *pLink, CLink NewLink) + : IUndoCommand("Edit Link") + , mpEditor(pEditor) + , mpEditLink(pLink) + , mOldLink(*pLink) + , mNewLink(NewLink) +{ + mOldSenderIndex = pLink->SenderIndex(); + mOldReceiverIndex = pLink->ReceiverIndex(); + mAffectedInstances << pLink->Sender(); + if (pLink->Receiver() != pLink->Sender()) mAffectedInstances << pLink->Receiver(); + if (NewLink.Sender() != pLink->Sender()) mAffectedInstances << NewLink.Sender(); + if (NewLink.Receiver() != pLink->Receiver()) mAffectedInstances << NewLink.Receiver(); +} + +void CEditLinkCommand::undo() +{ + if (mOldLink.Sender() != mNewLink.Sender()) + { + mNewLink.Sender()->RemoveLink(eOutgoing, mpEditLink); + mOldLink.Sender()->AddLink(eOutgoing, mpEditLink, mOldSenderIndex); + } + if (mOldLink.Receiver() != mNewLink.Receiver()) + { + mNewLink.Receiver()->RemoveLink(eIncoming, mpEditLink); + mOldLink.Receiver()->AddLink(eIncoming, mpEditLink, mOldReceiverIndex); + } + + *mpEditLink = mOldLink; + mpEditor->OnLinksModified(mAffectedInstances); +} + +void CEditLinkCommand::redo() +{ + if (mOldLink.Sender() != mNewLink.Sender()) + { + mOldLink.Sender()->RemoveLink(eOutgoing, mpEditLink); + mNewLink.Sender()->AddLink(eOutgoing, mpEditLink); + } + if (mOldLink.Receiver() != mNewLink.Receiver()) + { + mOldLink.Receiver()->RemoveLink(eIncoming, mpEditLink); + mNewLink.Receiver()->AddLink(eIncoming, mpEditLink); + } + + *mpEditLink = mNewLink; + mpEditor->OnLinksModified(mAffectedInstances); +} diff --git a/src/Editor/Undo/CEditLinkCommand.h b/src/Editor/Undo/CEditLinkCommand.h new file mode 100644 index 00000000..901c1457 --- /dev/null +++ b/src/Editor/Undo/CEditLinkCommand.h @@ -0,0 +1,27 @@ +#ifndef CEDITLINKCOMMAND_H +#define CEDITLINKCOMMAND_H + +#include "IUndoCommand.h" +#include "Editor/WorldEditor/CWorldEditor.h" +#include + +class CEditLinkCommand : public IUndoCommand +{ + CWorldEditor *mpEditor; + CLink *mpEditLink; + + CLink mOldLink; + CLink mNewLink; + u32 mOldSenderIndex; + u32 mOldReceiverIndex; + + QList mAffectedInstances; + +public: + CEditLinkCommand(CWorldEditor *pEditor, CLink *pLink, CLink NewLink); + void undo(); + void redo(); + bool AffectsCleanState() const { return true; } +}; + +#endif // CEDITLINKCOMMAND_H diff --git a/src/Editor/Undo/UndoCommands.h b/src/Editor/Undo/UndoCommands.h index 980f9245..a1066b5a 100644 --- a/src/Editor/Undo/UndoCommands.h +++ b/src/Editor/Undo/UndoCommands.h @@ -15,6 +15,10 @@ #include "CResizeScriptArrayCommand.h" #include "CChangeLayerCommand.h" +#include "CAddLinkCommand.h" +#include "CDeleteLinksCommand.h" +#include "CEditLinkCommand.h" + #include "EUndoCommand.h" #endif // UNDOCOMMANDS diff --git a/src/Editor/WorldEditor/CLinkDialog.cpp b/src/Editor/WorldEditor/CLinkDialog.cpp index 9bf7acee..5d3b2cea 100644 --- a/src/Editor/WorldEditor/CLinkDialog.cpp +++ b/src/Editor/WorldEditor/CLinkDialog.cpp @@ -2,6 +2,9 @@ #include "ui_CLinkDialog.h" #include "CSelectInstanceDialog.h" #include "CStateMessageModel.h" +#include "CWorldEditor.h" +#include "Editor/Undo/CAddLinkCommand.h" +#include "Editor/Undo/CEditLinkCommand.h" #include CLinkDialog::CLinkDialog(CWorldEditor *pEditor, QWidget *pParent /*= 0*/) @@ -13,6 +16,7 @@ CLinkDialog::CLinkDialog(CWorldEditor *pEditor, QWidget *pParent /*= 0*/) , mpReceiver(nullptr) , mSenderStateModel(CStateMessageModel::eStates, this) , mReceiverMessageModel(CStateMessageModel::eMessages, this) + , mpEditLink(nullptr) { ui->setupUi(this); ui->SenderStateComboBox->setModel(&mSenderStateModel); @@ -44,6 +48,29 @@ void CLinkDialog::showEvent(QShowEvent *) SetReceiverNameLabel(); } +void CLinkDialog::NewLink(CScriptObject *pSender, CScriptObject *pReceiver) +{ + mpEditLink = nullptr; + SetSender(pSender); + SetReceiver(pReceiver); + if (pSender) ui->SenderStateComboBox->setCurrentIndex(0); + if (pReceiver) ui->ReceiverMessageComboBox->setCurrentIndex(0); +} + +void CLinkDialog::EditLink(CLink *pLink) +{ + mpEditLink = pLink; + CScriptObject *pSender = pLink->Sender(); + CScriptObject *pReceiver = pLink->Receiver(); + SetSender(pSender); + SetReceiver(pReceiver); + + if (pSender) + ui->SenderStateComboBox->setCurrentIndex(mSenderStateModel.StateIndex(pLink->State())); + if (pReceiver) + ui->ReceiverMessageComboBox->setCurrentIndex(mReceiverMessageModel.MessageIndex(pLink->Message())); +} + void CLinkDialog::SetMaster(CMasterTemplate *pMaster) { if (mpMaster != pMaster) @@ -99,7 +126,7 @@ u32 CLinkDialog::State() const u32 CLinkDialog::Message() const { - return mReceiverMessageModel.State(ui->ReceiverMessageComboBox->currentIndex()); + return mReceiverMessageModel.Message(ui->ReceiverMessageComboBox->currentIndex()); } void CLinkDialog::SetSenderNameLabel() @@ -127,6 +154,25 @@ void CLinkDialog::SetReceiverNameLabel() } // ************ PUBLIC SLOTS ************ +void CLinkDialog::accept() +{ + CLink Link(mpEditor->ActiveArea(), State(), Message(), Sender()->InstanceID(), Receiver()->InstanceID()); + + if (!mpEditLink) + { + CAddLinkCommand *pCmd = new CAddLinkCommand(mpEditor, Link); + mpEditor->UndoStack()->push(pCmd); + } + + else if (Link != *mpEditLink) + { + CEditLinkCommand *pCmd = new CEditLinkCommand(mpEditor, mpEditLink, Link); + mpEditor->UndoStack()->push(pCmd); + } + + QDialog::accept(); +} + void CLinkDialog::OnSwapClicked() { CScriptObject *pSender = mpReceiver; diff --git a/src/Editor/WorldEditor/CLinkDialog.h b/src/Editor/WorldEditor/CLinkDialog.h index 14d7d299..9ca71e9a 100644 --- a/src/Editor/WorldEditor/CLinkDialog.h +++ b/src/Editor/WorldEditor/CLinkDialog.h @@ -2,13 +2,14 @@ #define CLINKDIALOG_H #include "CStateMessageModel.h" -#include "CWorldEditor.h" #include namespace Ui { class CLinkDialog; } +class CWorldEditor; + class CLinkDialog : public QDialog { Q_OBJECT @@ -17,6 +18,7 @@ class CLinkDialog : public QDialog CMasterTemplate *mpMaster; CScriptObject *mpSender; CScriptObject *mpReceiver; + CLink *mpEditLink; CStateMessageModel mSenderStateModel; CStateMessageModel mReceiverMessageModel; @@ -29,6 +31,9 @@ public: void resizeEvent(QResizeEvent *); void showEvent(QShowEvent *); + void NewLink(CScriptObject *pSender, CScriptObject *pReceiver); + void EditLink(CLink *pLink); + void SetMaster(CMasterTemplate *pMaster); void SetSender(CScriptObject *pSender); void SetReceiver(CScriptObject *pReceiver); @@ -42,6 +47,7 @@ public: inline CScriptObject* Receiver() const { return mpReceiver; } public slots: + void accept(); void OnSwapClicked(); void OnPickFromViewportClicked(); void OnPickModeClick(const SRayIntersection& rkHit, QMouseEvent *pEvent); diff --git a/src/Editor/WorldEditor/CLinkModel.cpp b/src/Editor/WorldEditor/CLinkModel.cpp index d80a38c2..572a1130 100644 --- a/src/Editor/WorldEditor/CLinkModel.cpp +++ b/src/Editor/WorldEditor/CLinkModel.cpp @@ -15,7 +15,7 @@ void CLinkModel::SetObject(CScriptObject *pObj) emit layoutChanged(); } -void CLinkModel::SetConnectionType(EConnectionType type) +void CLinkModel::SetConnectionType(ELinkType type) { mType = type; emit layoutChanged(); @@ -24,12 +24,7 @@ void CLinkModel::SetConnectionType(EConnectionType type) int CLinkModel::rowCount(const QModelIndex&) const { if (mpObject) - { - if (mType == eIncoming) - return mpObject->NumInLinks(); - else - return mpObject->NumOutLinks(); - } + return mpObject->NumLinks(mType); else return 0; } @@ -45,21 +40,22 @@ QVariant CLinkModel::data(const QModelIndex &index, int role) const else if ((role == Qt::DisplayRole) || (role == Qt::ToolTipRole)) { - SLink link = (mType == eIncoming ? mpObject->InLink(index.row()) : mpObject->OutLink(index.row())); + CLink *pLink = mpObject->Link(mType, index.row()); switch (index.column()) { case 0: // Column 0 - Target Object { - CScriptObject *pTargetObj = mpObject->Area()->GetInstanceByID(link.ObjectID); + u32 TargetID = (mType == eIncoming ? pLink->SenderID() : pLink->ReceiverID()); + CScriptObject *pTarget = mpObject->Area()->GetInstanceByID(TargetID); - if (pTargetObj) { - QString ObjType = QString("[%1] ").arg(UICommon::ToQString(pTargetObj->Template()->Name())); - return ObjType + UICommon::ToQString(pTargetObj->InstanceName()); + if (pTarget) { + QString ObjType = QString("[%1] ").arg(UICommon::ToQString(pTarget->Template()->Name())); + return ObjType + UICommon::ToQString(pTarget->InstanceName()); } else { - QString strID = QString::number(link.ObjectID, 16); + QString strID = QString::number(TargetID, 16); while (strID.length() < 8) strID = "0" + strID; return QString("External: 0x") + strID; } @@ -67,13 +63,13 @@ QVariant CLinkModel::data(const QModelIndex &index, int role) const case 1: // Column 1 - State { - TString StateName = mpObject->MasterTemplate()->StateByID(link.State).Name; + TString StateName = mpObject->MasterTemplate()->StateByID(pLink->State()).Name; return UICommon::ToQString(StateName); } case 2: // Column 2 - Message { - TString MessageName = mpObject->MasterTemplate()->MessageByID(link.Message).Name; + TString MessageName = mpObject->MasterTemplate()->MessageByID(pLink->Message()).Name; return UICommon::ToQString(MessageName); } diff --git a/src/Editor/WorldEditor/CLinkModel.h b/src/Editor/WorldEditor/CLinkModel.h index 16bf26a2..6d42f16d 100644 --- a/src/Editor/WorldEditor/CLinkModel.h +++ b/src/Editor/WorldEditor/CLinkModel.h @@ -8,19 +8,13 @@ class CLinkModel : public QAbstractTableModel { Q_OBJECT -public: - enum EConnectionType { - eIncoming, eOutgoing - }; - -private: CScriptObject *mpObject; - EConnectionType mType; + ELinkType mType; public: explicit CLinkModel(QObject *pParent = 0); void SetObject(CScriptObject *pObj); - void SetConnectionType(EConnectionType type); + void SetConnectionType(ELinkType type); int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; diff --git a/src/Editor/WorldEditor/CStateMessageModel.h b/src/Editor/WorldEditor/CStateMessageModel.h index 87cadd8f..0b95a115 100644 --- a/src/Editor/WorldEditor/CStateMessageModel.h +++ b/src/Editor/WorldEditor/CStateMessageModel.h @@ -91,7 +91,33 @@ public: endResetModel(); } - void SetScriptTemplate(CScriptTemplate *pScript) + u32 StateIndex(u32 StateID) const + { + if (mType == eMessages) return -1; + + for (int iState = 0; iState < mEntries.size(); iState++) + { + if (mEntries[iState].ID == StateID) + return iState; + } + + return -1; + } + + u32 MessageIndex(u32 MessageID) const + { + if (mType == eStates) return -1; + + for (int iMsg = 0; iMsg < mEntries.size(); iMsg++) + { + if (mEntries[iMsg].ID == MessageID) + return iMsg; + } + + return -1; + } + + inline void SetScriptTemplate(CScriptTemplate *pScript) { mpScript = pScript; } diff --git a/src/Editor/WorldEditor/CWorldEditor.cpp b/src/Editor/WorldEditor/CWorldEditor.cpp index 8f080493..b67d468b 100644 --- a/src/Editor/WorldEditor/CWorldEditor.cpp +++ b/src/Editor/WorldEditor/CWorldEditor.cpp @@ -31,6 +31,7 @@ CWorldEditor::CWorldEditor(QWidget *parent) : mpArea = nullptr; mpWorld = nullptr; + mpLinkDialog = new CLinkDialog(this, this); mpPoiDialog = nullptr; mGizmoHovering = false; mGizmoTransforming = false; @@ -108,6 +109,7 @@ void CWorldEditor::closeEvent(QCloseEvent *pEvent) if (ShouldClose) { mUndoStack.clear(); + mpLinkDialog->close(); if (mpPoiDialog) mpPoiDialog->close(); @@ -176,6 +178,9 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex) CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpArea->Version()); ui->InstancesTabContents->SetMaster(pMaster); + // Set up dialogs + mpLinkDialog->SetMaster(pMaster); + // Set window title CStringTable *pWorldNameTable = mpWorld->GetWorldName(); TWideString WorldName = pWorldNameTable ? pWorldNameTable->GetString("ENGL", 0) : "[Untitled World]"; @@ -248,6 +253,17 @@ bool CWorldEditor::Save() } } +void CWorldEditor::OnLinksModified(const QList& rkInstances) +{ + foreach (CScriptObject *pInstance, rkInstances) + { + CScriptNode *pNode = mScene.NodeForObject(pInstance); + pNode->LinksModified(); + } + + emit InstanceLinksModified(rkInstances); +} + void CWorldEditor::OnPropertyModified(IProperty *pProp) { bool EditorProperty = false; diff --git a/src/Editor/WorldEditor/CWorldEditor.h b/src/Editor/WorldEditor/CWorldEditor.h index 8809b70c..25ffc010 100644 --- a/src/Editor/WorldEditor/CWorldEditor.h +++ b/src/Editor/WorldEditor/CWorldEditor.h @@ -1,6 +1,7 @@ #ifndef CWORLDEDITOR_H #define CWORLDEDITOR_H +#include "CLinkDialog.h" #include "CPoiMapEditDialog.h" #include "Editor/INodeEditor.h" #include "Editor/CGizmo.h" @@ -35,6 +36,7 @@ class CWorldEditor : public INodeEditor TResPtr mpArea; QTimer mRefreshTimer; + CLinkDialog *mpLinkDialog; CPoiMapEditDialog *mpPoiDialog; public: @@ -47,9 +49,11 @@ public: inline CGameArea* ActiveArea() const { return mpArea; } inline EGame CurrentGame() const { return mpArea ? mpArea->Version() : eUnknownVersion; } + inline CLinkDialog* LinkDialog() const { return mpLinkDialog; } public slots: bool Save(); + void OnLinksModified(const QList& rkInstances); void OnPropertyModified(IProperty *pProp); void SetSelectionActive(bool Active); void SetSelectionInstanceNames(const QString& rkNewName, bool IsDone); @@ -101,6 +105,7 @@ signals: void LayersModified(); void InstancesLayerAboutToChange(); void InstancesLayerChanged(const QList& rkInstanceList); + void InstanceLinksModified(const QList& rkInstances); void PropertyModified(IProperty *pProp, bool IsEditorProperty); }; diff --git a/src/Editor/WorldEditor/WModifyTab.cpp b/src/Editor/WorldEditor/WModifyTab.cpp index c03156f1..28c34acc 100644 --- a/src/Editor/WorldEditor/WModifyTab.cpp +++ b/src/Editor/WorldEditor/WModifyTab.cpp @@ -3,6 +3,7 @@ #include "CLinkDialog.h" #include "CWorldEditor.h" +#include "Editor/Undo/UndoCommands.h" #include #include @@ -20,10 +21,9 @@ WModifyTab::WModifyTab(QWidget *pParent) ui->PropertyView->header()->setSectionResizeMode(1, QHeaderView::Fixed); mpInLinkModel = new CLinkModel(this); - mpInLinkModel->SetConnectionType(CLinkModel::eIncoming); + mpInLinkModel->SetConnectionType(eIncoming); mpOutLinkModel = new CLinkModel(this); - mpOutLinkModel->SetConnectionType(CLinkModel::eOutgoing); - mpLinkDialog = nullptr; + mpOutLinkModel->SetConnectionType(eOutgoing); ui->InLinksTableView->setModel(mpInLinkModel); ui->OutLinksTableView->setModel(mpOutLinkModel); @@ -31,10 +31,14 @@ WModifyTab::WModifyTab(QWidget *pParent) ui->OutLinksTableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); connect(ui->InLinksTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnLinkTableDoubleClick(QModelIndex))); connect(ui->OutLinksTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnLinkTableDoubleClick(QModelIndex))); + connect(ui->InLinksTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnIncomingLinksSelectionModified())); + connect(ui->OutLinksTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnOutgoingLinksSelectionModified())); connect(ui->AddOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnAddOutgoingLinkClicked())); connect(ui->AddIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnAddIncomingLinkClicked())); connect(ui->DeleteOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnDeleteOutgoingLinkClicked())); connect(ui->DeleteIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnDeleteIncomingLinkClicked())); + connect(ui->EditOutgoingConnectionButton, SIGNAL(clicked()), this, SLOT(OnEditOutgoingLinkClicked())); + connect(ui->EditIncomingConnectionButton, SIGNAL(clicked()), this, SLOT(OnEditIncomingLinkClicked())); ClearUI(); } @@ -48,8 +52,8 @@ void WModifyTab::SetEditor(CWorldEditor *pEditor) { mpWorldEditor = pEditor; ui->PropertyView->SetEditor(mpWorldEditor); - connect(mpWorldEditor, SIGNAL(Closed()), this, SLOT(OnWorldEditorClosed())); connect(mpWorldEditor, SIGNAL(SelectionTransformed()), this, SLOT(OnWorldSelectionTransformed())); + connect(mpWorldEditor, SIGNAL(InstanceLinksModified(const QList&)), this, SLOT(OnInstanceLinksModified(const QList&))); } void WModifyTab::GenerateUI(QList& Selection) @@ -69,9 +73,12 @@ void WModifyTab::GenerateUI(QList& Selection) // Set up UI ui->PropertyView->SetInstance(pObj); + ui->LightGroupBox->hide(); + + ui->InLinksTableView->clearSelection(); + ui->OutLinksTableView->clearSelection(); mpInLinkModel->SetObject(pObj); mpOutLinkModel->SetObject(pObj); - ui->LightGroupBox->hide(); } } } @@ -88,36 +95,21 @@ void WModifyTab::ClearUI() mpSelectedNode = nullptr; } -void WModifyTab::CreateLinkDialog() -{ - if (!mpLinkDialog) - { - mpLinkDialog = new CLinkDialog(mpWorldEditor, this); - - if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) - { - CScriptNode *pScript = static_cast(mpSelectedNode); - mpLinkDialog->SetMaster(pScript->Object()->MasterTemplate()); - } - - connect(mpLinkDialog, SIGNAL(accepted()), this, SLOT(OnLinkDialogAccept())); - connect(mpLinkDialog, SIGNAL(rejected()), this, SLOT(OnLinkDialogReject())); - } -} - -void WModifyTab::DeleteLinkDialog() -{ - if (mpLinkDialog) - { - delete mpLinkDialog; - mpLinkDialog = nullptr; - } -} - // ************ PUBLIC SLOTS ************ -void WModifyTab::OnWorldEditorClosed() +void WModifyTab::OnInstanceLinksModified(const QList& rkInstances) { - DeleteLinkDialog(); + if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) + { + CScriptObject *pInstance = static_cast(mpSelectedNode)->Object(); + + if (pInstance && rkInstances.contains(pInstance)) + { + mpInLinkModel->layoutChanged(); + mpOutLinkModel->layoutChanged(); + ui->InLinksTableView->clearSelection(); + ui->OutLinksTableView->clearSelection(); + } + } } void WModifyTab::OnWorldSelectionTransformed() @@ -125,20 +117,26 @@ void WModifyTab::OnWorldSelectionTransformed() ui->PropertyView->UpdateEditorProperties(QModelIndex()); } +void WModifyTab::OnOutgoingLinksSelectionModified() +{ + u32 NumSelectedRows = ui->OutLinksTableView->selectionModel()->selectedRows().size(); + ui->EditOutgoingConnectionButton->setEnabled(NumSelectedRows == 1); +} + +void WModifyTab::OnIncomingLinksSelectionModified() +{ + u32 NumSelectedRows = ui->InLinksTableView->selectionModel()->selectedRows().size(); + ui->EditIncomingConnectionButton->setEnabled(NumSelectedRows == 1); +} + void WModifyTab::OnAddOutgoingLinkClicked() { if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) { CScriptObject *pInst = static_cast(mpSelectedNode)->Object(); - CreateLinkDialog(); - - if (mpLinkDialog->Sender() != pInst) - { - mpLinkDialog->SetSender(pInst); - mpLinkDialog->SetReceiver(nullptr); - } - - mpLinkDialog->show(); + CLinkDialog *pDialog = mpWorldEditor->LinkDialog(); + pDialog->NewLink(pInst, nullptr); + pDialog->show(); } } @@ -147,34 +145,82 @@ void WModifyTab::OnAddIncomingLinkClicked() if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) { CScriptObject *pInst = static_cast(mpSelectedNode)->Object(); - CreateLinkDialog(); - - if (mpLinkDialog->Receiver() != pInst) - { - mpLinkDialog->SetSender(nullptr); - mpLinkDialog->SetReceiver(pInst); - } - - mpLinkDialog->show(); + CLinkDialog *pDialog = mpWorldEditor->LinkDialog(); + pDialog->NewLink(nullptr, pInst); + pDialog->show(); } } void WModifyTab::OnDeleteOutgoingLinkClicked() { + if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) + { + QModelIndexList SelectedIndices = ui->OutLinksTableView->selectionModel()->selectedRows(); + + if (!SelectedIndices.isEmpty()) + { + QVector Indices; + + for (int iIdx = 0; iIdx < SelectedIndices.size(); iIdx++) + Indices << SelectedIndices[iIdx].row(); + + CScriptObject *pInst = static_cast(mpSelectedNode)->Object(); + CDeleteLinksCommand *pCmd = new CDeleteLinksCommand(mpWorldEditor, pInst, eOutgoing, Indices); + mpWorldEditor->UndoStack()->push(pCmd); + } + } } void WModifyTab::OnDeleteIncomingLinkClicked() { + if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) + { + QModelIndexList SelectedIndices = ui->InLinksTableView->selectionModel()->selectedRows(); + + if (!SelectedIndices.isEmpty()) + { + QVector Indices; + + for (int iIdx = 0; iIdx < SelectedIndices.size(); iIdx++) + Indices << SelectedIndices[iIdx].row(); + + CScriptObject *pInst = static_cast(mpSelectedNode)->Object(); + CDeleteLinksCommand *pCmd = new CDeleteLinksCommand(mpWorldEditor, pInst, eIncoming, Indices); + mpWorldEditor->UndoStack()->push(pCmd); + } + } } -void WModifyTab::OnLinkDialogAccept() +void WModifyTab::OnEditOutgoingLinkClicked() { - DeleteLinkDialog(); + if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) + { + QModelIndexList SelectedIndices = ui->OutLinksTableView->selectionModel()->selectedRows(); + + if (SelectedIndices.size() == 1) + { + CScriptObject *pInst = static_cast(mpSelectedNode)->Object(); + CLinkDialog *pDialog = mpWorldEditor->LinkDialog(); + pDialog->EditLink(pInst->Link(eOutgoing, SelectedIndices.front().row())); + pDialog->show(); + } + } } -void WModifyTab::OnLinkDialogReject() +void WModifyTab::OnEditIncomingLinkClicked() { - DeleteLinkDialog(); + if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) + { + QModelIndexList SelectedIndices = ui->InLinksTableView->selectionModel()->selectedRows(); + + if (SelectedIndices.size() == 1) + { + CScriptObject *pInst = static_cast(mpSelectedNode)->Object(); + CLinkDialog *pDialog = mpWorldEditor->LinkDialog(); + pDialog->EditLink(pInst->Link(eIncoming, SelectedIndices.front().row())); + pDialog->show(); + } + } } // ************ PRIVATE SLOTS ************ @@ -184,14 +230,14 @@ void WModifyTab::OnLinkTableDoubleClick(QModelIndex Index) { // The link table will only be visible if the selected node is a script node CScriptNode *pNode = static_cast(mpSelectedNode); - SLink Link; + u32 InstanceID; if (sender() == ui->InLinksTableView) - Link = pNode->Object()->InLink(Index.row()); + InstanceID = pNode->Object()->Link(eIncoming, Index.row())->SenderID(); else if (sender() == ui->OutLinksTableView) - Link = pNode->Object()->OutLink(Index.row()); + InstanceID = pNode->Object()->Link(eOutgoing, Index.row())->ReceiverID(); - CScriptNode *pLinkedNode = pNode->Scene()->ScriptNodeByID(Link.ObjectID); + CScriptNode *pLinkedNode = pNode->Scene()->ScriptNodeByID(InstanceID); if (pLinkedNode) { diff --git a/src/Editor/WorldEditor/WModifyTab.h b/src/Editor/WorldEditor/WModifyTab.h index f83eb6e7..dc0480f8 100644 --- a/src/Editor/WorldEditor/WModifyTab.h +++ b/src/Editor/WorldEditor/WModifyTab.h @@ -28,28 +28,25 @@ class WModifyTab : public QWidget CLinkModel *mpInLinkModel; CLinkModel *mpOutLinkModel; - CLinkDialog *mpLinkDialog; - public: explicit WModifyTab(QWidget *pParent = 0); ~WModifyTab(); void SetEditor(CWorldEditor *pEditor); void GenerateUI(QList& Selection); void ClearUI(); - void CreateLinkDialog(); - void DeleteLinkDialog(); public slots: - void OnWorldEditorClosed(); + void OnInstanceLinksModified(const QList& rkInstances); void OnWorldSelectionTransformed(); + void OnOutgoingLinksSelectionModified(); + void OnIncomingLinksSelectionModified(); void OnAddOutgoingLinkClicked(); void OnAddIncomingLinkClicked(); void OnDeleteOutgoingLinkClicked(); void OnDeleteIncomingLinkClicked(); - void OnLinkDialogAccept(); - void OnLinkDialogReject(); - + void OnEditOutgoingLinkClicked(); + void OnEditIncomingLinkClicked(); private: Ui::WModifyTab *ui; diff --git a/src/Editor/WorldEditor/WModifyTab.ui b/src/Editor/WorldEditor/WModifyTab.ui index cd39aece..6884c37e 100644 --- a/src/Editor/WorldEditor/WModifyTab.ui +++ b/src/Editor/WorldEditor/WModifyTab.ui @@ -127,11 +127,14 @@ - QAbstractScrollArea::AdjustToContents + QAbstractScrollArea::AdjustIgnored QAbstractItemView::NoEditTriggers + + QAbstractItemView::SelectRows + QAbstractItemView::ScrollPerPixel @@ -173,6 +176,28 @@ + + + + false + + + + 0 + 0 + + + + + 30 + 16777215 + + + + Edit + + + @@ -205,6 +230,18 @@ + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + 75 @@ -237,6 +274,22 @@ + + + + false + + + + 39 + 16777215 + + + + Edit + + +