Merge branch '138-toggle-free-look-with-hotkey' into main/gingo-test
[ryzomcore.git] / ryzom / client / src / r2 / tool.cpp
blob191ec1cba6d97563de6e931d47783b996b66f627
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2013-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "stdpch.h"
23 #include "tool.h"
24 #include "editor.h"
25 #include "r2_config.h"
27 #include "../interface_v3/interface_manager.h"
28 #include "nel/gui/event_descriptor.h"
29 #include "../motion/user_controls.h"
30 #include "../global.h"
31 #include "../entities.h"
32 #include "../water_map.h"
33 #include "../input.h"
34 #include "../time_client.h"
35 #include "displayer_visual.h"
36 #include "../interface_v3/group_map.h"
37 #include "nel/gui/lua_ihm.h"
39 #include "nel/pacs/u_global_position.h"
40 #include "nel/pacs/u_global_retriever.h"
42 #include "nel/misc/matrix.h"
43 #include "nel/misc/vector_2f.h"
45 #include "nel/3d/u_landscape.h"
47 #include "nel/3d/packed_world.h"
49 #ifdef DEBUG_NEW
50 #define new DEBUG_NEW
51 #endif
53 extern NLPACS::UGlobalRetriever *GR;
56 using namespace NLMISC;
57 using namespace NL3D;
59 namespace R2
62 const uint32 DEFAULT_ENTITY_MIN_OPACITY = 128;
65 bool CTool::_MouseCaptured = false;
66 NLMISC::CRefPtr<NLMISC::CCDBNodeLeaf> CTool::_UserCharFade;
68 static const CVector cardinals[] =
70 CVector(1.f, 0.f, 0.f),
71 CVector(1.f, 1.f, 0.f).normed(),
72 CVector(0.f, 1.f, 0.f),
73 CVector(-1.f, 1.f, 0.f).normed(),
74 CVector(-1.f, 0.f, 0.f),
75 CVector(-1.f, -1.f, 0.f).normed(),
76 CVector(0.f, -1.f, 0.f),
77 CVector(1.f, -1.f, 0.f).normed()
80 // ***************************************************************
81 CTool::CTool()
83 _DoubleClickStartTime = -1;
84 _DoubleClickX = -1;
85 _DoubleClickY = -1;
86 _AutoPanLastHandlingFrame = 0;
87 _AutoPanDelay = 0;
88 _NumPans = 0;
92 // ***************************************************************
93 void CTool::startDoubleClickCheck()
95 //H_AUTO(R2_CTool_startDoubleClickCheck)
96 _DoubleClickStartTime = T0;
97 getMousePos(_DoubleClickX, _DoubleClickY);
100 // ***************************************************************
101 bool CTool::checkDoubleClick()
103 //H_AUTO(R2_CTool_checkDoubleClick)
104 if (_DoubleClickStartTime == -1) return false;
105 if (T0 - _DoubleClickStartTime >= CWidgetManager::getInstance()->getUserDblClickDelay()) return false;
106 sint32 mx, my;
107 getMousePos(mx, my);
108 const sint32 moveThrehsold = 2;
109 // test that mouse hasn't moved too much between the 2 clicks
110 return abs(mx - _DoubleClickX) <= moveThrehsold && abs(my - _DoubleClickY) <= moveThrehsold;
113 // ***************************************************************
114 bool CTool::isShiftDown()
116 //H_AUTO(R2_CTool_isShiftDown)
117 return Driver->AsyncListener.isKeyDown(KeySHIFT) ||
118 Driver->AsyncListener.isKeyDown(KeyLSHIFT) ||
119 Driver->AsyncListener.isKeyDown(KeyRSHIFT);
122 // ***************************************************************
123 bool CTool::isCtrlDown()
125 //H_AUTO(R2_CTool_isCtrlDown)
126 return Driver->AsyncListener.isKeyDown(KeyCONTROL) ||
127 Driver->AsyncListener.isKeyDown(KeyLCONTROL) ||
128 Driver->AsyncListener.isKeyDown(KeyRCONTROL);
131 // ***************************************************************
132 CInterfaceManager &CTool::getUI()
134 //H_AUTO(R2_CTool_getUI)
135 return CEditor::getUI();
138 // ***************************************************************
139 void CTool::getScreenSize(uint32 &scrW, uint32 &scrH)
141 //H_AUTO(R2_CTool_getScreenSize)
142 CViewRenderer::getInstance()->getScreenSize(scrW, scrH);
145 // ***************************************************************
146 uint32 CTool::getScreenWidth()
148 //H_AUTO(R2_CTool_getScreenWidth)
149 uint32 scrW, scrH;
150 CViewRenderer::getInstance()->getScreenSize(scrW, scrH);
151 return scrW;
154 // ***************************************************************
155 uint32 CTool::getScreenHeight()
157 //H_AUTO(R2_CTool_getScreenHeight)
158 uint32 scrW, scrH;
159 CViewRenderer::getInstance()->getScreenSize(scrW, scrH);
160 return scrH;
163 // ***************************************************************
164 void CTool::getMousePos(sint32 &x, sint32 &y)
166 //H_AUTO(R2_CTool_getMousePos)
167 CViewPointer *cursor = static_cast< CViewPointer* >( CWidgetManager::getInstance()->getPointer() );
168 if(cursor == NULL)
170 x = y = -1;
171 return;
173 cursor->getPointerPos(x, y);
176 // ***************************************************************
177 void CTool::getMouseDown(bool &down, sint32 &x, sint32 &y)
179 down = false;
180 //H_AUTO(R2_CTool_getMousePos)
181 CViewPointer *cursor = static_cast< CViewPointer* >( CWidgetManager::getInstance()->getPointer() );
182 if(cursor == NULL)
184 x = y = -1;
185 return;
187 down = cursor->getPointerDown(x, y);
190 // ***************************************************************
191 void CTool::getMouseMiddleDown(bool &down, sint32 &x, sint32 &y)
193 //H_AUTO(R2_CTool_getMousePos)
194 CViewPointer *cursor = static_cast< CViewPointer* >( CWidgetManager::getInstance()->getPointer() );
195 if(cursor == NULL)
197 x = y = -1;
198 return;
200 down = cursor->getPointerMiddleDown(x, y);
203 // ***************************************************************
204 void CTool::getMouseRightDown(bool &down, sint32 &x, sint32 &y)
206 //H_AUTO(R2_CTool_getMousePos)
207 CViewPointer *cursor = static_cast< CViewPointer* >( CWidgetManager::getInstance()->getPointer() );
208 if(cursor == NULL)
210 x = y = -1;
211 return;
213 down = cursor->getPointerRightDown(x, y);
217 // ***************************************************************
218 sint32 CTool::getMouseX()
220 //H_AUTO(R2_CTool_getMouseX)
221 sint32 x, y;
222 getMousePos(x, y);
223 return x;
226 // ***************************************************************
227 sint32 CTool::getMouseY()
229 //H_AUTO(R2_CTool_getMouseY)
230 sint32 x, y;
231 getMousePos(x, y);
232 return y;
235 // ***************************************************************
236 bool CTool::isMouseOnUI()
238 //H_AUTO(R2_CTool_isMouseOnUI)
239 return CWidgetManager::getInstance()->getWindowUnder(getMouseX(), getMouseY()) != NULL;
243 // ***************************************************************
244 CGroupMap *CTool::getWorldMap()
246 //H_AUTO(R2_CTool_getWorldMap)
247 static NLMISC::CRefPtr<CGroupMap> mapPtr;
248 static volatile bool cacheTest = true;
249 if (!mapPtr || !cacheTest)
251 mapPtr = dynamic_cast<CGroupMap*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:map:content:map_content:actual_map"));
253 return mapPtr;
256 // ***************************************************************
257 CGroupMap *CTool::isMouseOnWorldMap()
259 //H_AUTO(R2_CTool_isMouseOnWorldMap)
260 const std::vector<CInterfaceGroup *> &groupsUnder = CWidgetManager::getInstance()->getGroupsUnderPointer();
261 if (groupsUnder.empty()) return NULL;
262 for(uint k = 0; k < groupsUnder.size(); ++k)
264 CGroupMap *gm = dynamic_cast<CGroupMap *>(groupsUnder[k]);
265 if (gm) return gm;
267 return NULL;
270 // ***************************************************************
271 CGroupContainer *CTool::isMouseOnContainer()
273 //H_AUTO(R2_CTool_isMouseOnContainer)
274 const std::vector<CInterfaceGroup *> &groupsUnder = CWidgetManager::getInstance()->getGroupsUnderPointer();
275 if (groupsUnder.empty()) return NULL;
276 for(uint k = 0; k < groupsUnder.size(); ++k)
278 CInterfaceGroup* gc = groupsUnder[k]->getParentContainer();
279 if (gc)
280 return static_cast< CGroupContainer* >( gc );
282 return NULL;
285 // ***************************************************************
286 void CTool::computeWorldViewRay(sint32 posX, sint32 posY, CWorldViewRay &dest)
288 //H_AUTO(R2_CTool_computeWorldViewRay)
289 CGroupMap *gm = isMouseOnWorldMap();
290 if (gm)
292 sint32 windowX = posX - gm->getXReal();
293 sint32 windowY = posY - gm->getYReal();
294 CVector2f mapPos;
295 gm->windowToMap(mapPos, windowX, windowY);
296 if (mapPos.x >= 0.f && mapPos.y >= 0.f && mapPos.x <= 1.f && mapPos.y <= 1.f)
298 CVector2f worldPos;
299 gm->mapToWorld(worldPos, mapPos);
300 dest.Dir = -CVector::K;
301 dest.Origin.set(worldPos.x, worldPos.y, 10000.f); // look from above
302 dest.OnMiniMap = true;
303 dest.Right = CVector(1.f, 0.f, 0.f);
304 dest.Up = CVector(0.f, 1.f, 0.f);
305 dest.Valid = true;
306 return;
308 else
310 dest.OnMiniMap = true;
311 dest.Valid = false;
312 return;
316 uint32 scrW, scrH;
317 getScreenSize(scrW, scrH);
318 const NL3D::CFrustum &fru = MainCam.getFrustum();
320 dest.Dir.x = posX / (float) scrW;
321 dest.Dir.x = fru.Left + (fru.Right - fru.Left) * dest.Dir.x;
322 dest.Dir.z = posY / (float) scrH;
323 dest.Dir.z = fru.Bottom + (fru.Top - fru.Bottom) * dest.Dir.z;
324 dest.Dir.y = fru.Near;
326 dest.Dir /= fru.Near;
327 CMatrix camMatrix = MainCam.getMatrix();
328 dest.Right = camMatrix.getI().normed();
329 dest.Up = camMatrix.getK().normed();
330 dest.Dir = camMatrix.mulVector(dest.Dir);
331 dest.Origin = camMatrix.getPos();
332 dest.OnMiniMap = false;
333 dest.Valid = true;
335 // When looking upward, the camera may be "snapped" to the ground
336 // In this case, false intersection with the ground may be detected, so move forward
337 // in ray direction to resolve that problem
338 const float distBias = 0.20f;
339 dest.Origin += distBias * dest.Dir;
342 // ***************************************************************
343 bool CTool::isInScreen(sint32 x, sint32 y)
345 //H_AUTO(R2_CTool_isInScreen)
346 uint32 scrW, scrH;
347 getScreenSize(scrW, scrH);
348 return x >= 0 && y >=0 && x < (sint32) scrW && y < (sint32) scrH;
351 // ***************************************************************
352 CTool::TRayIntersectionType CTool::getPacsType(const NLMISC::CVector &pos, float threshold, NLPACS::UGlobalPosition &destPos)
354 //H_AUTO(R2_CTool_getPacsType)
355 if (!GR) return NoIntersection;
356 destPos = GR->retrievePosition(pos, threshold);
357 if (destPos.InstanceId != -1)
359 float waterHeight = 0.0f;
360 if(GR->isWaterPosition(destPos, waterHeight))
362 // for now, forbid to put something in water
363 return InvalidPacsPos;
365 return ValidPacsPos;
367 else
369 return InvalidPacsPos;
373 // ***************************************************************
374 bool CTool::raytrace(const NLMISC::CVector &segmentStart, const NLMISC::CVector &dir, NLMISC::CVector &inter)
376 //H_AUTO(R2_CTool_raytrace)
377 // try precise collision first if ray not vertical (not supported by CollisionManager)
378 H_AUTO ( RZ_Client_Camera_Collision_For_R2 )
379 if (dir.x != 0.f || dir.y != 0.f)
381 if (Landscape)
383 // use a shortest distance for collision manager (for speed)
384 CVector segmentEnd = segmentStart + 100.f * dir;
385 float dist = Landscape->getRayCollision(segmentStart, segmentEnd);
386 if (dist != 1.f)
388 inter = segmentStart + dist * (segmentEnd - segmentStart);
389 return true;
393 // else try with coarsest version of the island
394 // ... else try with coarsest version of the whole island
395 CPackedWorld *pw = getEditor().getIslandCollision().getPackedIsland();
396 if (pw)
398 if (pw->raytrace(segmentStart, segmentStart + 2000.f * dir, inter))
400 return true;
404 return false;
407 // ***************************************************************
408 CTool::TRayIntersectionType CTool::computeWorldMapIntersection(float x, float y, NLMISC::CVector &inter)
410 //H_AUTO(R2_CTool_computeWorldMapIntersection)
411 const CArray2D<sint16> &heightMap = getEditor().getIslandCollision().getHeightMap();
412 if (!heightMap.empty())
414 // here we take only the surface with a valid z, so the 'InvalidPacsPocs' case is not possible
415 if (computeNearestValidSurfaceFromHeightMap(x, y, inter)) return ValidPacsPos;
416 return NoIntersection;
418 if (!GR) return NoIntersection;
419 // if heightmap not present then relies on old pacs test method...(transition code before all goes through the heightmap)
420 CVector pos(x, y, 0.f);
422 NLPACS::UGlobalPosition pacsPos;
423 TRayIntersectionType interType = getPacsType(pos, 100000.f, pacsPos);
424 nlassert(interType != NoIntersection);
425 CVector firstGuess = pacsPos.LocalPosition.Estimation;
426 firstGuess.x = x;
427 firstGuess.y = y;
428 // now we have the good z, try to refine pos using the camera collision
429 float deltaZ = 2.f;
430 float bias = 0.001f; // if (dx == 0) and (dy == 0), the camera collision fails (not supported, visibly)
431 // workaround it by adding a small bias
432 if (interType == InvalidPacsPos)
434 // no good z here..., so must test the whole range
435 firstGuess.z = 0.f;
436 deltaZ *= 1000.f;
437 bias *= 1000.f;
439 CVector refinedStart = firstGuess + deltaZ * CVector::K + bias * CVector::I;
440 CVector refinedEnd = firstGuess - deltaZ * CVector::K - bias * CVector::I;
442 float dist = CollisionManager->getCameraCollision(refinedStart, refinedEnd, 0.1f, false);
443 if (dist == 1.f) return NoIntersection;
445 inter = refinedStart + dist * (refinedEnd - refinedStart);
446 return interType;
449 // ***************************************************************
450 inline bool CTool::isIslandValidPos(const CArray2D<sint16> &heightMap, const CScenarioEntryPoints::CCompleteIsland &islandDesc, float x, float y)
452 //H_AUTO(R2_CTool_isIslandValidPos)
453 sint mapX = (sint) (x - islandDesc.XMin);
454 sint mapY = (sint) (y - islandDesc.YMin);
455 clamp(mapX, 0, (sint) heightMap.getWidth() - 1);
456 clamp(mapY, 0, (sint) heightMap.getHeight() - 1);
457 sint hmZ = heightMap(mapX, mapY);
458 return hmZ < 0x7ffe;
462 // 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)
463 static bool areaRaytrace(CPackedWorld &pw, const CVector &start, const CVector &end, CVector &inter, CVector *normal)
465 //H_AUTO(R2_areaRaytrace)
466 static volatile float distMax = 0.2f;
467 static volatile float distStep = 0.05f;
468 if (pw.raytrace(start, end, inter, NULL, normal)) return true;
469 for (float dist = distStep; dist <= distMax; dist += distStep)
472 for (uint k = 0; k < sizeofarray(cardinals); ++k)
474 CVector delta = dist * cardinals[k];
475 if (pw.raytrace(start + delta, end + delta, inter, NULL, normal))
477 inter -= delta; // remove correction
478 return true;
482 return false;
485 // ***************************************************************
486 bool CTool::computeNearestValidSurfaceFromHeightMap(float x, float y, NLMISC::CVector &inter)
488 //H_AUTO(R2_CTool_computeNearestValidSurfaceFromHeightMap)
489 const CArray2D<sint16> &heightMap = getEditor().getIslandCollision().getHeightMap();
490 nlassert (!heightMap.empty());
493 const CScenarioEntryPoints::CCompleteIsland *islandDesc = getEditor().getIslandCollision().getCurrIslandDesc();
494 if (!islandDesc) return false;
496 sint mapX = (sint) (x - islandDesc->XMin);
497 sint mapY = (sint) (y - islandDesc->YMin);
499 if (mapX < 0 || mapY < 0 || mapX >= (islandDesc->XMax - islandDesc->XMin) || mapY >= (islandDesc->YMax - islandDesc->YMin)) return false;
500 sint hmZ = heightMap(mapX, mapY);
501 if (hmZ >= 0x7ffe) return false; // not an accessible pos
503 if (!isIslandValidPos(heightMap, *islandDesc, x + 0.5f, y) ||
504 !isIslandValidPos(heightMap, *islandDesc, x - 0.5f, y) ||
505 !isIslandValidPos(heightMap, *islandDesc, x, y + 0.5f) ||
506 !isIslandValidPos(heightMap, *islandDesc, x, y - 0.5f)) return false;
508 float z = 1.f + 2.f * hmZ;
509 // this is a possibly valid position
510 // compute nearest surface from here, and see if not far from the intersection
511 CPackedWorld *pw = getEditor().getIslandCollision().getPackedIsland();
512 nlassert(pw); // packed world should be always present when heightmap is
514 CVector inter1;
515 CVector inter2;
516 CVector origin(x, y, z);
517 CVector bias(0.f, 0.f, 0.1f);
518 NLMISC::CVector normal1;
519 NLMISC::CVector normal2;
520 static volatile float minAngleSin = 0.2f;
521 bool inter1Found = areaRaytrace(*pw, origin - bias, origin + 10000.f * CVector::K, inter1, &normal1);
522 bool inter2Found = areaRaytrace(*pw, origin + bias, origin - 10000.f * CVector::K, inter2, &normal2);
523 inter1Found = inter1Found && normal1.z >= minAngleSin;
524 inter2Found = inter2Found && normal2.z >= minAngleSin;
525 if (!inter1Found && !inter2Found) return false;
527 if (inter1Found && inter2Found)
529 // because z in heightmap in usually a 'ceil' of real height, tends to favor surface below
530 // to avoid special case were the cliff top is very close to the ground. add a bias for this (equal
531 // to the heightmap precision)
532 origin.z -= 2.f;
533 inter = ((inter1 - origin).norm() < (inter2 - origin).norm()) ? inter1 : inter2;
535 else if (inter1Found)
537 inter = inter1;
539 else
541 inter = inter2;
543 return true;
546 // ***************************************************************
547 bool CTool::isValid2DPos(const NLMISC::CVector2f &pos)
549 //H_AUTO(R2_CTool_isValid2DPos)
550 return getEditor().getIslandCollision().isValidPos(CVector2f(pos.x, pos.y));
553 // ***************************************************************
554 CTool::TRayIntersectionType CTool::computeLandscapeRayIntersection(const CWorldViewRay &worldViewRay, NLMISC::CVector &inter)
556 //H_AUTO(R2_CTool_computeLandscapeRayIntersection)
557 if (!worldViewRay.Valid) return NoIntersection;
558 if (worldViewRay.OnMiniMap)
560 return computeWorldMapIntersection(worldViewRay.Origin.x, worldViewRay.Origin.y, inter);
562 // Compute collision point with landscape
563 if (!raytrace(worldViewRay.Origin, worldViewRay.Dir, inter))
565 // NB following code is intended to fix the problem of holes betweens zone in the packed collision
566 static volatile float bias = 0.15f;
567 bool found = false;
568 for (uint k = 0; k < sizeofarray(cardinals) && !found; ++k)
570 CVector delta = bias * (cardinals[k].x * worldViewRay.Right + cardinals[k].y * worldViewRay.Up);
571 found = raytrace(worldViewRay.Origin + delta, worldViewRay.Dir, inter);
573 if (!found)
575 // Because of holes near zones boundaries, add a small bias
576 return NoIntersection;
580 const CArray2D<sint16> &heightMap = getEditor().getIslandCollision().getHeightMap();
581 if (!heightMap.empty())
583 // if heightmap is present, use it because it gives us more reliable information
584 CVector surfPos;
585 if (!computeNearestValidSurfaceFromHeightMap(inter.x, inter.y, surfPos)) return InvalidPacsPos;
586 static volatile float threshold = 2.f;
587 return (inter - surfPos).norm() < threshold ? ValidPacsPos : InvalidPacsPos;
590 // get pacs type at intersection
591 if (!GR)
593 return NoIntersection;
595 else
597 // see if pacs collisions are ok at that pos
598 NLPACS::UGlobalPosition dummyPos;
599 return getPacsType(inter, 2.f, dummyPos);
603 // **********************************************
604 void CTool::handleMouseOverPlayer(bool over)
606 //H_AUTO(R2_CTool_handleMouseOverPlayer)
607 // If the mouse is over the player make the player transparent
608 CCDBNodeLeaf *pNL = _UserCharFade ? &*_UserCharFade
609 : &*(_UserCharFade = NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:USER_CHAR_FADE", false));
610 if ((pNL != NULL) && (pNL->getValue32() == 1) && UserEntity->selectable())
612 // If the nearest entity is the player, hide!
613 if (over)
614 UserEntity->makeTransparent(true);
615 else
616 UserEntity->makeTransparent(false);
618 else
619 UserEntity->makeTransparent(false);
622 // ***************************************************************
623 CInstance *CTool::checkInstanceUnderMouse(IDisplayerUIHandle **miniMapHandle /*= NULL*/)
625 //H_AUTO(R2_CTool_checkInstanceUnderMouse)
626 // Get the pointer position (in pixels)
627 if (miniMapHandle)
629 *miniMapHandle = NULL;
631 sint32 x, y;
632 getMousePos(x, y);
633 if (isMouseOnUI())
635 CGroupMap *gm = isMouseOnWorldMap();
636 if (gm)
638 CInstance *inst = getEditor().getSelectedInstance();
639 CDisplayerVisual *currSelectedDV = inst ? inst->getDisplayerVisual() : NULL;
640 sint32 mouseXInWindow;
641 sint32 mouseYInWindow;
642 gm->getCorner(mouseXInWindow, mouseYInWindow, Hotspot_BL);
643 mouseXInWindow = x - mouseXInWindow;
644 mouseYInWindow = y - mouseYInWindow;
645 IDisplayerUIHandle *bestCandidate = NULL;
646 sint8 bestCandidateLayer = -128;
647 // see if the element is under the mouse
648 const std::vector<CCtrlBase *> &ctrlsUnder = CWidgetManager::getInstance()->getCtrlsUnderPointer();
649 for(sint k = (sint)ctrlsUnder.size() - 1; k >= 0; --k)
651 IDisplayerUIHandle *handle = dynamic_cast<IDisplayerUIHandle *>(ctrlsUnder[k]);
652 if (handle != NULL)
654 CDisplayerVisual *dv = handle->getDisplayedInstance().getDisplayerVisual();
655 if (ctrlsUnder[k]->getRenderLayer() > bestCandidateLayer ||
656 (ctrlsUnder[k]->getRenderLayer() == bestCandidateLayer && currSelectedDV == dv)
660 if (dv && dv->isSelectable())
662 // test for real hit
663 if (handle->contains(mouseXInWindow, mouseYInWindow))
665 bestCandidate = handle;
666 bestCandidateLayer = ctrlsUnder[k]->getRenderLayer();
672 if (bestCandidate)
674 if (miniMapHandle)
676 *miniMapHandle = bestCandidate;
678 return &(bestCandidate->getDisplayedInstance());
681 // else asks lua if there's a selectable instance under the mouse
682 // (so that we don't need to know of the UI ..., world map is a special case)
684 CLuaState &ls = getEditor().getLua();
685 CLuaStackRestorer lsr(&ls, 0);
686 if (getEditor().callEnvMethod("getInstanceIdFromUIUnderMouse", 0, 1))
688 if (ls.isString(1))
690 CInstance *inst = getEditor().getInstanceFromId(ls.toString(1));
691 if (inst) return inst;
696 else if (!IsMouseFreeLook() && !CWidgetManager::getInstance()->getCapturePointerLeft() && !CWidgetManager::getInstance()->getCapturePointerRight())
698 // Over the screen ?
699 if (isInScreen(x, y))
701 // Get the pointer position (in float)
702 float cursX, cursY;
703 cursX = x / (float) getScreenWidth();
704 cursY = y / (float) getScreenHeight();
706 // Get the entities under position
707 bool isPlayerUnderCursor;
708 CInstance *inst = getEditor().getInstanceUnderPos(cursX, cursY, 2000.f, isPlayerUnderCursor);
709 handleMouseOverPlayer(isPlayerUnderCursor);
710 return inst;
713 return NULL;
717 // ***************************************************************
718 void CTool::handleMouseOverInstance(const char *cursorDefault, const char *cursorOverUnselectedInstance, const char *cursorOverSelectedInstance)
720 //H_AUTO(R2_CTool_handleMouseOverInstance)
721 setMouseCursor(cursorDefault);
722 CInstance *instanceUnder = checkInstanceUnderMouse();
723 if (!instanceUnder)
725 getEditor().setFocusedInstance(NULL);
726 return;
729 getEditor().setFocusedInstance(instanceUnder);
730 if (instanceUnder != getEditor().getSelectedInstance())
732 setMouseCursor(cursorOverUnselectedInstance);
734 else
736 // indicate that the user can move the instance
737 setMouseCursor(cursorOverSelectedInstance);
741 // ***************************************************************
742 bool CTool::onMouseRightButtonClicked()
744 //H_AUTO(R2_CTool_onMouseRightButtonClicked)
745 if (!getEditor().getFocusedInstance())
747 getEditor().setSelectedInstance(NULL);
748 return false;
750 getEditor().setSelectedInstance(getEditor().getFocusedInstance());
751 getEditor().displayContextMenu();
752 return true;
755 // ***************************************************************
756 bool CTool::defaultRightButtonDownHandling()
758 //H_AUTO(R2_CTool_defaultRightButtonDownHandling)
759 /*if (!getEditor().getFocusedInstance())
761 // cancel any current selection, this will cause the default context menu to beshown
762 getEditor().setSelectedInstance(NULL);
763 return false;
765 getEditor().setSelectedInstance(getEditor().getFocusedInstance());
766 getEditor().displayContextMenu();
767 return true; */
768 return false;
771 // ***************************************************************
772 void CTool::captureMouse()
774 //H_AUTO(R2_CTool_captureMouse)
775 CGroupMap *gm = isMouseOnWorldMap();
776 if (gm)
778 CWidgetManager::getInstance()->setCapturePointerLeft(gm);
780 else
782 UserControls.captureMouse();
783 CWidgetManager::getInstance()->enableMouseHandling(false);
785 CWidgetManager::getInstance()->setContextHelpActive(false);
786 _MouseCaptured = true;
789 // ***************************************************************
790 void CTool::releaseMouse()
792 //H_AUTO(R2_CTool_releaseMouse)
793 CWidgetManager::getInstance()->setCapturePointerLeft(NULL);
794 UserControls.releaseMouse();
795 CWidgetManager::getInstance()->enableMouseHandling(true);
796 CWidgetManager::getInstance()->setContextHelpActive(true);
797 _MouseCaptured = false;
800 // *********************************************************************************************************
801 bool CTool::isMouseCaptured()
803 //H_AUTO(R2_CTool_isMouseCaptured)
804 return _MouseCaptured;
807 // *********************************************************************************************************
808 void CTool::setMouseCursor(const std::string &cursorTexture)
810 //H_AUTO(R2_CTool_setMouseCursor)
811 CViewPointer *cursor = static_cast< CViewPointer* >( CWidgetManager::getInstance()->getPointer() );
812 if(cursor)
814 cursor->setCursor(cursorTexture);
818 // ***************************************************************
819 bool CTool::handleEvent(const NLGUI::CEventDescriptor &event)
821 //H_AUTO(R2_CTool_handleEvent)
822 bool handled = false;
823 if (event.getType() == NLGUI::CEventDescriptor::mouse)
825 NLGUI::CEventDescriptorMouse &eventDesc = (NLGUI::CEventDescriptorMouse&)event;
826 switch(eventDesc.getEventTypeExtended())
828 case NLGUI::CEventDescriptorMouse::mousemove:
829 handled = onMouseMove();
830 break;
831 case NLGUI::CEventDescriptorMouse::mouseleftdown:
832 handled = onMouseLeftButtonDown();
833 //if (handled) nlwarning("onMouseLeftButtonDown handled");
834 break;
835 case NLGUI::CEventDescriptorMouse::mouserightdown:
836 handled = onMouseRightButtonDown();
837 //if (handled) nlwarning("onMouseRightButtonDown handled");
838 break;
839 case NLGUI::CEventDescriptorMouse::mouseleftup:
840 handled = onMouseLeftButtonUp();
841 //if (handled) nlwarning("onMouseLeftButtonUp handled");
842 break;
843 case NLGUI::CEventDescriptorMouse::mouserightup:
844 handled = onMouseRightButtonUp();
845 //if (handled) nlwarning("onMouseRightButtonUp handled");
846 break;
849 if (event.getType() == NLGUI::CEventDescriptor::system)
851 const NLGUI::CEventDescriptorSystem &eds = (const NLGUI::CEventDescriptorSystem &) event;
852 if (eds.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::setfocus)
854 const NLGUI::CEventDescriptorSetFocus &edsf = (const NLGUI::CEventDescriptorSetFocus &) eds;
855 if (edsf.hasFocus() == true)
857 onFocusGained();
861 return handled;
864 // ***************************************************************
865 CDynamicMapClient &CTool::getDMC()
867 //H_AUTO(R2_CTool_getDMC)
868 return getEditor().getDMC();
871 // ***************************************************************
872 void CTool::handleWorldMapAutoPan(sint32 &outDx, sint32 &outDy)
874 //H_AUTO(R2_CTool_handleWorldMapAutoPan)
875 outDx = outDy = 0;
876 CGroupMap *gm = getWorldMap();
877 if (!gm) return;
878 // if not handled at previous frame then reset
879 uint64 currFrameCounter = Driver->getSwapBufferCounter();
880 if (currFrameCounter == _AutoPanLastHandlingFrame) return; // already handled this frame
881 if (_AutoPanLastHandlingFrame <= currFrameCounter - 2)
883 _AutoPanDelay = 0;
884 _NumPans = 0;
886 // see if mouse pos is valid
887 sint32 mx, my;
888 getMousePos(mx, my);
889 if (!gm->isIn(mx, my))
891 return;
893 sint32 autoPanBorder = CV_MapAutoPanBorder.get();
894 sint32 x, y, w, h;
895 gm->computeMapRectInsideGroup(x, y, w, h);
896 sint32 dx = (gm->getXReal() + w - mx) <= autoPanBorder ? 1 : 0;
897 dx -= (mx - gm->getXReal()) <= autoPanBorder ? 1 : 0;
898 sint32 dy = (gm->getYReal() + h - my) <= autoPanBorder ? 1 : 0;
899 dy -= (my - gm->getYReal()) <= autoPanBorder ? 1 : 0;
900 if (!dx && !dy)
902 _AutoPanDelay = 0;
903 _NumPans = 0;
904 return;
906 _AutoPanLastHandlingFrame = currFrameCounter;
907 // select delay (shorter in fast-pan mode)
908 sint32 delay = (_NumPans > CV_MapAutoFastPanNumTicks.get()) ? CV_MapAutoFastPanDeltaInMs.get()
909 : CV_MapAutoPanDeltaInMs.get();
911 _AutoPanDelay += DT64;
912 if (_AutoPanDelay >= delay)
914 _AutoPanDelay = _AutoPanDelay % delay;
915 sint32 panDelta = CV_MapAutoPanSpeedInPixels.get();
916 outDx = panDelta * dx;
917 outDy = panDelta * dy;
918 gm->pan(outDx, outDy);
919 ++ _NumPans; // one step toward 'fast pan' mode
920 return;
922 return;
925 // ***************************************************************
926 int CTool::luaIsPickTool(CLuaState &ls)
928 //H_AUTO(R2_CTool_luaIsPickTool)
929 CLuaIHM::checkArgCount(ls, "isPickTool", 0);
930 ls.push(isPickTool());
931 return 1;
934 // ***************************************************************
935 void CTool::setContextHelp(const ucstring &contextHelp)
937 //H_AUTO(R2_CTool_setContextHelp)
938 // forward the call to lua (all ui handling done bye lua)
939 CLuaState &ls = getEditor().getLua();
940 CLuaStackChecker lsc(&ls);
941 #ifdef RYZOM_LUA_UCSTRING
942 CLuaIHM::push(ls, contextHelp);
943 #else
944 ls.push(contextHelp.toUtf8());
945 #endif
946 getEditor().callEnvMethod("setToolContextHelp", 1, 0);
949 // ***************************************************************
950 NLMISC::CRGBA CTool::getInvalidPosColor()
952 //H_AUTO(R2_CTool_getInvalidPosColor)
953 double duration = CV_InaccessiblePosAnimDurationInMS.get();
954 if (duration <= 0) duration = 0.1;
955 return blend(CV_InaccessiblePosColor0.get(),
956 CV_InaccessiblePosColor1.get(),
957 0.5f + 0.5f * (float) cos(NLMISC::Pi * fmod((double) T1, duration) / duration));
961 } // R2