2010-05-06 00:08:41 +00:00
|
|
|
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
|
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as
|
|
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
#include "stdpch.h"
|
|
|
|
//
|
|
|
|
#include "editor.h"
|
|
|
|
#include "tool_select_move.h"
|
|
|
|
#include "entity_sorter.h"
|
|
|
|
//
|
|
|
|
#include "../interface_v3/interface_manager.h"
|
|
|
|
#include "../client_cfg.h"
|
|
|
|
#include "../entities.h"
|
|
|
|
#include "../global.h"
|
|
|
|
#include "displayer_visual.h"
|
|
|
|
#include "../sheet_manager.h"
|
|
|
|
|
|
|
|
#include "dmc/com_lua_module.h"
|
|
|
|
//
|
|
|
|
#include "nel/misc/matrix.h"
|
|
|
|
|
|
|
|
|
|
|
|
using namespace NLMISC;
|
|
|
|
|
|
|
|
|
|
|
|
namespace R2
|
|
|
|
{
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
CToolSelectMove::CToolSelectMove()
|
|
|
|
{
|
|
|
|
_MouseX = -1;
|
|
|
|
_MouseY = -1;
|
|
|
|
_DeltaX = 0;
|
|
|
|
_DeltaY = 0;
|
|
|
|
_ValidPos = false;
|
|
|
|
_State = Idle;
|
|
|
|
_Moved = false;
|
|
|
|
_Duplicating = false;
|
|
|
|
_InitiallyAccessible = false;
|
|
|
|
_InitiallyValidShape = true;
|
|
|
|
_DeltaAnchor.set(0, 0, 0);
|
|
|
|
_StartPos.set(0, 0, 0);
|
|
|
|
_LastValidPos.set(0, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
bool CToolSelectMove::checkAdditionnalRoomLeftFor(CInstance &instance)
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_checkAdditionnalRoomLeftFor)
|
|
|
|
CLuaObject &luaProj = instance.getLuaProjection();
|
|
|
|
CLuaState &ls = getEditor().getLua();
|
|
|
|
CLuaStackRestorer lsr(&ls, 0);
|
|
|
|
// check ai & static cost : if they are too big, can't create the duplicate
|
|
|
|
if (!luaProj.callMethodByNameNoThrow("getAiCost", 0, 1)
|
|
|
|
|| !ls.isNumber(-1))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
uint aiCost = (uint) ls.toNumber(-1);
|
|
|
|
ls.pop();
|
|
|
|
if (!luaProj.callMethodByNameNoThrow("getStaticObjectCost", 0, 1))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
uint staticCost = (uint) ls.toNumber(-1);
|
|
|
|
ls.pop();
|
|
|
|
if (!getEditor().verifyRoomLeft(aiCost, staticCost))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
CInstance *CToolSelectMove::createGhost(CInstance &instance)
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_createGhost)
|
|
|
|
CLuaState &ls = getEditor().getLua();
|
|
|
|
// copy then do a local paste
|
|
|
|
CLuaStackRestorer lsr(&ls, 0);
|
|
|
|
//
|
|
|
|
CLuaObject &luaProj = instance.getLuaProjection();
|
|
|
|
CLuaObject &classDef = instance.getClass();
|
|
|
|
if (luaProj.callMethodByNameNoThrow("copy", 0, 1))
|
|
|
|
{
|
|
|
|
// now we got a table that is an exact (canonical) copy of the original object, with the
|
|
|
|
// same instance ids..
|
|
|
|
// prepare for new insertion by renaming these instance id's (which 'newCopy' does)
|
|
|
|
if (classDef["newCopy"].callNoThrow(1, 1))
|
|
|
|
{
|
|
|
|
// now, insert the new copy as a ghost in the new scene
|
|
|
|
if (classDef["pasteGhost"].callNoThrow(1, 1))
|
|
|
|
{
|
|
|
|
|
|
|
|
CLuaObject ghost(ls); // pop the ghost from stack
|
|
|
|
CInstance *newInst = getEditor().getInstanceFromId(ghost["InstanceId"].toString());
|
|
|
|
if (newInst)
|
|
|
|
{
|
|
|
|
if (!newInst->getGhost())
|
|
|
|
{
|
|
|
|
nlwarning("When duplicating an object using the 'select/move' tool, temporary duplicate should be inserted \
|
|
|
|
as a ghost in the scene, removing object...");
|
|
|
|
getEditor().getDMC().requestEraseNode(newInst->getId(), "", -1);
|
|
|
|
}
|
|
|
|
// set the flag so that the cost of this object isn't taken in account in the displayed quotas
|
|
|
|
newInst->getLuaProjection()["User"].setValue("GhostDuplicate", true);
|
|
|
|
getEditor().setSelectedInstance(newInst);
|
|
|
|
newInst->getDisplayerVisual()->setDisplayFlag(CDisplayerVisual::FlagHideActivities, true);
|
|
|
|
nlwarning("CToolSelectMove: beginning duplicate with instance with id %s", newInst->getId().c_str());
|
|
|
|
// show in "frozen" state
|
|
|
|
{
|
|
|
|
/*CObjectNumber *numberValue = new CObjectNumber(2); // 2 = frozen state
|
|
|
|
getEditor().getDMC().requestSetNode(newInst->getId(), "DisplayMode", numberValue);
|
|
|
|
delete numberValue;
|
|
|
|
*/
|
|
|
|
newInst->getDisplayerVisual()->setDisplayMode(CDisplayerVisual::DisplayModeFrozen);
|
|
|
|
getEditor().getEntitySorter()->clipEntitiesByDist();
|
|
|
|
return newInst;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
void CToolSelectMove::beginAction(CInstance &instance)
|
|
|
|
{
|
|
|
|
_DeltaAnchor.set(0, 0, 0);
|
|
|
|
//H_AUTO(R2_CToolSelectMove_beginAction)
|
|
|
|
_StartTime = T1;
|
|
|
|
CDisplayerVisual *dv = instance.getDisplayerVisual();
|
|
|
|
if(!dv)
|
|
|
|
{
|
|
|
|
cancel();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_InitiallyAccessible = dv->isAccessible();
|
|
|
|
_InitiallyValidShape = true;
|
|
|
|
CDisplayerVisual *parentDV = dv->getParent();
|
|
|
|
if (parentDV && parentDV->isGroup())
|
|
|
|
{
|
|
|
|
_InitiallyAccessible = _InitiallyAccessible && parentDV->isAccessible();
|
|
|
|
_InitiallyValidShape = parentDV->isValidShape();
|
|
|
|
}
|
|
|
|
//
|
|
|
|
getMousePos(_MouseX, _MouseY);
|
|
|
|
// must work with eval link point because world pos may be realy far from link pos (for regions)
|
|
|
|
ISelectableObject::TSelectionType selectionType = dv->getSelectionType();
|
|
|
|
switch(selectionType)
|
|
|
|
{
|
|
|
|
case ISelectableObject::GroundProjected:
|
|
|
|
{
|
|
|
|
// because displayed instance is projected on scene (like roads, regions ...)
|
|
|
|
// anchor point is scene / mouse ray intersection point
|
|
|
|
CTool::CWorldViewRay worldViewRay;
|
|
|
|
computeWorldViewRay(_MouseX, _MouseY, worldViewRay);
|
|
|
|
CVector inter; // intersection of view ray with landscape
|
|
|
|
_ValidPos = false;
|
|
|
|
switch(computeLandscapeRayIntersection(worldViewRay, inter))
|
|
|
|
{
|
|
|
|
case NoIntersection:
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
case ValidPacsPos:
|
|
|
|
case InvalidPacsPos:
|
|
|
|
_StartPos = inter;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nlassert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ISelectableObject::LocalSelectBox:
|
|
|
|
case ISelectableObject::WorldSelectBox:
|
|
|
|
dv->snapToGround();
|
|
|
|
_StartPos = dv->evalLinkPoint(false);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nlassert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_DeltaAnchor = _StartPos - dv->getWorldPos();
|
|
|
|
// nico : tmp fix to have correct z until luaSnapToGround is fixed
|
|
|
|
if (instance.getEntity())
|
|
|
|
{
|
|
|
|
instance.getEntity()->snapToGround();
|
|
|
|
_StartPos.z = instance.getEntity()->pos().z;
|
|
|
|
}
|
|
|
|
//nlwarning("starting to move instance at pos %s", NLMISC::toString(_StartPos.asVector()).c_str());
|
|
|
|
const NL3D::CFrustum &fru = MainCam.getFrustum();
|
|
|
|
CVector posInCam = MainCam.getMatrix().inverted() * _StartPos;
|
|
|
|
CVector projectedPos = fru.projectZ(posInCam);
|
|
|
|
/** Compute delta in screen space between instance position and the click position
|
|
|
|
* We maintain that delta when the instance moves
|
|
|
|
*/
|
|
|
|
uint32 w, h;
|
|
|
|
getScreenSize(w, h);
|
|
|
|
if (!isMouseOnWorldMap())
|
|
|
|
{
|
|
|
|
_DeltaX = _MouseX - (sint32) (w * projectedPos.x);
|
|
|
|
_DeltaY = _MouseY - (sint32) (h * projectedPos.y);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_DeltaX = 0;
|
|
|
|
_DeltaY = 0;
|
|
|
|
}
|
|
|
|
///////////////////////
|
|
|
|
// TMP TMP TMP
|
|
|
|
_PosRefX = (sint32) (w * projectedPos.x);
|
|
|
|
_PosRefY = (sint32) (h * projectedPos.y);
|
|
|
|
|
|
|
|
//
|
|
|
|
/*
|
|
|
|
CVector debugPos;
|
|
|
|
CVector rayOrigin;
|
|
|
|
CVector rayDir;
|
|
|
|
computeWorldViewRay(_MouseX - _DeltaX, _MouseY - _DeltaY, rayOrigin, rayDir);
|
|
|
|
computeLandscapeRayIntersection(rayOrigin, rayOrigin + 1000.f * rayDir, debugPos);
|
|
|
|
*/
|
|
|
|
//nlwarning("ref point = %s", NLMISC::toString(debugPos).c_str());
|
|
|
|
//
|
|
|
|
_RefX = _MouseX - _DeltaX;
|
|
|
|
_RefY = _MouseY - _DeltaY;
|
|
|
|
//
|
|
|
|
_MouseRefX = _MouseX;
|
|
|
|
_MouseRefY = _MouseY;
|
|
|
|
///////////////////////
|
|
|
|
_ValidPos = false;
|
|
|
|
_Moved = false;
|
|
|
|
|
|
|
|
if (isShiftDown())
|
|
|
|
{
|
|
|
|
// see in advance if there will be room left to create a duplicate for this object
|
|
|
|
if (!checkAdditionnalRoomLeftFor(instance))
|
|
|
|
{
|
|
|
|
_State = Idle;
|
|
|
|
getEditor().setCurrentTool(NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_Duplicating = true;
|
|
|
|
_ValidPos = true; // valid pos for first frame
|
|
|
|
_FinalPos = _StartPos;
|
|
|
|
_GhostInstance = NULL;
|
|
|
|
}
|
|
|
|
_LastValidPos = _StartPos;
|
|
|
|
dv->setMoveInProgress(true);
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
void CToolSelectMove::cancelAction(CInstance &instance)
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_cancelAction)
|
|
|
|
CDisplayerVisual *dv = instance.getDisplayerVisual();
|
|
|
|
nlassert(dv);
|
|
|
|
dv->setMoveInProgress(false);
|
|
|
|
if (_Moved)
|
|
|
|
{
|
|
|
|
std::string posInstanceId = instance.getPosInstanceId();
|
|
|
|
if (posInstanceId.empty()) return;
|
|
|
|
getEditor().requestRollbackLocalNode(posInstanceId, "");
|
|
|
|
}
|
|
|
|
if (_Duplicating)
|
|
|
|
{
|
|
|
|
// remove the ghost node
|
|
|
|
if (_GhostInstance)
|
|
|
|
{
|
|
|
|
nlassert(_GhostInstance->getGhost()); // should have been inserted as a ghost in the scene
|
|
|
|
//
|
|
|
|
getEditor().getDMC().requestEraseNode(_GhostInstance->getId(), "", -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_Duplicating = false;
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
void CToolSelectMove::commitAction(CInstance &instance)
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_commitAction)
|
|
|
|
CDisplayerVisual *dv = instance.getDisplayerVisual();
|
|
|
|
nlassert(dv);
|
|
|
|
dv->setMoveInProgress(false);
|
|
|
|
if (!_ValidPos)
|
|
|
|
{
|
|
|
|
cancelAction(instance);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_Duplicating && _Moved)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!_GhostInstance)
|
|
|
|
{
|
|
|
|
cancelAction(instance);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!_GhostInstance->getGhost())
|
|
|
|
{
|
|
|
|
cancelAction(instance);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CLuaState &ls = getEditor().getLua();
|
|
|
|
CLuaObject luaProj = _GhostInstance->getLuaProjection();
|
|
|
|
if (!checkAdditionnalRoomLeftFor(*_GhostInstance))
|
|
|
|
{
|
|
|
|
cancelAction(instance);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CLuaStackRestorer lsr(&ls, ls.getTop());
|
|
|
|
// duplicate the ghost, and insert in the scene for real
|
|
|
|
CLuaObject classDef = instance.getClass();
|
|
|
|
std::string oldName = luaProj["Name"].toString(); // keep name, because the newCopy call
|
|
|
|
// will generate a newone, causing indices to increase 2 by 2
|
|
|
|
|
|
|
|
// restore default value for display mode
|
|
|
|
//getEditor().getDMC().requestEraseNode(_GhostInstance->getId(), "DisplayMode", -1);
|
|
|
|
if (_GhostInstance->getDisplayerVisual())
|
|
|
|
{
|
|
|
|
_GhostInstance->getDisplayerVisual()->setDisplayMode(CDisplayerVisual::DisplayModeVisible);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_Moved)
|
|
|
|
{
|
|
|
|
if (luaProj.callMethodByNameNoThrow("copy", 0, 1))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::string posInstanceId = instance.getPosInstanceId();
|
|
|
|
if (!posInstanceId.empty())
|
|
|
|
{
|
|
|
|
getEditor().requestRollbackLocalNode(posInstanceId, ""); // Locally modified coords have
|
|
|
|
// (of a local object ...)
|
|
|
|
// have been read during the copy,
|
|
|
|
// get rid of them...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
getDMC().newAction(CI18N::get("uiR2EDCopyAction") + _GhostInstance->getDisplayName());
|
|
|
|
getEditor().getDMC().requestEraseNode(_GhostInstance->getId(), "", -1);
|
|
|
|
// now, instanciate cannonical copy
|
|
|
|
if (classDef["newCopy"].callNoThrow(1, 1))
|
|
|
|
{
|
|
|
|
// set real pos for the copy & the name
|
|
|
|
CLuaObject newCopy(ls);
|
|
|
|
CLuaObject pos = newCopy["Position"];
|
|
|
|
try
|
|
|
|
{
|
|
|
|
newCopy.setValue("Name", oldName);
|
|
|
|
pos.setValue("x", _FinalPos.x);
|
|
|
|
pos.setValue("y", _FinalPos.y);
|
|
|
|
pos.setValue("z", _FinalPos.z);
|
|
|
|
}
|
2011-06-02 16:31:40 +00:00
|
|
|
catch(const ELuaNotATable &)
|
2010-05-06 00:08:41 +00:00
|
|
|
{
|
|
|
|
nlwarning("Error while setting position of copied object");
|
|
|
|
}
|
|
|
|
// ... and paste for real
|
|
|
|
if (_AutoGroup.getGroupingCandidate())
|
|
|
|
{
|
|
|
|
newCopy.push();
|
|
|
|
std::auto_ptr<CObject> desc(CComLuaModule::getObjectFromLua(ls.getStatePointer()));
|
|
|
|
_AutoGroup.group(desc.get(), _FinalPos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newCopy.push();
|
|
|
|
ls.push(false); // second parameter is for "not a new place"
|
|
|
|
ls.push(instance.getId()); // last param give the original instance id of the object being copied
|
|
|
|
classDef["paste"].callNoThrow(3, 1);
|
|
|
|
getEditor().getDMC().getActionHistoric().endAction();
|
|
|
|
getEditor().getDMC().flushActions();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string posInstanceId = instance.getPosInstanceId();
|
|
|
|
if (!posInstanceId.empty())
|
|
|
|
{
|
|
|
|
getEditor().requestRollbackLocalNode(posInstanceId, ""); // Locally modified coords
|
|
|
|
// (of a local object ...)
|
|
|
|
// have been read during the copy,
|
|
|
|
// get rid of them...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_Duplicating = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (_Moved)
|
|
|
|
{
|
|
|
|
if (!_ValidPos)
|
|
|
|
{
|
|
|
|
cancelAction(instance);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ucstring instanceName = instance.getDisplayName();
|
|
|
|
if(instanceName == CI18N::get("uiR2EDNoName"))
|
|
|
|
instanceName = ucstring(instance.getClassName());
|
|
|
|
|
|
|
|
//getDMC().newAction(CI18N::get("uiR2EDMoveAction") + instance.getDisplayName());
|
|
|
|
getDMC().newAction(CI18N::get("uiR2EDMoveAction") + instanceName);
|
|
|
|
std::string posInstanceId = instance.getPosInstanceId();
|
|
|
|
if (posInstanceId.empty()) return;
|
|
|
|
std::string instanceId = instance.getObjectTable()->getAttr("InstanceId")->toString();
|
|
|
|
R2::getEditor().getLua().push(instanceId);
|
|
|
|
R2::getEditor().callEnvFunc( "checkLeaderDistAndUngroup", 1, 0);
|
|
|
|
getEditor().requestCommitLocalNode(posInstanceId, "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cancelAction(instance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
const char *CToolSelectMove::getCursorForPossibleAction() const
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_getCursorForPossibleAction)
|
|
|
|
return isShiftDown() ? "curs_can_pan_dup.tga" : "curs_can_pan.tga";
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
const char *CToolSelectMove::getDefaultCursor() const
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_getDefaultCursor)
|
|
|
|
if (isMouseOnUI() && !isMouseOnWorldMap()) return DEFAULT_CURSOR;
|
|
|
|
return isShiftDown() ? "curs_dup.tga" : DEFAULT_CURSOR;
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
const char *CToolSelectMove::getPickCursor() const
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_getPickCursor)
|
|
|
|
return isShiftDown() ? "curs_pick_dup.tga" : "curs_pick.tga";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
void CToolSelectMove::updateAction(CInstance &instance)
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_updateAction)
|
|
|
|
CDisplayerVisual *vd = _GhostInstance ? _GhostInstance->getDisplayerVisual() : instance.getDisplayerVisual();
|
|
|
|
if(!vd) return;
|
|
|
|
setMouseCursor(_Duplicating ? "curs_pan_dup.tga" : "curs_pan.tga");
|
|
|
|
sint32 mx, my;
|
|
|
|
getMousePos(mx, my);
|
|
|
|
sint32 autoPanDx, autoPanDy;
|
|
|
|
handleWorldMapAutoPan(autoPanDx, autoPanDy);
|
|
|
|
CDisplayerVisual *vdParent = vd->getParent();
|
|
|
|
if (mx != _MouseX || my != _MouseY || autoPanDx || autoPanDy)
|
|
|
|
{
|
|
|
|
if (!autoPanDx && !autoPanDy)
|
|
|
|
{
|
|
|
|
const sint32 moveThreshold = 2;
|
|
|
|
const sint32 timeThreshold = 300; // if some time elapsed, then do the move
|
|
|
|
if (!_Moved && abs(mx - _MouseRefX) < moveThreshold && abs(my - _MouseRefY) < moveThreshold &&
|
|
|
|
(T1 - _StartTime) < timeThreshold
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// discard small displacement when clicking (move is unwanted most of the time)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_Duplicating && !_GhostInstance)
|
|
|
|
{
|
|
|
|
_GhostInstance = createGhost(instance);
|
|
|
|
if (!_GhostInstance)
|
|
|
|
{
|
|
|
|
_State = Idle;
|
|
|
|
getEditor().setCurrentTool(NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
vd = _GhostInstance->getDisplayerVisual();
|
|
|
|
if (!vd) return;
|
|
|
|
vdParent = vd->getParent();
|
|
|
|
}
|
|
|
|
//
|
|
|
|
_Moved = true;
|
|
|
|
// mouse has moved
|
|
|
|
// TODO : onMouseMove msg
|
|
|
|
// update mouse pos
|
|
|
|
_MouseX = mx;
|
|
|
|
_MouseY = my;
|
|
|
|
//
|
|
|
|
CTool::CWorldViewRay worldViewRay;
|
|
|
|
//
|
|
|
|
_ValidPos = true;
|
|
|
|
//
|
|
|
|
if (!isMouseOnWorldMap())
|
|
|
|
{
|
|
|
|
if (isMouseOnUI())
|
|
|
|
{
|
|
|
|
_FinalPos = _LastValidPos;
|
|
|
|
_ValidPos = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
computeWorldViewRay(mx - _DeltaX, my - _DeltaY, worldViewRay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
computeWorldViewRay(mx, my, worldViewRay);
|
|
|
|
if (!worldViewRay.OnMiniMap)
|
|
|
|
{
|
|
|
|
_FinalPos = _LastValidPos;
|
|
|
|
_ValidPos = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_ValidPos)
|
|
|
|
{
|
|
|
|
CVector inter; // intersection of view ray with landscape
|
|
|
|
_ValidPos = false;
|
|
|
|
switch(computeLandscapeRayIntersection(worldViewRay, inter))
|
|
|
|
{
|
|
|
|
case NoIntersection:
|
|
|
|
// no collision, can't drop instance, so let it at its start position
|
|
|
|
_FinalPos = _LastValidPos;
|
|
|
|
_ValidPos = false;
|
|
|
|
break;
|
|
|
|
case ValidPacsPos:
|
|
|
|
//nlwarning("moving instance at pos %s", NLMISC::toString(inter).c_str());
|
|
|
|
_FinalPos = inter;
|
|
|
|
_ValidPos = isValid2DPos(inter);
|
|
|
|
if (_ValidPos)
|
|
|
|
{
|
|
|
|
_LastValidPos = _FinalPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case InvalidPacsPos:
|
|
|
|
// If the object moved is a region or a group, invalid pacs pos
|
|
|
|
// may still be acceptable as long as an intersection was found
|
|
|
|
if (vd->isInvalidPacsPosAcceptable())
|
|
|
|
{
|
|
|
|
_FinalPos = inter;
|
|
|
|
_ValidPos = true; // good pos to drop instance
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_FinalPos = inter;
|
|
|
|
_ValidPos = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nlassert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_FinalPos = _FinalPos - _DeltaAnchor;
|
|
|
|
// make final pos relative to parent
|
|
|
|
if (!_Duplicating && vd->inheritPos() && instance.getParent())
|
|
|
|
{
|
|
|
|
if (vdParent)
|
|
|
|
{
|
|
|
|
_FinalPos = _FinalPos - vdParent->getWorldPos();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
|
|
setInstancePos(_FinalPos, instance);
|
|
|
|
}
|
|
|
|
if (_InitiallyAccessible)
|
|
|
|
{
|
|
|
|
// NB : do not do the test if initially inaccessible to allow user to go to an 'accessible' state more easily
|
|
|
|
if (!vd->isAccessible())
|
|
|
|
{
|
|
|
|
_ValidPos = false;
|
|
|
|
}
|
|
|
|
if (vdParent && !vdParent->isAccessible())
|
|
|
|
{
|
|
|
|
_ValidPos = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
|
|
if (_InitiallyValidShape)
|
|
|
|
{
|
|
|
|
// is started from an invalid shape, allow to use intermediate invalid shapes to correct it
|
|
|
|
if (vdParent && vdParent->isGroup() && !vdParent->isValidShape())
|
|
|
|
{
|
|
|
|
_ValidPos = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_Moved && _ValidPos)
|
|
|
|
{
|
|
|
|
std::string instanceId = instance.getObjectTable()->getAttr("InstanceId")->toString();
|
|
|
|
R2::getEditor().getLua().push(instanceId);
|
|
|
|
R2::getEditor().callEnvFunc( "checkGroupDistance", 1, 1);
|
|
|
|
if (!R2::getEditor().getLua().isBoolean(-1))
|
|
|
|
{
|
|
|
|
nlassert(0 && "checkGroupDistance return wrong type");
|
|
|
|
}
|
|
|
|
bool positionOk = R2::getEditor().getLua().toBoolean(-1);
|
|
|
|
R2::getEditor().getLua().pop();
|
|
|
|
_ValidPos = positionOk;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
if (!_ValidPos && _Moved)
|
|
|
|
{
|
|
|
|
setMouseCursor("curs_stop.tga");
|
|
|
|
}
|
|
|
|
// TMP TMP
|
|
|
|
/*
|
|
|
|
{
|
|
|
|
uint32 w, h;
|
|
|
|
getScreenSize(w, h);
|
|
|
|
Driver->setMatrixMode3D(MainCam);
|
|
|
|
Driver->setModelMatrix(CMatrix::Identity);
|
|
|
|
drawBox(_StartPos - CVector(0.02f, 0.02f, 0.02f), _StartPos + CVector(0.02f, 0.02f, 0.02f), CRGBA::Magenta);
|
|
|
|
Driver->setMatrixMode2D11();
|
|
|
|
Driver->drawLine((_RefX - 5) / (float) w, _RefY / (float) h, (_RefX + 5) / (float) w, _RefY / (float) h, CRGBA::Red);
|
|
|
|
Driver->drawLine(_RefX / (float) w, (_RefY - 5) / (float) h, _RefX / (float) w, (_RefY + 5) / (float) h, CRGBA::Red);
|
|
|
|
//
|
|
|
|
Driver->drawLine((_PosRefX - 5) / (float) w, _PosRefY / (float) h, (_PosRefX + 5) / (float) w, _PosRefY / (float) h, CRGBA::Green);
|
|
|
|
Driver->drawLine(_PosRefX / (float) w, (_PosRefY - 5) / (float) h, _PosRefX / (float) w, (_PosRefY + 5) / (float) h, CRGBA::Green);
|
|
|
|
//
|
|
|
|
Driver->drawLine((_MouseRefX - 5) / (float) w, _MouseRefY / (float) h, (_MouseRefX + 5) / (float) w, _MouseRefY / (float) h, CRGBA::Yellow);
|
|
|
|
Driver->drawLine(_MouseRefX / (float) w, (_MouseRefY - 5) / (float) h, _MouseRefX / (float) w, (_MouseRefY + 5) / (float) h, CRGBA::Yellow);
|
|
|
|
//
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
void CToolSelectMove::setInstancePos(const NLMISC::CVectorD &pos, CInstance &instance)
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_setInstancePos)
|
|
|
|
std::string posInstanceId = instance.getPosInstanceId();
|
|
|
|
if (posInstanceId.empty()) return;
|
|
|
|
CObject *newPos = buildVector(pos, posInstanceId);
|
|
|
|
getEditor().requestSetLocalNode(_GhostInstance ? _GhostInstance->getId() : instance.getId(), "Position", newPos);
|
|
|
|
delete newPos;
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
bool CToolSelectMove::isActionPossibleOn(const CInstance &instance) const
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_isActionPossibleOn)
|
|
|
|
CInstance &mutableInstance = const_cast<CInstance &>(instance);
|
|
|
|
CDisplayerVisual *dv = mutableInstance.getDisplayerVisual();
|
|
|
|
if (dv && dv->getActualDisplayMode() != CDisplayerVisual::DisplayModeVisible)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (isShiftDown())
|
|
|
|
{
|
|
|
|
CLuaStackRestorer lsr(&getEditor().getLua(), 0);
|
|
|
|
mutableInstance.getLuaProjection().callMethodByNameNoThrow("isCopyable", 0, 1); // isCopyable should be 'const', so no worries here ...
|
|
|
|
if (getEditor().getLua().toBoolean(1))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
void CToolSelectMove::onActivate()
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_onActivate)
|
|
|
|
setContextHelp(CI18N::get("uiR2EDToolSelectMove"));
|
|
|
|
}
|
|
|
|
|
2010-06-10 09:50:19 +00:00
|
|
|
// ***************************************************************
|
2010-05-06 00:08:41 +00:00
|
|
|
void CToolSelectMove::updateBeforeRender()
|
|
|
|
{
|
|
|
|
//H_AUTO(R2_CToolSelectMove_updateBeforeRender)
|
|
|
|
if (_Duplicating && _GhostInstance)
|
|
|
|
{
|
|
|
|
_AutoGroup.update(_FinalPos, _GhostInstance->getPaletteId(), _ValidPos && !isCtrlDown());
|
|
|
|
_GhostInstance->getDisplayerVisual()->setDisplayFlag(CDisplayerVisual::FlagHideActivities, _AutoGroup.getGroupingCandidate() != NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!_Duplicating && _GhostInstance) _GhostInstance->getDisplayerVisual()->setDisplayFlag(CDisplayerVisual::FlagHideActivities, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////
|
|
|
|
// ACTION HANDLERS //
|
|
|
|
/////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make the select/move tool current
|
|
|
|
*/
|
|
|
|
class CAHSelectMove : public IActionHandler
|
|
|
|
{
|
|
|
|
virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */)
|
|
|
|
{
|
|
|
|
getEditor().setCurrentTool(new CToolSelectMove);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
REGISTER_ACTION_HANDLER(CAHSelectMove, "r2ed_select_move");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // R2
|
|
|
|
|