// 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