khanat-code-old/code/ryzom/client/src/r2/tool.cpp
2010-06-13 16:58:11 +02:00

902 lines
28 KiB
C++

// 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 "tool.h"
#include "editor.h"
#include "r2_config.h"
//
#include "../interface_v3/interface_manager.h"
#include "../interface_v3/event_descriptor.h"
#include "../motion/user_controls.h"
#include "../global.h"
#include "../entities.h"
#include "../water_map.h"
#include "../input.h"
#include "../time_client.h"
#include "displayer_visual.h"
#include "../interface_v3/group_map.h"
#include "../interface_v3/lua_ihm.h"
//
#include "nel/pacs/u_global_position.h"
#include "nel/pacs/u_global_retriever.h"
//
#include "nel/misc/matrix.h"
#include "nel/misc/vector_2f.h"
//
#include "nel/3d/u_landscape.h"
//
#include "nel/3d/packed_world.h"
extern NLPACS::UGlobalRetriever *GR;
using namespace NLMISC;
using namespace NL3D;
namespace R2
{
const uint32 DEFAULT_ENTITY_MIN_OPACITY = 128;
bool CTool::_MouseCaptured = false;
static const CVector cardinals[] =
{
CVector(1.f, 0.f, 0.f),
CVector(1.f, 1.f, 0.f).normed(),
CVector(0.f, 1.f, 0.f),
CVector(-1.f, 1.f, 0.f).normed(),
CVector(-1.f, 0.f, 0.f),
CVector(-1.f, -1.f, 0.f).normed(),
CVector(0.f, -1.f, 0.f),
CVector(1.f, -1.f, 0.f).normed()
};
// ***************************************************************
CTool::CTool()
{
_DoubleClickStartTime = -1;
_DoubleClickX = -1;
_DoubleClickY = -1;
_AutoPanLastHandlingFrame = 0;
_AutoPanDelay = 0;
_NumPans = 0;
}
// ***************************************************************
void CTool::startDoubleClickCheck()
{
//H_AUTO(R2_CTool_startDoubleClickCheck)
_DoubleClickStartTime = T0;
getMousePos(_DoubleClickX, _DoubleClickY);
}
// ***************************************************************
bool CTool::checkDoubleClick()
{
//H_AUTO(R2_CTool_checkDoubleClick)
if (_DoubleClickStartTime == -1) return false;
if (T0 - _DoubleClickStartTime >= getUI().getUserDblClickDelay()) return false;
sint32 mx, my;
getMousePos(mx, my);
const sint32 moveThrehsold = 2;
// test that mouse hasn't moved too much between the 2 clicks
return abs(mx - _DoubleClickX) <= moveThrehsold && abs(my - _DoubleClickY) <= moveThrehsold;
}
// ***************************************************************
bool CTool::isShiftDown()
{
//H_AUTO(R2_CTool_isShiftDown)
return Driver->AsyncListener.isKeyDown(KeySHIFT) ||
Driver->AsyncListener.isKeyDown(KeyLSHIFT) ||
Driver->AsyncListener.isKeyDown(KeyRSHIFT);
}
// ***************************************************************
bool CTool::isCtrlDown()
{
//H_AUTO(R2_CTool_isCtrlDown)
return Driver->AsyncListener.isKeyDown(KeyCONTROL) ||
Driver->AsyncListener.isKeyDown(KeyLCONTROL) ||
Driver->AsyncListener.isKeyDown(KeyRCONTROL);
}
// ***************************************************************
CInterfaceManager &CTool::getUI()
{
//H_AUTO(R2_CTool_getUI)
return CEditor::getUI();
}
// ***************************************************************
void CTool::getScreenSize(uint32 &scrW, uint32 &scrH)
{
//H_AUTO(R2_CTool_getScreenSize)
getUI().getViewRenderer().getScreenSize(scrW, scrH);
}
// ***************************************************************
uint32 CTool::getScreenWidth()
{
//H_AUTO(R2_CTool_getScreenWidth)
uint32 scrW, scrH;
getUI().getViewRenderer().getScreenSize(scrW, scrH);
return scrW;
}
// ***************************************************************
uint32 CTool::getScreenHeight()
{
//H_AUTO(R2_CTool_getScreenHeight)
uint32 scrW, scrH;
getUI().getViewRenderer().getScreenSize(scrW, scrH);
return scrH;
}
// ***************************************************************
void CTool::getMousePos(sint32 &x, sint32 &y)
{
//H_AUTO(R2_CTool_getMousePos)
CViewPointer *cursor = getUI().getPointer();
if(cursor == NULL)
{
x = y = -1;
return;
}
cursor->getPointerPos(x, y);
}
// ***************************************************************
sint32 CTool::getMouseX()
{
//H_AUTO(R2_CTool_getMouseX)
sint32 x, y;
getMousePos(x, y);
return x;
}
// ***************************************************************
sint32 CTool::getMouseY()
{
//H_AUTO(R2_CTool_getMouseY)
sint32 x, y;
getMousePos(x, y);
return y;
}
// ***************************************************************
bool CTool::isMouseOnUI()
{
//H_AUTO(R2_CTool_isMouseOnUI)
return getUI().getWindowUnder(getMouseX(), getMouseY()) != NULL;
}
// ***************************************************************
CGroupMap *CTool::getWorldMap()
{
//H_AUTO(R2_CTool_getWorldMap)
static NLMISC::CRefPtr<CGroupMap> mapPtr;
static volatile bool cacheTest = true;
if (!mapPtr || !cacheTest)
{
mapPtr = dynamic_cast<CGroupMap*>(getUI().getElementFromId("ui:interface:map:content:map_content:actual_map"));
}
return mapPtr;
}
// ***************************************************************
CGroupMap *CTool::isMouseOnWorldMap()
{
//H_AUTO(R2_CTool_isMouseOnWorldMap)
const std::vector<CInterfaceGroup *> &groupsUnder = getUI().getGroupsUnderPointer();
if (groupsUnder.empty()) return NULL;
for(uint k = 0; k < groupsUnder.size(); ++k)
{
CGroupMap *gm = dynamic_cast<CGroupMap *>(groupsUnder[k]);
if (gm) return gm;
}
return NULL;
}
// ***************************************************************
CGroupContainer *CTool::isMouseOnContainer()
{
//H_AUTO(R2_CTool_isMouseOnContainer)
const std::vector<CInterfaceGroup *> &groupsUnder = getUI().getGroupsUnderPointer();
if (groupsUnder.empty()) return NULL;
for(uint k = 0; k < groupsUnder.size(); ++k)
{
CGroupContainer *gc = groupsUnder[k]->getParentContainer();
if (gc) return gc;
}
return NULL;
}
// ***************************************************************
void CTool::computeWorldViewRay(sint32 posX, sint32 posY, CWorldViewRay &dest)
{
//H_AUTO(R2_CTool_computeWorldViewRay)
CGroupMap *gm = isMouseOnWorldMap();
if (gm)
{
sint32 windowX = posX - gm->getXReal();
sint32 windowY = posY - gm->getYReal();
CVector2f mapPos;
gm->windowToMap(mapPos, windowX, windowY);
if (mapPos.x >= 0.f && mapPos.y >= 0.f && mapPos.x <= 1.f && mapPos.y <= 1.f)
{
CVector2f worldPos;
gm->mapToWorld(worldPos, mapPos);
dest.Dir = -CVector::K;
dest.Origin.set(worldPos.x, worldPos.y, 10000.f); // look from above
dest.OnMiniMap = true;
dest.Right = CVector(1.f, 0.f, 0.f);
dest.Up = CVector(0.f, 1.f, 0.f);
dest.Valid = true;
return;
}
else
{
dest.OnMiniMap = true;
dest.Valid = false;
return;
}
}
uint32 scrW, scrH;
getScreenSize(scrW, scrH);
const NL3D::CFrustum &fru = MainCam.getFrustum();
//
dest.Dir.x = posX / (float) scrW;
dest.Dir.x = fru.Left + (fru.Right - fru.Left) * dest.Dir.x;
dest.Dir.z = posY / (float) scrH;
dest.Dir.z = fru.Bottom + (fru.Top - fru.Bottom) * dest.Dir.z;
dest.Dir.y = fru.Near;
//
dest.Dir /= fru.Near;
CMatrix camMatrix = MainCam.getMatrix();
dest.Right = camMatrix.getI().normed();
dest.Up = camMatrix.getK().normed();
dest.Dir = camMatrix.mulVector(dest.Dir);
dest.Origin = camMatrix.getPos();
dest.OnMiniMap = false;
dest.Valid = true;
// When looking upward, the camera may be "snapped" to the ground
// In this case, false intersection with the ground may be detected, so move forward
// in ray direction to resolve that problem
const float distBias = 0.20f;
dest.Origin += distBias * dest.Dir;
}
// ***************************************************************
bool CTool::isInScreen(sint32 x, sint32 y)
{
//H_AUTO(R2_CTool_isInScreen)
uint32 scrW, scrH;
getScreenSize(scrW, scrH);
return x >= 0 && y >=0 && x < (sint32) scrW && y < (sint32) scrH;
}
// ***************************************************************
CTool::TRayIntersectionType CTool::getPacsType(const NLMISC::CVector &pos, float threshold, NLPACS::UGlobalPosition &destPos)
{
//H_AUTO(R2_CTool_getPacsType)
if (!GR) return NoIntersection;
destPos = GR->retrievePosition(pos, threshold);
if (destPos.InstanceId != -1)
{
float waterHeight = 0.0f;
if(GR->isWaterPosition(destPos, waterHeight))
{
// for now, forbid to put something in water
return InvalidPacsPos;
}
return ValidPacsPos;
}
else
{
return InvalidPacsPos;
}
}
// ***************************************************************
bool CTool::raytrace(const NLMISC::CVector &segmentStart, const NLMISC::CVector &dir, NLMISC::CVector &inter)
{
//H_AUTO(R2_CTool_raytrace)
// try precise collision first if ray not vertical (not supported by CollisionManager)
H_AUTO ( RZ_Client_Camera_Collision_For_R2 )
if (dir.x != 0.f || dir.y != 0.f)
{
if (Landscape)
{
// use a shortest distance for collision manager (for speed)
CVector segmentEnd = segmentStart + 100.f * dir;
float dist = Landscape->getRayCollision(segmentStart, segmentEnd);
if (dist != 1.f)
{
inter = segmentStart + dist * (segmentEnd - segmentStart);
return true;
}
}
}
// else try with coarsest version of the island
// ... else try with coarsest version of the whole island
CPackedWorld *pw = getEditor().getIslandCollision().getPackedIsland();
if (pw)
{
if (pw->raytrace(segmentStart, segmentStart + 2000.f * dir, inter))
{
return true;
}
}
//
return false;
}
// ***************************************************************
CTool::TRayIntersectionType CTool::computeWorldMapIntersection(float x, float y, NLMISC::CVector &inter)
{
//H_AUTO(R2_CTool_computeWorldMapIntersection)
const CArray2D<sint16> &heightMap = getEditor().getIslandCollision().getHeightMap();
if (!heightMap.empty())
{
// here we take only the surface with a valid z, so the 'InvalidPacsPocs' case is not possible
if (computeNearestValidSurfaceFromHeightMap(x, y, inter)) return ValidPacsPos;
return NoIntersection;
}
if (!GR) return NoIntersection;
// if heightmap not present then relies on old pacs test method...(transition code before all goes through the heightmap)
CVector pos(x, y, 0.f);
//
NLPACS::UGlobalPosition pacsPos;
TRayIntersectionType interType = getPacsType(pos, 100000.f, pacsPos);
nlassert(interType != NoIntersection);
CVector firstGuess = pacsPos.LocalPosition.Estimation;
firstGuess.x = x;
firstGuess.y = y;
// now we have the good z, try to refine pos using the camera collision
float deltaZ = 2.f;
float bias = 0.001f; // if (dx == 0) and (dy == 0), the camera collision fails (not supported, visibly)
// workaround it by adding a small bias
if (interType == InvalidPacsPos)
{
// no good z here..., so must test the whole range
firstGuess.z = 0.f;
deltaZ *= 1000.f;
bias *= 1000.f;
}
CVector refinedStart = firstGuess + deltaZ * CVector::K + bias * CVector::I;
CVector refinedEnd = firstGuess - deltaZ * CVector::K - bias * CVector::I;
//
float dist = CollisionManager->getCameraCollision(refinedStart, refinedEnd, 0.1f, false);
if (dist == 1.f) return NoIntersection;
//
inter = refinedStart + dist * (refinedEnd - refinedStart);
return interType;
}
// ***************************************************************
inline bool CTool::isIslandValidPos(const CArray2D<sint16> &heightMap, const CScenarioEntryPoints::CCompleteIsland &islandDesc, float x, float y)
{
//H_AUTO(R2_CTool_isIslandValidPos)
sint mapX = (sint) (x - islandDesc.XMin);
sint mapY = (sint) (y - islandDesc.YMin);
clamp(mapX, 0, (sint) heightMap.getWidth() - 1);
clamp(mapY, 0, (sint) heightMap.getHeight() - 1);
sint hmZ = heightMap(mapX, mapY);
return hmZ < 0x7ffe;
}
// do several raytracing test on the small region until a hit is found (at boundary of zones, welding is not perfect so it helps to remove this problem)
static bool areaRaytrace(CPackedWorld &pw, const CVector &start, const CVector &end, CVector &inter, CVector *normal)
{
//H_AUTO(R2_areaRaytrace)
static volatile float distMax = 0.2f;
static volatile float distStep = 0.05f;
if (pw.raytrace(start, end, inter, NULL, normal)) return true;
for (float dist = distStep; dist <= distMax; dist += distStep)
{
for (uint k = 0; k < sizeofarray(cardinals); ++k)
{
CVector delta = dist * cardinals[k];
if (pw.raytrace(start + delta, end + delta, inter, NULL, normal))
{
inter -= delta; // remove correction
return true;
}
}
}
return false;
}
// ***************************************************************
bool CTool::computeNearestValidSurfaceFromHeightMap(float x, float y, NLMISC::CVector &inter)
{
//H_AUTO(R2_CTool_computeNearestValidSurfaceFromHeightMap)
const CArray2D<sint16> &heightMap = getEditor().getIslandCollision().getHeightMap();
nlassert (!heightMap.empty());
//
const CScenarioEntryPoints::CCompleteIsland *islandDesc = getEditor().getIslandCollision().getCurrIslandDesc();
if (!islandDesc) return false;
sint mapX = (sint) (x - islandDesc->XMin);
sint mapY = (sint) (y - islandDesc->YMin);
if (mapX < 0 || mapY < 0 || mapX >= (islandDesc->XMax - islandDesc->XMin) || mapY >= (islandDesc->YMax - islandDesc->YMin)) return false;
sint hmZ = heightMap(mapX, mapY);
if (hmZ >= 0x7ffe) return false; // not an accessible pos
if (!isIslandValidPos(heightMap, *islandDesc, x + 0.5f, y) ||
!isIslandValidPos(heightMap, *islandDesc, x - 0.5f, y) ||
!isIslandValidPos(heightMap, *islandDesc, x, y + 0.5f) ||
!isIslandValidPos(heightMap, *islandDesc, x, y - 0.5f)) return false;
float z = 1.f + 2.f * hmZ;
// this is a possibly valid position
// compute nearest surface from here, and see if not far from the intersection
CPackedWorld *pw = getEditor().getIslandCollision().getPackedIsland();
nlassert(pw); // packed world should be always present when heightmap is
//
CVector inter1;
CVector inter2;
CVector origin(x, y, z);
CVector bias(0.f, 0.f, 0.1f);
NLMISC::CVector normal1;
NLMISC::CVector normal2;
static volatile float minAngleSin = 0.2f;
bool inter1Found = areaRaytrace(*pw, origin - bias, origin + 10000.f * CVector::K, inter1, &normal1);
bool inter2Found = areaRaytrace(*pw, origin + bias, origin - 10000.f * CVector::K, inter2, &normal2);
inter1Found = inter1Found && normal1.z >= minAngleSin;
inter2Found = inter2Found && normal2.z >= minAngleSin;
if (!inter1Found && !inter2Found) return false;
if (inter1Found && inter2Found)
{
// because z in heightmap in usually a 'ceil' of real height, tends to favor surface below
// to avoid special case were the cliff top is very close to the ground. add a bias for this (equal
// to the heightmap precision)
origin.z -= 2.f;
inter = ((inter1 - origin).norm() < (inter2 - origin).norm()) ? inter1 : inter2;
}
else if (inter1Found)
{
inter = inter1;
}
else
{
inter = inter2;
}
return true;
}
// ***************************************************************
bool CTool::isValid2DPos(const NLMISC::CVector2f &pos)
{
//H_AUTO(R2_CTool_isValid2DPos)
return getEditor().getIslandCollision().isValidPos(CVector2f(pos.x, pos.y));
}
// ***************************************************************
CTool::TRayIntersectionType CTool::computeLandscapeRayIntersection(const CWorldViewRay &worldViewRay, NLMISC::CVector &inter)
{
//H_AUTO(R2_CTool_computeLandscapeRayIntersection)
if (!worldViewRay.Valid) return NoIntersection;
if (worldViewRay.OnMiniMap)
{
return computeWorldMapIntersection(worldViewRay.Origin.x, worldViewRay.Origin.y, inter);
}
// Compute collision point with landscape
if (!raytrace(worldViewRay.Origin, worldViewRay.Dir, inter))
{
// NB following code is intended to fix the problem of holes betweens zone in the packed collision
static volatile float bias = 0.15f;
bool found = false;
for (uint k = 0; k < sizeofarray(cardinals) && !found; ++k)
{
CVector delta = bias * (cardinals[k].x * worldViewRay.Right + cardinals[k].y * worldViewRay.Up);
found = raytrace(worldViewRay.Origin + delta, worldViewRay.Dir, inter);
}
if (!found)
{
// Because of holes near zones boundaries, add a small bias
return NoIntersection;
}
}
//
const CArray2D<sint16> &heightMap = getEditor().getIslandCollision().getHeightMap();
if (!heightMap.empty())
{
// if heightmap is present, use it because it gives us more reliable information
CVector surfPos;
if (!computeNearestValidSurfaceFromHeightMap(inter.x, inter.y, surfPos)) return InvalidPacsPos;
static volatile float threshold = 2.f;
return (inter - surfPos).norm() < threshold ? ValidPacsPos : InvalidPacsPos;
}
// get pacs type at intersection
if (!GR)
{
return NoIntersection;
}
else
{
// see if pacs collisions are ok at that pos
NLPACS::UGlobalPosition dummyPos;
return getPacsType(inter, 2.f, dummyPos);
}
}
// **********************************************
void CTool::handleMouseOverPlayer(bool over)
{
//H_AUTO(R2_CTool_handleMouseOverPlayer)
// If the mouse is over the player make the player transparent
CCDBNodeLeaf *pNL = getUI().getDbProp("UI:SAVE:USER_CHAR_FADE", false);
if ((pNL != NULL) && (pNL->getValue32() == 1) && UserEntity->selectable())
{
// If the nearest entity is the player, hide!
if (over)
UserEntity->makeTransparent(true);
else
UserEntity->makeTransparent(false);
}
else
UserEntity->makeTransparent(false);
}
// ***************************************************************
CInstance *CTool::checkInstanceUnderMouse(IDisplayerUIHandle **miniMapHandle /*= NULL*/)
{
//H_AUTO(R2_CTool_checkInstanceUnderMouse)
// Get the pointer position (in pixels)
if (miniMapHandle)
{
*miniMapHandle = NULL;
}
sint32 x, y;
getMousePos(x, y);
if (isMouseOnUI())
{
CGroupMap *gm = isMouseOnWorldMap();
if (gm)
{
CInstance *inst = getEditor().getSelectedInstance();
CDisplayerVisual *currSelectedDV = inst ? inst->getDisplayerVisual() : NULL;
sint32 mouseXInWindow;
sint32 mouseYInWindow;
gm->getCorner(mouseXInWindow, mouseYInWindow, Hotspot_BL);
mouseXInWindow = x - mouseXInWindow;
mouseYInWindow = y - mouseYInWindow;
IDisplayerUIHandle *bestCandidate = NULL;
sint8 bestCandidateLayer = -128;
// see if the element is under the mouse
const std::vector<CCtrlBase *> &ctrlsUnder = getUI().getCtrlsUnderPointer();
for(sint k = (sint)ctrlsUnder.size() - 1; k >= 0; --k)
{
IDisplayerUIHandle *handle = dynamic_cast<IDisplayerUIHandle *>(ctrlsUnder[k]);
if (handle != NULL)
{
CDisplayerVisual *dv = handle->getDisplayedInstance().getDisplayerVisual();
if (ctrlsUnder[k]->getRenderLayer() > bestCandidateLayer ||
(ctrlsUnder[k]->getRenderLayer() == bestCandidateLayer && currSelectedDV == dv)
)
{
if (dv && dv->isSelectable())
{
// test for real hit
if (handle->contains(mouseXInWindow, mouseYInWindow))
{
bestCandidate = handle;
bestCandidateLayer = ctrlsUnder[k]->getRenderLayer();
}
}
}
}
}
if (bestCandidate)
{
if (miniMapHandle)
{
*miniMapHandle = bestCandidate;
}
return &(bestCandidate->getDisplayedInstance());
}
}
// else asks lua if there's a selectable instance under the mouse
// (so that we don't need to know of the UI ..., world map is a special case)
{
CLuaState &ls = getEditor().getLua();
CLuaStackRestorer lsr(&ls, 0);
if (getEditor().callEnvMethod("getInstanceIdFromUIUnderMouse", 0, 1))
{
if (ls.isString(1))
{
CInstance *inst = getEditor().getInstanceFromId(ls.toString(1));
if (inst) return inst;
}
}
}
}
else if (!IsMouseFreeLook() && !getUI().getCapturePointerLeft() && !getUI().getCapturePointerRight())
{
// Over the screen ?
if (isInScreen(x, y))
{
// Get the pointer position (in float)
float cursX, cursY;
cursX = x / (float) getScreenWidth();
cursY = y / (float) getScreenHeight();
// Get the entities under position
bool isPlayerUnderCursor;
CInstance *inst = getEditor().getInstanceUnderPos(cursX, cursY, 2000.f, isPlayerUnderCursor);
handleMouseOverPlayer(isPlayerUnderCursor);
return inst;
}
}
return NULL;
}
// ***************************************************************
void CTool::handleMouseOverInstance(const char *cursorDefault, const char *cursorOverUnselectedInstance, const char *cursorOverSelectedInstance)
{
//H_AUTO(R2_CTool_handleMouseOverInstance)
setMouseCursor(cursorDefault);
CInstance *instanceUnder = checkInstanceUnderMouse();
if (!instanceUnder)
{
getEditor().setFocusedInstance(NULL);
return;
}
getEditor().setFocusedInstance(instanceUnder);
if (instanceUnder != getEditor().getSelectedInstance())
{
setMouseCursor(cursorOverUnselectedInstance);
}
else
{
// indicate that the user can move the instance
setMouseCursor(cursorOverSelectedInstance);
}
}
// ***************************************************************
bool CTool::onMouseRightButtonClicked()
{
//H_AUTO(R2_CTool_onMouseRightButtonClicked)
if (!getEditor().getFocusedInstance())
{
getEditor().setSelectedInstance(NULL);
return false;
}
getEditor().setSelectedInstance(getEditor().getFocusedInstance());
getEditor().displayContextMenu();
return true;
}
// ***************************************************************
bool CTool::defaultRightButtonDownHandling()
{
//H_AUTO(R2_CTool_defaultRightButtonDownHandling)
/*if (!getEditor().getFocusedInstance())
{
// cancel any current selection, this will cause the default context menu to beshown
getEditor().setSelectedInstance(NULL);
return false;
}
getEditor().setSelectedInstance(getEditor().getFocusedInstance());
getEditor().displayContextMenu();
return true; */
return false;
}
// ***************************************************************
void CTool::captureMouse()
{
//H_AUTO(R2_CTool_captureMouse)
CGroupMap *gm = isMouseOnWorldMap();
if (gm)
{
getUI().setCapturePointerLeft(gm);
}
else
{
UserControls.captureMouse();
getUI().enableMouseHandling(false);
}
getUI().setContextHelpActive(false);
_MouseCaptured = true;
}
// ***************************************************************
void CTool::releaseMouse()
{
//H_AUTO(R2_CTool_releaseMouse)
getUI().setCapturePointerLeft(NULL);
UserControls.releaseMouse();
getUI().enableMouseHandling(true);
getUI().setContextHelpActive(true);
_MouseCaptured = false;
}
// *********************************************************************************************************
bool CTool::isMouseCaptured()
{
//H_AUTO(R2_CTool_isMouseCaptured)
return _MouseCaptured;
}
// *********************************************************************************************************
void CTool::setMouseCursor(const char *cursorTexture)
{
//H_AUTO(R2_CTool_setMouseCursor)
CViewPointer *cursor = getUI().getPointer();
if(cursor)
{
cursor->setCursor(cursorTexture);
}
}
// ***************************************************************
bool CTool::handleEvent(const CEventDescriptor &event)
{
//H_AUTO(R2_CTool_handleEvent)
bool handled = false;
if (event.getType() == CEventDescriptor::mouse)
{
CEventDescriptorMouse &eventDesc = (CEventDescriptorMouse&)event;
switch(eventDesc.getEventTypeExtended())
{
case CEventDescriptorMouse::mousemove:
handled = onMouseMove();
break;
case CEventDescriptorMouse::mouseleftdown:
handled = onMouseLeftButtonDown();
//if (handled) nlwarning("onMouseLeftButtonDown handled");
break;
case CEventDescriptorMouse::mouserightdown:
handled = onMouseRightButtonDown();
//if (handled) nlwarning("onMouseRightButtonDown handled");
break;
case CEventDescriptorMouse::mouseleftup:
handled = onMouseLeftButtonUp();
//if (handled) nlwarning("onMouseLeftButtonUp handled");
break;
case CEventDescriptorMouse::mouserightup:
handled = onMouseRightButtonUp();
//if (handled) nlwarning("onMouseRightButtonUp handled");
break;
}
}
if (event.getType() == CEventDescriptor::system)
{
const CEventDescriptorSystem &eds = (const CEventDescriptorSystem &) event;
if (eds.getEventTypeExtended() == CEventDescriptorSystem::setfocus)
{
const CEventDescriptorSetFocus &edsf = (const CEventDescriptorSetFocus &) eds;
if (edsf.hasFocus() == true)
{
onFocusGained();
}
}
}
return handled;
}
// ***************************************************************
CDynamicMapClient &CTool::getDMC()
{
//H_AUTO(R2_CTool_getDMC)
return getEditor().getDMC();
}
// ***************************************************************
void CTool::handleWorldMapAutoPan(sint32 &outDx, sint32 &outDy)
{
//H_AUTO(R2_CTool_handleWorldMapAutoPan)
outDx = outDy = 0;
CGroupMap *gm = getWorldMap();
if (!gm) return;
// if not handled at previous frame then reset
uint64 currFrameCounter = Driver->getSwapBufferCounter();
if (currFrameCounter == _AutoPanLastHandlingFrame) return; // already handled this frame
if (_AutoPanLastHandlingFrame <= currFrameCounter - 2)
{
_AutoPanDelay = 0;
_NumPans = 0;
}
// see if mouse pos is valid
sint32 mx, my;
getMousePos(mx, my);
if (!gm->isIn(mx, my))
{
return;
}
sint32 autoPanBorder = CV_MapAutoPanBorder.get();
sint32 x, y, w, h;
gm->computeMapRectInsideGroup(x, y, w, h);
sint32 dx = (gm->getXReal() + w - mx) <= autoPanBorder ? 1 : 0;
dx -= (mx - gm->getXReal()) <= autoPanBorder ? 1 : 0;
sint32 dy = (gm->getYReal() + h - my) <= autoPanBorder ? 1 : 0;
dy -= (my - gm->getYReal()) <= autoPanBorder ? 1 : 0;
if (!dx && !dy)
{
_AutoPanDelay = 0;
_NumPans = 0;
return;
}
_AutoPanLastHandlingFrame = currFrameCounter;
// select delay (shorter in fast-pan mode)
sint32 delay = (_NumPans > CV_MapAutoFastPanNumTicks.get()) ? CV_MapAutoFastPanDeltaInMs.get()
: CV_MapAutoPanDeltaInMs.get();
_AutoPanDelay += DT64;
if (_AutoPanDelay >= delay)
{
_AutoPanDelay = _AutoPanDelay % delay;
sint32 panDelta = CV_MapAutoPanSpeedInPixels.get();
outDx = panDelta * dx;
outDy = panDelta * dy;
gm->pan(outDx, outDy);
++ _NumPans; // one step toward 'fast pan' mode
return;
}
return;
}
// ***************************************************************
int CTool::luaIsPickTool(CLuaState &ls)
{
//H_AUTO(R2_CTool_luaIsPickTool)
CLuaIHM::checkArgCount(ls, "isPickTool", 0);
ls.push(isPickTool());
return 1;
}
// ***************************************************************
void CTool::setContextHelp(const ucstring &contextHelp)
{
//H_AUTO(R2_CTool_setContextHelp)
// forward the call to lua (all ui handling done bye lua)
CLuaState &ls = getEditor().getLua();
CLuaStackChecker lsc(&ls);
CLuaIHM::push(ls, contextHelp);
getEditor().callEnvMethod("setToolContextHelp", 1, 0);
}
// ***************************************************************
NLMISC::CRGBA CTool::getInvalidPosColor()
{
//H_AUTO(R2_CTool_getInvalidPosColor)
double duration = CV_InaccessiblePosAnimDurationInMS.get();
if (duration <= 0) duration = 0.1;
return blend(CV_InaccessiblePosColor0.get(),
CV_InaccessiblePosColor1.get(),
0.5f + 0.5f * (float) cos(NLMISC::Pi * fmod((double) T1, duration) / duration));
}
} // R2