// Ryzom - MMORPG Framework // 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 . #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 mapPtr; static volatile bool cacheTest = true; if (!mapPtr || !cacheTest) { mapPtr = dynamic_cast(getUI().getElementFromId("ui:interface:map:content:map_content:actual_map")); } return mapPtr; } // *************************************************************** CGroupMap *CTool::isMouseOnWorldMap() { //H_AUTO(R2_CTool_isMouseOnWorldMap) const std::vector &groupsUnder = getUI().getGroupsUnderPointer(); if (groupsUnder.empty()) return NULL; for(uint k = 0; k < groupsUnder.size(); ++k) { CGroupMap *gm = dynamic_cast(groupsUnder[k]); if (gm) return gm; } return NULL; } // *************************************************************** CGroupContainer *CTool::isMouseOnContainer() { //H_AUTO(R2_CTool_isMouseOnContainer) const std::vector &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 &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 &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 &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 &heightMap = getEditor().getIslandCollision().getHeightMap(); if (!heightMap.empty()) { // if heightmap is present, use it because it gives us more reliable informations 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 &ctrlsUnder = getUI().getCtrlsUnderPointer(); for(sint k = (sint)ctrlsUnder.size() - 1; k >= 0; --k) { IDisplayerUIHandle *handle = dynamic_cast(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