// 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 . #ifndef R2_TOOL_H #define R2_TOOL_H #include "nel/misc/smart_ptr.h" #include "nel/misc/array_2d.h" // #include "nel/gui/interface_element.h" #include "game_share/scenario_entry_points.h" // class CInterfaceManager; class CGroupContainer; namespace NLGUI { class CEventDescriptor; class CLuaObject; } class CGroupMap; namespace NLMISC { class CVector; } namespace DMS { class CDynamicMapClient; } namespace R2 { extern const uint32 DEFAULT_ENTITY_MIN_OPACITY; struct IDisplayerUIHandle; class CEditor; class CInstance; class CDynamicMapClient; /** Base class for manipulation tools found in the R2 editor * There's only one tool at a moment and mouse/keyboard events are routed to that tool * * \author Nicolas Vizerie * \author Nevrax France * \date 5/2005 */ class CTool : public CReflectableRefPtrTarget, public NLMISC::IClassable { public: class CWorldViewRay { public: NLMISC::CVector Origin; NLMISC::CVector Dir; NLMISC::CVector Right; NLMISC::CVector Up; bool OnMiniMap; bool Valid; public: CWorldViewRay() { Origin = NLMISC::CVector::Null; Dir = NLMISC::CVector::Null; Right = NLMISC::CVector::Null; Up = NLMISC::CVector::Null; OnMiniMap = false; Valid = false; } }; //\TODO nico find a better place for this enum TRayIntersectionType { NoIntersection, ValidPacsPos, InvalidPacsPos }; // typedef NLMISC::CSmartPtr TSmartPtr; // CTool(); virtual ~CTool() {} // // Init parameters from script virtual bool init(const CLuaObject &/* parameters */) { return true; } /** Get this tool name in the ui. This name is used to identify the tool in the ui (used by the r2.ToolUI:setActiveToolUIByName function defined in r2ed_ui.lua) * May return "" if there's no ui associated with that tool */ virtual const char *getToolUIName() const = 0; virtual bool isCreationTool() const = 0; virtual bool isPickTool() const {return false; } // // This update is called at each frame for the current tool just before rendering virtual void updateBeforeRender() {} // This update is called at each frame for the current tool just after rendering virtual void updateAfterRender() = 0; ////////////////////////// // EVENTS HANDLING // ////////////////////////// /** Entry point for events handling. * Methods where defined below for convenience for the most common events * like 'onMouseLeftButtonDown' or 'onMouseRightButtonUp'. * Default behaviour of this member function is to forward the event to these event handling methods. * * A deriver may handle events not listed below by redefining this method, * possibly calling its parent version for events he is not interested in. * * \return true if the event has been handled by the tool */ virtual bool handleEvent (const NLGUI::CEventDescriptor &eventDesc); // virtual void onFocusGained() {} // the app window gained the focus (there's no 'focus lost' event here because it reset current tool, so 'CTooll::cancel' will be called instead) // IMPORTANT : Reacting to this should be unnecessary, as lost focus reset the current tool, // defaulting to the 'SelectMove' tool, that handle this event correctly. virtual bool onMouseLeftButtonDown() { return false; } virtual bool onMouseLeftButtonUp() { releaseMouse(); return false; } virtual bool onMouseRightButtonDown() { return false; } virtual bool onMouseRightButtonUp() { return false; } virtual bool onMouseMove() { return false; } // call when this tool is just being activated virtual void onActivate() {} // special messages for shortcut keys virtual bool onDeleteCmd() { return false; } /** * Unlike the other onMousexxx method, these are actually called AFTER the camera event handling * has been done. Doing the same in onMouseRightButtonUp or onMouseRightButtonDown * instead would require that the user test if mouse button was not released after a camera * move (in which case the click should not be handled of course) * * Usually the onMousexxxButtonUp are ususeful when working in pair with the associated 'mouse button down' event. * Such events usually provoke a mouse capture (example : selectMoveTool) */ virtual bool onMouseLeftButtonClicked() { return false; } /** NB : if the onMouseRightButtonClicked isn't handled by the tool then * editor will show the context menu */ virtual bool onMouseRightButtonClicked(); // Called by editor just before a new tool is made current virtual void cancel() = 0; // when returning true -> ignore next click causing unselect virtual bool getPreviousToolClickEndFlag(bool /* clear */ = true) { return false; } // double click handling : // 'startDoubleClickCheck' should be called when the 'onMouseLeftButtonClicked' msg is handled // then checkDoubleClick' should be call on any subsequent onMouseLeftButtonDown. If // the result is true then double click should be handled void startDoubleClickCheck(); bool checkDoubleClick(); // test if one of the 'shift' keys is down static bool isShiftDown(); // test if one of the 'ctrl' keys is down static bool isCtrlDown(); ////////////////////// // HELPER FUNCTIONS // ////////////////////// /** check which instance is under the mouse, possibly fading player in / out * \param miniMapHandle if not NULL, pointer will be filled with the minimap ui element that is under the mouse * \return NULL if there's no instance under the mouse */ static CInstance *checkInstanceUnderMouse(IDisplayerUIHandle **miniMapHandle = NULL); // helper : handle mouse over instance: highlight them when mouse is over & change mouse cursor accordingly static void handleMouseOverInstance(const char *cursorDefault, const char *cursorOverUnselectedInstance, const char *cursorOverSelectedInstance); // handle player under cursor (fade in / fade out) static void handleMouseOverPlayer(bool over); /** Default right button down handling : * If an entity is highlighted, * then select it and pop its menu * \return true if the event was handled */ bool defaultRightButtonDownHandling(); /** Capture the mouse * - The ui won't receive events from the mouse * - Maintaining the left button down doesn't trigger camera rotation any more. * Useful for tools such as 'select', 'move' ... */ static void captureMouse(); static void releaseMouse(); static bool isMouseCaptured(); // shortcut to get the ui static CInterfaceManager &getUI(); // Get mouse position static void getMousePos(sint32 &x, sint32 &y) ; // Get mouse x position static sint32 getMouseX(); // Get mouse y position static sint32 getMouseY(); // Set the current mouse cursor static void setMouseCursor(const char *cursorTexture); static void setMouseCursor(const std::string &cursorTexture) { setMouseCursor(cursorTexture.c_str()); } /** Compute a view vector (with its direction z set to 1) from coordinate of the mouse on screen * If the mouse is on the island map, then a vector looking down from heights will be returned */ static void computeWorldViewRay(sint32 posX, sint32 posY, CWorldViewRay &dest); // specific test for the world map static TRayIntersectionType computeWorldMapIntersection(float x, float y, NLMISC::CVector &inter); // get current screen size static void getScreenSize(uint32 &scrW, uint32 &scrH); // get current screen width static uint32 getScreenWidth(); // get current screen height static uint32 getScreenHeight(); // see if a point is in screen static bool isInScreen(sint32 x, sint32 y); // test whether the mouse is over the user interface static bool isMouseOnUI(); // retriever ptr on world map in the ui static CGroupMap *getWorldMap(); // test whether the mouse is over the map static CGroupMap *isMouseOnWorldMap(); // test whether the mouse is over a container static CGroupContainer *isMouseOnContainer(); /** Compute collision of a segment with the landscape * \param inter If return type is different from 'NoIntersection', then 'inter' is filled with the collision position */ static TRayIntersectionType computeLandscapeRayIntersection(const CWorldViewRay &worldViewRay, NLMISC::CVector &inter); // Get pacs type at the given position, with the given threshold. static TRayIntersectionType getPacsType(const NLMISC::CVector &pos, float threshold, NLPACS::UGlobalPosition &destPos); // set context help for the current tool static void setContextHelp(const ucstring &contextHelp); // shortcut to get the interface to the server CDynamicMapClient &getDMC(); /** handle world map auto-panning feature, should be called whenever auto-pan should be done * dx & dy are filled with delta of the map for this frame */ void handleWorldMapAutoPan(sint32 &dx, sint32 &dy); // lua exports int luaIsPickTool(CLuaState &ls); // REFLECT_EXPORT_START(R2::CTool, CReflectable) REFLECT_LUA_METHOD("isPickTool", luaIsPickTool); REFLECT_EXPORT_END static NLMISC::CRGBA getInvalidPosColor(); /** For derivers : additionnal checking can be done on the pos to choose * Pos must at least be a valid pacs pos * Default will check with a radius of 0.5 meter */ static bool isValid2DPos(const NLMISC::CVector2f &pos); private: sint64 _DoubleClickStartTime; sint32 _DoubleClickX; sint32 _DoubleClickY; uint64 _AutoPanLastHandlingFrame; sint64 _AutoPanDelay; sint64 _NumPans; static bool _MouseCaptured; private: /** compute the nearest valid surface at a given position from the island heightmap * (heightmap must not be empty or an assertion is raised) * \return true if a valid surface was found */ static bool computeNearestValidSurfaceFromHeightMap(float x, float y, NLMISC::CVector &inter); // trace a ray though the scene, using precise camera collision first, island packed collisions then. static bool raytrace(const NLMISC::CVector &segmentStart, const NLMISC::CVector &dir, NLMISC::CVector &inter); static bool isIslandValidPos(const NLMISC::CArray2D &heightMap, const CScenarioEntryPoints::CCompleteIsland &islandDesc, float x, float y); }; } // R2 #endif