mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-13 13:24:49 +00:00
4996705ed7
--HG-- branch : gui-refactoring
1086 lines
44 KiB
C++
1086 lines
44 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/>.
|
|
|
|
#ifndef R2_EDITOR_H
|
|
#define R2_EDITOR_H
|
|
|
|
#include "../interface_v3/lua_object.h"
|
|
#include "instance.h"
|
|
#include "tool.h"
|
|
#include "../decal.h"
|
|
#include "../decal_anim.h"
|
|
#include "entity_custom_select_box.h"
|
|
#include "island_collision.h"
|
|
#include "prim_render.h"
|
|
//
|
|
#include "nel/misc/singleton.h"
|
|
#include "nel/misc/class_registry.h"
|
|
#include "nel/misc/time_nl.h"
|
|
#include "nel/misc/sstring.h"
|
|
#include "nel/misc/array_2d.h"
|
|
//
|
|
#include "dmc/dmc.h"
|
|
//
|
|
#include "game_share/object.h"
|
|
#include "game_share/scenario_entry_points.h"
|
|
#include "game_share/r2_types.h"
|
|
|
|
|
|
|
|
|
|
|
|
class CGroupTree;
|
|
class CEntityCL;
|
|
|
|
namespace NL3D
|
|
{
|
|
class CPackedWorld;
|
|
}
|
|
|
|
namespace R2
|
|
{
|
|
class CDynamicMapService;
|
|
class CUserComponentsManager;
|
|
class CEntitySorter;
|
|
|
|
// texture for the default mouse cursor
|
|
extern const char *DEFAULT_CURSOR;
|
|
|
|
|
|
class CDisplayerVisualEntity;
|
|
|
|
/** Ryzom Ring in game editor
|
|
*
|
|
* ///////////////////////////////////////////////////////////
|
|
* // TMP OVERVIEW OF R2 UI BEHAVIOR (WORK IN PROGRESS ...) //
|
|
* ///////////////////////////////////////////////////////////
|
|
*
|
|
*
|
|
* The ui can be in one of the following states :
|
|
*
|
|
* I CREATION MODE
|
|
* ===============
|
|
*
|
|
* No tool is displayed as "highlighted"
|
|
*
|
|
* 1) Mouse in 3D view
|
|
* --------------------
|
|
* The mouse cursor has a little star to indicate "creation mode".
|
|
* If creation is not possible at the mouse position, then there's is a little 'stop' icon shown
|
|
* No instance selection is done
|
|
*
|
|
* EVENTS :
|
|
* --------
|
|
* LCLICK -> create
|
|
* RCLICK -> cancel & restore default tool
|
|
*
|
|
* NB : we don't do creation on LDOWN or RUP in order to allow camera manipulation
|
|
*
|
|
* 2) Mouse over UI
|
|
* -----------------
|
|
* The mouse cursor still has a little star to indicate "creation mode".
|
|
* Ideally on some actions, the creation is canceled, and the default tool is backuped;
|
|
* NB nico : this is unclear to me atm, is it for button clicks only ?
|
|
* (for example just moving a slider do not seem a good reason to cancel the tool...)
|
|
* so no-op for now
|
|
* (TODO : exception for the minimap, if we want to be able to create by clicking on it ?)
|
|
*
|
|
* II CONTEXT MENU MODE
|
|
* ====================
|
|
*
|
|
* The standard mouse cursor is displayed.
|
|
*
|
|
* EVENTS :
|
|
* --------
|
|
* LDOWN / RDOWN -> cancel the menu
|
|
*
|
|
* III CAPTURED BY UI MODE
|
|
* =======================
|
|
* ... same as 'context menu mode'
|
|
*
|
|
* IV TOOL MODE
|
|
* =============
|
|
* The current tool icon is highlighted in the toolbar
|
|
*
|
|
* std tool includes :
|
|
* - Move
|
|
* - Rotate
|
|
* ...
|
|
*
|
|
* 1) Mouse in 3D view
|
|
* --------------------
|
|
* a ) Mouse over empty space in scene
|
|
* -----------------------------------
|
|
* The standard mouse cursor is displayed
|
|
*
|
|
* EVENTS :
|
|
* --------
|
|
* nothing to do here, camera event are handled by the 'UserControls' global object
|
|
* If the right button is up without being preceded by a camera move, then the context
|
|
* menu is shown
|
|
*
|
|
*
|
|
* b ) Mouse over an instance in scene that is not the selection
|
|
* -------------------------------------------------------------
|
|
* The mouse with a little circle is displayed
|
|
*
|
|
* EVENTS :
|
|
* --------
|
|
* RDOWN -> Test and see which one is best :
|
|
* - a new slection is done and the context menu is shown ?
|
|
* - nothing ? (e.g no selection and the context menu is trigerred by
|
|
* the 'UserControls' global object as usual ?
|
|
* RDOWN while maintained action : cancel the current action and restore
|
|
* state (same behaviour as most apps)
|
|
* LDOWN -> selection + tool dependent (instant action or maintained action)
|
|
* a maintained action (such as moving an entity) will usually
|
|
* capture the mouse.
|
|
* others -> tool dependent
|
|
*
|
|
* c ) Mouse over an instance in scene that the selection
|
|
* -------------------------------------------------------------
|
|
* The mouse shows the action that can be performed
|
|
* RDOWN -> same as previous
|
|
* others -> tool dependent
|
|
* RDOWN while maintained action : cancel the current action and restore
|
|
* state (same behaviour as most apps)
|
|
*
|
|
* NB : a tool may want to capture the mouse, in this case it must call CTool::captureMouse
|
|
* and CTool::releaseMouse when it is done
|
|
*
|
|
* REMARKS:
|
|
*
|
|
* - Maybe it would be cool to make camera manipulation totally orthogonal to the system,
|
|
* and transparently layered on top of it.
|
|
* This would requires using something like the middle button or some shift / control combination
|
|
* to avoid conflict with the previous events ...
|
|
*
|
|
* 2) Mouse over UI
|
|
* -----------------
|
|
* The mouse is dipslayed as usual
|
|
* Events are taken in account by the UI, not by the tool
|
|
* This is transparent for CTool derivers, because mouse events won't reach them
|
|
* Clicking in the interface doesn't change the current tool
|
|
*
|
|
*
|
|
* \author Nicolas Vizerie
|
|
* \author Nevrax France
|
|
* \date 5/2005
|
|
*/
|
|
|
|
|
|
|
|
class CEditor : public NLMISC::CSingleton<CEditor>
|
|
{
|
|
public:
|
|
enum TMode { NotInitialized = 0, EditionMode, GoingToDMMode, GoingToEditionMode, TestMode, DMMode,
|
|
AnimationModeLoading, AnimationModeWaitingForLoading, AnimationModeDm, AnimationModePlay,
|
|
AnimationModeGoingToDm, AnimationModeGoingToPlay, ModeCount };
|
|
|
|
enum TAccessMode { AccessEditor = 0, AccessDM, AccessOutlandOwner, AccessModeCount, AccessModeUnknown = AccessModeCount };
|
|
|
|
////////////
|
|
// OBJECT //
|
|
////////////
|
|
|
|
CEditor();
|
|
~CEditor();
|
|
|
|
// Init what's need to be initialized depending on the configuration
|
|
void autoConfigInit(bool serverIsRingSession);
|
|
|
|
// Release what's need to be released depending on the configuration
|
|
void autoConfigRelease(bool serverIsRingSession);
|
|
|
|
// initialisation of modules & gw
|
|
void initModules(bool connectDMC);
|
|
void releaseModules();
|
|
|
|
// wait first scenario to be received (may be an empty one ...)
|
|
void waitScenario();
|
|
|
|
TAccessMode getAccessMode() const { return _AccessMode; }
|
|
void setAccessMode(TAccessMode mode);
|
|
|
|
// Initialisation of the editor
|
|
void init(TMode initialMode, TAccessMode accessMode);
|
|
|
|
// reload xml, lua & translation & reset. scenario is preserved
|
|
void reset();
|
|
// reload xml, lua & translation & reset. scenario is reseted
|
|
void resetScenario();
|
|
// reload xml, lua & translation & reset. Last save scenario is reloaded
|
|
void reloadScenario();
|
|
// load / reload the work language file
|
|
void loadLanguageFile();
|
|
//
|
|
void release();
|
|
// clear all content in the map, client side
|
|
void clearContent();
|
|
//
|
|
bool isInitialized() const { return _Initialized; }
|
|
// (re)load the ui if its date changed
|
|
void reloadUI();
|
|
|
|
// set current mode of the editor
|
|
void setMode(TMode mode);
|
|
TMode getMode() const { return _Mode; }
|
|
|
|
// test if user is DMing (be it masterless or not)
|
|
bool isDMing() const;
|
|
|
|
/////////////////////
|
|
// UPDATE & EVENTS //
|
|
/////////////////////
|
|
|
|
/** Events handling
|
|
* Handle an user input event
|
|
* \return true if the event was handled by the editor
|
|
*/
|
|
bool handleEvent (const NLGUI::CEventDescriptor &eventDesc);
|
|
|
|
// Handle copy, reaches the editor if no edit box is currently active
|
|
void copy();
|
|
|
|
// Handle paste, reaches the editor if no edit box is currently active
|
|
void paste();
|
|
|
|
// An entity has been selected with the standard selection system (usually in test mode)
|
|
void inGameSelection(const CLFECOMMON::TCLEntityId &slot);
|
|
|
|
void onContinentChanged();
|
|
|
|
|
|
/** update of editor just before precamera call for entities
|
|
*/
|
|
void updatePreCamera();
|
|
/** update of editor (after update of entities just before render)
|
|
*/
|
|
void updateBeforeRender();
|
|
/** Update of editor (just after render of the scene and entities update and before rendering of the interface)
|
|
* This is the place to display additionnal visual infos (slection bbox etc ...)
|
|
*/
|
|
void updateAfterRender();
|
|
|
|
// called after main loop 'swap buffer'
|
|
void updateBeforeSwapBuffer();
|
|
|
|
/////////////////////////////////////////////
|
|
// CLIENT OBJECTS (yet named 'instances' ) //
|
|
/////////////////////////////////////////////
|
|
|
|
// NB about this tmp mess
|
|
// 'CInstance' are client counterparts of 'CObjectTable'
|
|
|
|
// set current selected instance in the editor
|
|
void setSelectedInstance(CInstance *inst);
|
|
void forceSetSelectedInstance(CInstance *inst);
|
|
// get current selected instance in the editor
|
|
CInstance *getSelectedInstance() const;
|
|
// set current highlighted instance in the editor
|
|
void setFocusedInstance(CInstance *inst);
|
|
// get current higlighted entity in the editor (with mouseoverit)
|
|
CInstance *getFocusedInstance() const;
|
|
//
|
|
bool hasInstance(const CInstance *instance) const;
|
|
// Check if there's an instance with the given id
|
|
bool hasInstanceWithId(const TInstanceId &id) const;
|
|
// Get an instance from its id
|
|
CInstance *getInstanceFromId(const TInstanceId &id) const;
|
|
|
|
/** Create Visual Properties for an instance
|
|
* Return false if can not retrieve VisualProperties
|
|
*/
|
|
bool getVisualPropertiesFromObject(CObject* instance, SPropVisualA& vA, SPropVisualB& vB, SPropVisualC& vC);
|
|
|
|
/** Generate a new user readable name for an object in the scene
|
|
* If the name if already postfixed by a number, it will be stripped.
|
|
* Example : Npc 1, Npc 2, Group 1, Bandit camp 1 etc.
|
|
* Localised base name is given (for example "road" if using english)
|
|
* The function looks in all road instances and finds the first free number.
|
|
*
|
|
*
|
|
* \param baseClass Filter base class for the search : only class that derived from (or are of ) that class will
|
|
* be considered when looking for a new name
|
|
*/
|
|
ucstring genInstanceName(const ucstring &baseName);
|
|
|
|
// test if an instance name is post fixed by a number
|
|
static bool isPostFixedByNumber(const ucstring &baseName);
|
|
|
|
/** test if a ucstring has the format of a generated name that is : baseName + " " + index
|
|
* Return index on success or -1 on failure
|
|
*/
|
|
static sint getGeneratedNameIndex(const std::string &nameUtf8, const std::string &baseNameUtf8);
|
|
|
|
/** Set a local user value (we name it a "cookie") that should be attached to an instance at creation time
|
|
* (that is, when the object will be created on client -> when the server creation msg is received)
|
|
* The value will be stored in the 'User' lua table.
|
|
* The 'User' table is attached to each instances in the editor and is Read/Write
|
|
* (other properties in client and server and are read only, and should be modified by sending a requestxxx network message)
|
|
* This table is local only (that is, not seen by the server, nor by other clients editing the same map).
|
|
*
|
|
* Example of use :
|
|
*
|
|
* We want that when a road is created, a dialog pop to enter the name of the road.
|
|
*
|
|
* First we must be able to distinguish between roads created by ourselves and road created by
|
|
* other persons editing the map.
|
|
*
|
|
* Secondly we don't want that all roads to have this behaviour (for example, a road in a more complex system
|
|
* would be named automatically)
|
|
*
|
|
*
|
|
* In lua script it would look like :
|
|
*
|
|
* At creation request time :
|
|
* ==========================
|
|
*
|
|
* local road = r2.newComponent("Road")
|
|
* r2.setCookie(road.InstanceId, "AskName", true)
|
|
* r2.requestInsertNode(... -> send newtork creation message for the road
|
|
*
|
|
*
|
|
* When creation message is received (may be placed in the 'onCreate' method of a displayer attached to that instance)
|
|
* ==================================================================================================================
|
|
*
|
|
* function roadDisplayer:onCreate(road)
|
|
* if road.User.AskName == true then
|
|
* -- road was created by this client
|
|
* -- dialog to enter the name was asked
|
|
* -- => popup the dialog
|
|
* ....
|
|
* end
|
|
* end
|
|
*
|
|
* Other example : when an act is created by the user it becomes the current act
|
|
*
|
|
*/
|
|
void setCookie(const TInstanceId &instanceId, const std::string &key, CLuaObject &value);
|
|
// helpers to add cookie of predefined type (to be completed ..)
|
|
void setCookie(const TInstanceId &instanceId, const std::string &key, bool value);
|
|
|
|
|
|
struct IInstanceObserver : public NLMISC::CRefCount // refptr'ed observers
|
|
{
|
|
virtual ~IInstanceObserver() {}
|
|
|
|
typedef NLMISC::CRefPtr<IInstanceObserver> TRefPtr;
|
|
// called when the watched instance has been created
|
|
virtual void onInstanceCreated(CInstance &/* instance */) {}
|
|
virtual void onInstanceErased(CInstance &/* instance */) {}
|
|
virtual void onInstancePreHrcMove(CInstance &/* instance */) {}
|
|
virtual void onInstancePostHrcMove(CInstance &/* instance */) {}
|
|
virtual void onPreHrcMove(CInstance &/* instance */) {}
|
|
virtual void onPostHrcMove(CInstance &/* instance */) {}
|
|
virtual void onInstanceEraseRequest(CInstance &/* instance */) {}
|
|
virtual void onAttrModified(CInstance &/* instance */, const std::string &/* attrName */, sint32 /* attrIndex */) {}
|
|
};
|
|
|
|
typedef sint TInstanceObserverHandle;
|
|
|
|
enum { BadInstanceObserverHandle = -1 };
|
|
|
|
/** add a watch to know when an instance is created (the instance is identified by its instance id)
|
|
* returns a handle for removal, or 'BadInstanceObserverHandle' if creation failed
|
|
*/
|
|
TInstanceObserverHandle addInstanceObserver(const TInstanceId &instanceId, IInstanceObserver *observer);
|
|
// Get a pointer to an observer from its handle (or NULL if not found)
|
|
IInstanceObserver *getInstanceObserver(TInstanceObserverHandle handle);
|
|
// Remove an instance observer from its handle (but do not delete it). Return the pointer to the observer
|
|
IInstanceObserver *removeInstanceObserver(TInstanceObserverHandle handle);
|
|
/** Test from a pointer if the object is currently observing an instance
|
|
* NB : slow because of linear search
|
|
*/
|
|
bool isInstanceObserver(IInstanceObserver *observer) const;
|
|
|
|
|
|
|
|
////////////////////////
|
|
// LUA R2 ENVIRONMENT //
|
|
////////////////////////
|
|
|
|
// get table for global variables in lua environment (equivallent to _G lua variable)
|
|
CLuaObject &getGlobals() { return _Globals; }
|
|
// get table for registry in lua environment
|
|
CLuaObject &getRegistry() { return _Registry; }
|
|
// get lua classes (the r2.Classes table)
|
|
CLuaObject getClasses() throw(ELuaError);
|
|
// get R2 environment (the 'r2' table into lua global environment)
|
|
CLuaObject &getEnv();
|
|
// get the config table (that is the 'r2.Config' table)
|
|
CLuaObject getConfig();
|
|
// get a reference to the lua state object
|
|
CLuaState &getLua();
|
|
/** Project a CObjectTable into lua (accessor is pushed onto the lua stack)
|
|
* property of the table, which is a C++ object, will be accessible from lua (by using metatable)
|
|
*/
|
|
void projectInLua(const CObjectTable *table);
|
|
|
|
// get the default feature for the current selected act
|
|
CInstance *getDefaultFeature();
|
|
// get the default feature for the given act
|
|
CInstance *getDefaultFeature(CInstance *act);
|
|
|
|
// set the current act (NULL being synonimous with the base act)
|
|
void setCurrentAct(CInstance *act);
|
|
void setCurrentActFromTitle(const std::string &title);
|
|
CInstance *getCurrentAct() const { return _CurrentAct; }
|
|
CInstance *getBaseAct() const { return _BaseAct; }
|
|
|
|
/** helper : calls a function in the lua r2ed environment
|
|
* Arguments must have already been pushed on the stack
|
|
* If the call fails then an error msg is printed in the log, and no arguments are returned
|
|
*
|
|
* \param funcName name of the function to call (must resides in the r2ed table)
|
|
* \param numArgs, numbers of arguments that the functions will receive
|
|
* \param numRet : Number of parameters that the function returns
|
|
* As usual the stack will be adjusted to that size after the call
|
|
*/
|
|
bool callEnvFunc(const char *funcName, int numArgs, int numRet = 0);
|
|
// Behave like 'callEnvFunc', but call a method instead (r2 is passed as the 'self' parameter, that is)
|
|
bool callEnvMethod(const char *funcName, int numArgs, int numRet = 0);
|
|
|
|
//////////////////////
|
|
// SERVER COMMANDS //
|
|
//////////////////////
|
|
|
|
// access to interface with server
|
|
CDynamicMapClient &getDMC() const { nlassert(_DMC); return *_DMC; }
|
|
|
|
/** Set a property of an object locally. No network msg is sent, but modification events are triggered to signal that
|
|
* the object has been modified (rooted to one of the CDisplayerBase derived object, attached to the object being modified. see displayer_base.h).
|
|
* Changes to local value must be commited or cancelled when edition is finish by calling 'requestCommitLocalNode' or
|
|
* 'requestRollbackLocalNode'
|
|
* Typical use is by the slider widget : real value is sent accross the network only when the user release the mouse, but
|
|
* local update of object property related to the slider is done each time the slider is modified before release.
|
|
* During the maintained action, no network message are desireable.
|
|
* NB : a copy of input parameter 'value' will be done. Caller is responsible for deleting 'value' after use.
|
|
*/
|
|
void requestSetLocalNode(const std::string& instanceId, const std::string& attrName, const CObject *value);
|
|
void requestCommitLocalNode(const std::string& instanceId, const std::string& attrName);
|
|
void requestRollbackLocalNode(const std::string& instanceId, const std::string& attrName);
|
|
|
|
/////////////
|
|
// DECALS //
|
|
/////////////
|
|
void showHighlightDecal(const NLMISC::CVector &pos, float scale);
|
|
void showSelectDecal(const NLMISC::CVector &pos, float scale);
|
|
void addSelectingDecal(const NLMISC::CVector &pos, float scale);
|
|
void showSelectBox(const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat);
|
|
void showHighlightBox(const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat);
|
|
|
|
|
|
/////////////////
|
|
// COLLISIONS //
|
|
/////////////////
|
|
|
|
CIslandCollision &getIslandCollision() { return _IslandCollision; }
|
|
|
|
|
|
///////////
|
|
// MISC //
|
|
///////////
|
|
|
|
// Shortcut to the GUI
|
|
static CInterfaceManager &getUI();
|
|
//
|
|
/** Set current edition tool. NULL will reset to the default tool (selection tool)
|
|
*/
|
|
void setCurrentTool(CTool *tool);
|
|
// Get current tool for edition
|
|
CTool *getCurrentTool() const { return _CurrentTool; }
|
|
/** Helper to execute a lua script
|
|
* \param filename name of the lua script file
|
|
* \param fileDescText short description of the script function (for error messages)
|
|
* \return true on success
|
|
*/
|
|
bool doLuaScript(const char *filename, const char *fileDescText);
|
|
// helper : create an entity in scene at the given slot & position, replacing any previous entity in that slot
|
|
|
|
static CEntityCL *createEntity(uint slot, const NLMISC::CSheetId &sheetId, const NLMISC::CVector &pos, float heading, const std::string & permanentStatutIcon=std::string(""));
|
|
// helper : get an instance from a CEntityCL pointer
|
|
CInstance *getInstanceFromEntity(CEntityCL *entity) const;
|
|
// helper : clear content of the debug window
|
|
void clearDebugWindow();
|
|
// display the editor contextual menu
|
|
void displayContextMenu();
|
|
// tmp, for debug
|
|
void dumpInstances();
|
|
// helper : return the entity under the mouse
|
|
CInstance *getInstanceUnderPos(float x, float y, float distSelection, bool &isPlayerUnderCursor);
|
|
// helper : test intersection between an entity and a ray
|
|
static float preciseEntityIntersectionTest(CEntityCL &entity, const NLMISC::CVector &worldRayStart, const NLMISC::CVector &worldRayDir);
|
|
// Tmp show the connexion window and display a msg in it. An empty msg will close the window
|
|
static void connexionMsg(const std::string &stringId);
|
|
TEntityCustomSelectBoxMap &getEntityCustomSelectBoxMap() { return _EntityCustomSelectBoxMap; }
|
|
// from a pointer on an entity, retrieve its local selection bbox (possibly redefined in r2_ui_custom_boxes_data.lua
|
|
const NLMISC::CAABBox &getLocalSelectBox(CEntityCL &entity) const;
|
|
// from a pointer on an entity, retrieve its local selection bbox (possibly redefined in r2_ui_custom_boxes_data.lua
|
|
NLMISC::CAABBox getSelectBox(CEntityCL &entity) const;
|
|
// check if there's room left to create new objects in the scene
|
|
sint getLeftQuota();
|
|
|
|
|
|
bool checkRoomLeft();
|
|
// display an error msg to prompt the user to make room for new objects in its scenario
|
|
void makeRoomMsg();
|
|
// check if there is room in specific category if not display an error msg
|
|
// verify ther is at least size object in category StaticObject or AiObject
|
|
bool verifyRoomLeft(uint aiCost, uint staticCost);
|
|
|
|
// rename delete auto_save9, rename auto_save1.r2 to auto_save2.r2 and so until auto_save8.r2, copy auto_save.r2 to auto_save1.r2,
|
|
void autoSave();
|
|
|
|
/** Season driven from editor
|
|
* This value is usually 'Unknwown' unless the mode is "edit'.
|
|
* In this case, the value depends on the act being edited
|
|
*/
|
|
enum TSeason { Automatic = 0, Spring, Summer, Autumn, Winter, UnknownSeason };
|
|
TSeason getSeason() const;
|
|
void tpReceived();
|
|
|
|
bool getFixedLighting() const { return _FixedLighting; }
|
|
void setFixedLighting(bool enabled);
|
|
|
|
/** Get current infos of a plot item plot display in the editor
|
|
* Plot items are items with SCROLL_R2 as family. Each sheet can be used only once in
|
|
* a scenario (meaning that each icon can only be seen for a single plot item).
|
|
* The name of a plot item can also be change by using the lua command r2:setPlotItemName(sheetId, ucName)
|
|
*/
|
|
void setPlotItemInfos(const TMissionItem &infos);
|
|
const TMissionItem *getPlotItemInfos(uint32 sheetId) const;
|
|
|
|
|
|
// convert name of a class in R2 to a unique index (-1 if not found)
|
|
sint classToIndex(const std::string &className) const;
|
|
// this if one class id 'indexOf' another from their index
|
|
bool isKindOf(sint testedClassIndex, sint kindClassIndex) const;
|
|
// from one class index, returns the index to the derived class (-1 if not found)
|
|
sint getBaseClass(sint derivedClass) const;
|
|
|
|
|
|
///////////////////////////////
|
|
// > 254 entities management //
|
|
///////////////////////////////
|
|
|
|
CEntitySorter *getEntitySorter() const;
|
|
|
|
private:
|
|
|
|
CEntitySorter *_EntitySorter;
|
|
|
|
|
|
// mapping from sheet id to plot item name
|
|
std::map<uint32, TMissionItem> _PlotItemInfos;
|
|
CIslandCollision _IslandCollision;
|
|
|
|
//
|
|
bool _SerializeUIConfig;
|
|
TMode _Mode;
|
|
TAccessMode _AccessMode;
|
|
|
|
typedef std::map<const CObjectTable *, CInstance::TSmartPtr> TInstanceMap;
|
|
private:
|
|
|
|
CLuaObject _Globals; // match to the '_G' lua global variable
|
|
CLuaObject _Registry;
|
|
CLuaObject _Env;
|
|
CLuaObject _Config;
|
|
CLuaObject _ObjectProjectionMetatable;
|
|
CLuaObject _LuaUIMainLoop;
|
|
//
|
|
TInstanceMap _Instances;
|
|
CInstance *_SelectedInstance;
|
|
CInstance *_FocusedInstance;
|
|
|
|
CHashMap<std::string, uint> _ClassNameToIndex; // Map each class name to an unique index (filled at init)
|
|
NLMISC::CArray2D<uint8> _KindOfTable; // Table to test if one class derives from another (filled at init)
|
|
std::vector<sint> _BaseClassIndices; // for each class, give index of thebase class (vector ordered by classes indices)
|
|
|
|
typedef CHashMultiMap<ucstring, CInstance *, NLMISC::CUCStringHashMapTraits> TInstanceByName;
|
|
|
|
bool _MaxVisibleEntityExceededFlag;
|
|
|
|
// instance sorted by name
|
|
class CSortedInstances
|
|
{
|
|
public:
|
|
void insert(const ucstring &name, CInstance *inst);
|
|
void remove(CInstance *inst);
|
|
bool contains(CInstance *inst) const;
|
|
TInstanceByName::iterator begin() { return _ByName.begin(); }
|
|
TInstanceByName::iterator end() { return _ByName.end(); }
|
|
private:
|
|
|
|
typedef std::map<CInstance *, TInstanceByName::iterator> TInstanceToIter;
|
|
//
|
|
TInstanceByName _ByName;
|
|
TInstanceToIter _InstanceToIter;
|
|
};
|
|
// typedef TInstanceByDispName; // for usage by CInstance
|
|
// list of instances for each classes (ordered by class index)
|
|
std::vector<CSortedInstances> _InstancesByDispName; // instances sorted by their display name (private use)
|
|
|
|
//priv for debug
|
|
bool isRegisteredByDispName(CInstance *inst) const;
|
|
|
|
// Cookies (local objects attached to instances at creation time, see setCookie)
|
|
struct CCookie
|
|
{
|
|
std::string Key;
|
|
CLuaObject Value;
|
|
};
|
|
typedef std::list<CCookie> TCookieList; // for a single instance, map each key to its value
|
|
typedef std::map<TInstanceId, TCookieList> TCookieMap;
|
|
TCookieMap _Cookies;
|
|
//
|
|
CInstance::TRefPtr _CurrentAct;
|
|
CInstance::TRefPtr _BaseAct;
|
|
CInstance::TRefPtr _ScenarioInstance;
|
|
CObjectTable *_Scenario;
|
|
std::string _WantedActOnInit;
|
|
//
|
|
friend class CDynamicMapClientEventForwarder;
|
|
friend class CAHR2QuitConnectingScreen;
|
|
CDynamicMapClient *_DMC; // replication of server map state on that client
|
|
CDynamicMapService *_DMS; // the server (hosted in local for now)
|
|
CInstance::TRefPtr _LastInstanceUnderPos;
|
|
//
|
|
CTool::TSmartPtr _CurrentTool;
|
|
static bool _ReloadWanted;
|
|
//
|
|
CDecal _HighlightDecal;
|
|
CDecalAnim _HighlightDecalAnim;
|
|
CDecal _SelectDecal;
|
|
CDecalAnim _SelectDecalAnim;
|
|
CDecalAnim _SelectingDecalAnim;
|
|
CDecal _PionneerDecal;
|
|
// alternative selection for huge element like particle systems, display a box on ground rather than
|
|
// the selection circle
|
|
CPrimRender _SelectBox;
|
|
CPrimRender _HighlightBox;
|
|
//
|
|
NLMISC::TTime _LastAutoSaveTime;
|
|
CDecalAnim _PionneerDecalAnim;
|
|
struct CSelectingDecal : public NLMISC::CRefCount
|
|
{
|
|
CDecal Decal;
|
|
sint64 EndDate;
|
|
NLMISC::CVector Pos;
|
|
float Scale;
|
|
};
|
|
std::vector<NLMISC::CSmartPtr<CSelectingDecal> > _SelectingDecals;
|
|
sint64 _DecalRefTime; // reference time for "decals" animation
|
|
//
|
|
bool _EnteredInSetSelectedInstance; // prevent recursive call from CEditor::setSelectedInstance
|
|
bool _Initialized;
|
|
bool _ForceDesktopReset[4];
|
|
// cache to avoid to reparse the ui : last modification date of ui files
|
|
std::map<std::string, uint32> _LastUIModificationDate;
|
|
TEntityCustomSelectBoxMap _EntityCustomSelectBoxMap;
|
|
/** system for local generation of name : for each kind of name, gives the locally allocated ids
|
|
* -> Used to generate name for instances, taking in account instance that have not been added to the scene yet.
|
|
* (that is, requestSetNode message has been sent, but server has not added object to the scenario yet)
|
|
*/
|
|
typedef std::set<uint> TNameSet;
|
|
typedef std::map<std::string, TNameSet> TGeneratedNameMap;
|
|
TGeneratedNameMap _LocalGeneratedNames;
|
|
|
|
// instance observers
|
|
|
|
typedef std::multimap<TInstanceId, IInstanceObserver::TRefPtr> TInstanceObserverMap;
|
|
TInstanceObserverMap _InstanceObservers;
|
|
typedef std::map<TInstanceObserverHandle, TInstanceObserverMap::iterator> TInstanceObserverHandleMap;
|
|
TInstanceObserverHandleMap _InstanceObserverHandles; // map each observer handle into an entry in the map
|
|
TInstanceObserverHandle _InstanceObserverHandleCounter; // current handle to generate when adding a new observer
|
|
|
|
|
|
TSeason _Season;
|
|
bool _FixedLighting;
|
|
bool _IsWaitingTPForSeasonChange;
|
|
bool _UpdatingScenario;
|
|
bool _WillTP; // true if a teleport should be expected after the scenario has been updated
|
|
// in this case, first season change is ignored, because it is done during the teleport
|
|
|
|
bool _ScenarioReceivedFlag; // for the wait screen ...
|
|
bool _TPReceivedFlag; // for the wait screen ...
|
|
bool _WaitScenarioScreenWanted; // lua requests that we display the 'wait scenario' screen
|
|
bool _WaitScenarioScreenActive; // the 'wait scenario' screen is being displayed
|
|
bool _EditionModeDisconnectedFlag;
|
|
CObject *_NewScenario; // new scenario that will be updated just after the wait screen is over
|
|
uint32 _NewScenarioInitialAct;// the start at which the user start an edition session (can be ~= from 1 after a test session)
|
|
bool _PostponeScenarioUpdated;
|
|
|
|
// Contextual selection
|
|
//
|
|
// We keep track of the last selected 'logic entity' (npc most of the time),
|
|
// and the last list of primitive through which its current activity sequence goes
|
|
// This way we can handle the following scenario :
|
|
// - create a npc
|
|
// - make him wander in zone A then zone B
|
|
// - select contextual visibility for primitive
|
|
// - click on npc : both zone shows
|
|
// - click on zone A : zone B dissapear -> strange
|
|
// By keeping the last list of contextual primitive, we ensure that contextual selection
|
|
// remains visible when one click on one element in the currently displayed sequence
|
|
CInstance::TRefPtr _LastContextualLogicEntity;
|
|
std::vector<CInstance::TRefPtr> _LastContextualPrims;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
/////////////////////////////
|
|
// NETWORK EVENTS HANDLING //
|
|
/////////////////////////////
|
|
|
|
void nodeErased(const std::string& instanceId, const std::string& attrName, sint32 position);
|
|
|
|
void nodeInserted(const std::string& instanceId, const std::string& attrName, sint32 position,
|
|
const std::string& key, CObject* value);
|
|
|
|
virtual void nodeSet(const std::string& instanceId, const std::string& attrName, CObject* value);
|
|
|
|
|
|
void nodeMoved(const std::string& instanceId, const std::string& attrName, sint32 position,
|
|
const std::string& destInstanceId, const std::string& destAttrName, sint32 destPosition);
|
|
|
|
void scenarioUpdated(CObject* highLevel, bool willTP, uint32 startingActIndex);
|
|
|
|
// send the needed events to tell that the attr at 'attrName' inside 'parentInstance' (possibly at position (indexInArray') has been modified
|
|
void onAttrModified(CInstance &parentInstance, const std::string &attrName, sint32 indexInArray = -1);
|
|
|
|
// send the needed events to tell that an object has been modified (& propagate to parents)
|
|
void onAttrModified(const CObject *value);
|
|
|
|
|
|
void onResetEditionMode();
|
|
void onEditionModeConnected( uint32 userSlotId, uint32 adventureId, CObject* highLevel, const std::string& versionName, bool willTP, uint32 initialActIndex);
|
|
void onAnimationModeConnected(const CClientMessageAdventureUserConnection& connected);
|
|
void onEditionModeDisconnected();
|
|
void onTestModeConnected();
|
|
// deconnect from test or play
|
|
void onTestModeDisconnected(TSessionId sessionId, uint32 lasAct, TScenarioSessionType sessionType);
|
|
|
|
|
|
///////////////////////////////////////////////////////
|
|
// EDITOR OBJECTS (instances) / CObjectTable mapping //
|
|
///////////////////////////////////////////////////////
|
|
|
|
public:
|
|
// Get a CObjectTable from its id
|
|
const CObjectTable *getObjectTableFromId(const TInstanceId &id) const;
|
|
private:
|
|
// Erase the current Scenario (and block outgoing message)
|
|
void eraseScenario();
|
|
void onErase(CObject *object);
|
|
void onErase(CObject *object, bool &foundInBase, std::string &nameInParent);
|
|
/** Create a new CInstance for the given CObject
|
|
* - Displayer are attached
|
|
* - Object is inserted in the object map
|
|
* - 'onCreate' msg is sent
|
|
* 'obj' must be a table or it fails
|
|
*/
|
|
void createNewInstanceForObjectTable(const CObject *obj);
|
|
void createNewInstanceForObjectTableInternal(const CObject *obj);
|
|
void onPostCreate(const CObject *obj);
|
|
|
|
void waitScenarioScreen();
|
|
|
|
public:
|
|
/** private : retrieve lua 'User' table attached to an object (a read / write table)
|
|
* The table is pushed on stack
|
|
*/
|
|
static void getLuaUserTableFromObject(CLuaState &ls, const CObjectTable &table);
|
|
private:
|
|
|
|
//////////////////////////////////////
|
|
// EDITOR FUNCTIONS EXPORTED TO LUA //
|
|
//////////////////////////////////////
|
|
static int luaGetVisualPropertiesFromInstanceId(CLuaState &ls);
|
|
static int luaGetSelectedInstanceId(CLuaState &ls);
|
|
static int luaGetSelectedInstance(CLuaState &ls);
|
|
static int luaSetSelectedInstanceId(CLuaState &ls);
|
|
static int luaSetCurrentTool(CLuaState &ls);
|
|
static int luaGetCurrentTool(CLuaState &ls);
|
|
static int luaGetInstanceFromId(CLuaState &ls);
|
|
static int luaDisplayContextMenu(CLuaState &ls);
|
|
static int luaConnectAsCreator(CLuaState &ls);
|
|
static int luaDofile(CLuaState &ls); // equivalent of the 'dofile' lua function, but with more info output
|
|
static int luaTryFile(CLuaState &ls); // same as try file, but do not throw an exception on error, just return an error message and return false
|
|
static int luaSetEntityCustomSelectBox(CLuaState &ls);
|
|
static int luaGetEntityCustomSelectBox(CLuaState &ls);
|
|
static int luaChoosePos(CLuaState &ls);
|
|
static int luaSnapPosToGround(CLuaState &ls); // takes x, y, z as parameters and return the snapped position
|
|
static int luaGetUserEntityPosition(CLuaState &ls);
|
|
static int luaGetUserEntityFront(CLuaState &ls);
|
|
static int luaRequestSetLocalNode(CLuaState &ls);
|
|
static int luaRequestCommitLocalNode(CLuaState &ls);
|
|
static int luaRequestRollbackLocalNode(CLuaState &ls);
|
|
static int luaSetCurrentActFromId(CLuaState &ls);
|
|
static int luaGetCurrentAct(CLuaState &ls);
|
|
static int luaAddInstanceObserver(CLuaState &ls); // param 1 = instance id of the instance to observe
|
|
// param 2 = table of an object that will receive notifications. The table should contain methods
|
|
// with the same names than those found in 'IInstanceObserver' (plus parameters are the same)
|
|
// the method returns a handle for future deletion
|
|
static int luaRemoveInstanceObserver(CLuaState &ls); // remove an observer that was previously added by a 'addInstanceObserver'
|
|
// param 1 = the handle returned by 'addInstanceObserver' at the registration time
|
|
// returns a reference to the observer that was registered
|
|
static int luaGenInstanceName(CLuaState &ls); // calls CEditor::genInstanceName, same parameters
|
|
// NB : return has type 'ucstring', so may need to call :toUtf8() in the lua script
|
|
static int luaIsPostFixedByNumber(CLuaState &ls);
|
|
static int luaIsClearingContent(CLuaState &ls);
|
|
static int luaSetCookie(CLuaState &ls); // same than CEditor::setCookie
|
|
static int luaIsCreature(CLuaState &ls);
|
|
static int luaSetEditorSeason(CLuaState &ls); // set the weather to display when editing
|
|
static int luaSetFixedLighting(CLuaState &ls);
|
|
static int luaGetFixedLighting(CLuaState &ls);
|
|
static int luaSetPlotItemInfos(CLuaState &ls);
|
|
static int luaIsCurrentSelectionPlayer(CLuaState &ls);
|
|
static int luaFindEmptyPlace(CLuaState &ls);
|
|
static int luaIsInIslandRect(CLuaState &ls); // test if pos is in the current island (not necessarily a valid pos, but inside the island rect)
|
|
// takes x, y as entry, returns true on success
|
|
|
|
static int luaGetCurrentIslandName(CLuaState &ls);
|
|
|
|
static int luaWaitScenarioScreen(CLuaState &ls); // display the wait screen after the scenario creation has been launched
|
|
static int luaIsScenarioUpdating(CLuaState &ls);
|
|
|
|
// undo / redo possible ?
|
|
static int luaCanUndo(CLuaState &ls);
|
|
static int luaCanRedo(CLuaState &ls);
|
|
// return the name of the editer
|
|
static sint luaGetUserEntityName(CLuaState &ls);
|
|
|
|
static int luaGetStartingAnimationFilename(CLuaState &ls);
|
|
|
|
static int luaKickCharacter(CLuaState &ls);
|
|
static int luaUnkickCharacter(CLuaState &ls);
|
|
static int luaTeleportToCharacter(CLuaState &ls);
|
|
static int luaEnumInstances(CLuaState &ls);
|
|
|
|
|
|
void connect();
|
|
|
|
// remove all object from the entity manager (but the player)
|
|
static void removeAllEntitySlots();
|
|
|
|
|
|
////////////////
|
|
// PLOT ITEMS //
|
|
////////////////
|
|
|
|
public:
|
|
static uint getMaxNumPlotItems();
|
|
static NLMISC::CCDBNodeLeaf *getPlotItemSheetDBLeaf(uint index);
|
|
static bool getIsStartingScenario() { return _IsStartingScenario; }
|
|
bool isClearingContent() const { return _ClearingContent; }
|
|
|
|
private:
|
|
void initPlotItems();
|
|
void initReferencePlotItems();
|
|
static void initDummyPlotItems();
|
|
void resetPlotItems();
|
|
static void setReferencePlotItemSheet(uint index, uint32 sheetId);
|
|
static NLMISC::CCDBNodeLeaf *getRefPlotItemSheetDBLeaf(uint index);
|
|
|
|
//////////
|
|
// MISC //
|
|
//////////
|
|
void setMaxVisibleEntityExceededFlag(bool on);
|
|
void backupRequestCommands();
|
|
void restoreRequestCommands();
|
|
void setForceDesktopReset(bool force);
|
|
void setUIMode(uint8 mode);
|
|
bool loadUIConfig(const std::string &prefix);
|
|
void loadStandardUI();
|
|
void saveUIConfig();
|
|
void hideRingWindows();
|
|
std::string getUIPrefix(TMode mode) const;
|
|
void loadKeySet(const std::string &keySet);
|
|
static std::string getKeySetPrefix(TMode mode);
|
|
void saveCurrentKeySet();
|
|
void reloadUI(const char *filename);
|
|
void initHighlightDecal();
|
|
void updateDecalBlendRegion(CDecal &decal, const NLMISC::CVector &pos);
|
|
void initPalette();
|
|
void initObjectProjectionMetatable();
|
|
void registerDisplayers();
|
|
void registerTools();
|
|
void registerLuaFunc();
|
|
// add a C++ method in the environement
|
|
void registerEnvMethod(const char *name, TLuaWrappedFunction func);
|
|
void registerEnvFunction(const char *name, TLuaWrappedFunction func);
|
|
// Initialisation of contextual cursor.
|
|
void initDecals();
|
|
void showDecal(const NLMISC::CVector2f &pos, float scale, CDecal &decal, const CDecalAnim &decalAnim);
|
|
void updatePrimitiveContextualVisibility();
|
|
void initClassInheritanceTable();
|
|
// contextual mouse handling
|
|
static void checkCursor();
|
|
// Forward click from contextual cursor to current tool
|
|
static void mouseClick(bool rightButton, bool dblClick);
|
|
// callback to reload the editor when one of the config files changed
|
|
static void reloadEditorCallback(const std::string &filename);
|
|
// update the display of decals created when the player select an instance in the scene
|
|
void updateSelectingDecals();
|
|
// display of highlight or select box (for selection of huge objects)
|
|
// the CDecalAnim is used to mimic the color cycle seen when standard selection circle is displayed
|
|
// (no CPrimRenderAnim for now)
|
|
void showPrimRender(CPrimRender &dest, const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat, const CDecalAnim &refDecalAnim);
|
|
|
|
CLuaObject _OldLuaRequestInsertNode;
|
|
CLuaObject _OldLuaRequestInsertGhostNode;
|
|
CLuaObject _OldLuaRequestSetNode;
|
|
CLuaObject _OldLuaRequestEraseNode;
|
|
CLuaObject _OldLuaRequestMoveNode;
|
|
|
|
bool _ClearingContent;
|
|
|
|
std::string _ConnexionMsg;
|
|
static std::string _ScenarioToLoadWhenEntreringIntoAnimation;
|
|
static bool _IsStartingScenario; // the scenario is an animation scenario launch from the ring access point
|
|
|
|
//bool _ModeEnabled[ModeCount];
|
|
|
|
public:
|
|
// private method
|
|
CInstance *getInstanceFromObject(const CObject *obj) const; // nb : only table have associated instance
|
|
void connectAsCreator();
|
|
// notify obervers of an instance that it has been created
|
|
void notifyInstanceObserversOfCreation(CInstance &inst);
|
|
|
|
// TMP for debug : dump missing collisions in the log
|
|
void checkMissingCollisions();
|
|
|
|
// trigger an instance observers for the given instance id. A copy of the observer list is made, thus
|
|
// allowing for safe removal of observer when they are triggered
|
|
struct IObserverAction
|
|
{
|
|
virtual ~IObserverAction() { }
|
|
virtual void doAction(IInstanceObserver &obs) = 0;
|
|
};
|
|
void triggerInstanceObserver(const TInstanceId &id, IObserverAction &action);
|
|
|
|
static void setStartingAnimationFilename(const std::string& filename);
|
|
|
|
// for CInstance usage : allows to keep a list of instances sorted by their display name
|
|
void registerInstanceDispName(const ucstring &displayName, CInstance *inst);
|
|
void unregisterInstanceDispName(CInstance *inst);
|
|
|
|
};
|
|
|
|
// shortcut function to get the editor
|
|
inline CEditor &getEditor() { return CEditor::getInstance(); }
|
|
|
|
// test whether editor is currently enabled and is in EDITION mode. (In this mode, selection is managed by the editor rather than by the entity manager)
|
|
bool isEditionCurrent();
|
|
|
|
/** helper to create a class from the registry
|
|
* \TODO a true factory class!
|
|
* \TODO or put this in a better place (NLMISC ? )
|
|
*/
|
|
template <class T>
|
|
T *createObjectFromClassName(const std::string &className)
|
|
{
|
|
if (className.empty()) return NULL;
|
|
try
|
|
{
|
|
NLMISC::IClassable *obj = NLMISC::CClassRegistry::create(className);
|
|
if (!obj)
|
|
{
|
|
nlwarning("Couldn't create object of class %s", className.c_str());
|
|
return NULL;
|
|
}
|
|
T *inst = dynamic_cast<T *>(obj);
|
|
if (!inst)
|
|
{
|
|
nlwarning("<R2::createObjectFromClassName> class %s found in the registry, but does not match the expected class.",
|
|
obj->getClassName().c_str());
|
|
delete obj;
|
|
return NULL;
|
|
}
|
|
return inst;
|
|
}
|
|
catch(const NLMISC::ERegistry &)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
extern bool ResetWanted;
|
|
extern bool ResetScenarioWanted;
|
|
extern bool ReloadScenarioWanted;
|
|
extern bool ConnectionWanted;
|
|
//
|
|
|
|
|
|
|
|
// helper : get a NLMISC::CVector from a DMC::CObject
|
|
NLMISC::CVector getVector(const CObject *obj);
|
|
NLMISC::CVectorD getVectorD(const CObject *obj);
|
|
//helper : build a CObject from a NLMISC::CVectorD
|
|
CObject *buildVector(const NLMISC::CVectorD &v, const std::string &instanceId = "");
|
|
|
|
|
|
|
|
// DMC helpers:
|
|
std::string getString(const CObject *obj, const std::string &attrName);
|
|
double getNumber(const CObject *obj, const std::string &attrName);
|
|
const CObject *getObject(const CObject *obj, const std::string &attrName);
|
|
|
|
/** Helper : read an enum from a lua string, printing necessary error if no match is found.
|
|
* nil found -> no op
|
|
*/
|
|
template <class T>
|
|
void enumFromLua(const CLuaObject &value,
|
|
std::pair<std::string, T> *enumTable,
|
|
uint numEnum,
|
|
T &dest,
|
|
const std::string &errorMsgPrefix
|
|
)
|
|
{
|
|
if (value.isNil())
|
|
{
|
|
return;
|
|
}
|
|
if (!value.isString())
|
|
{
|
|
nlwarning("%s : String expected when reading '%s'", errorMsgPrefix.c_str(), value.getId().c_str());
|
|
return;
|
|
}
|
|
for(uint k = 0; k < numEnum; ++k)
|
|
{
|
|
if (value.toString() == enumTable[k].first)
|
|
{
|
|
dest = enumTable[k].second;
|
|
return;
|
|
}
|
|
}
|
|
nlwarning("%s : Unknown enum %s read from object %s", errorMsgPrefix.c_str(),
|
|
value.toString().c_str(),
|
|
value.getId().c_str());
|
|
}
|
|
|
|
|
|
} // R2
|
|
|
|
bool IsInRingMode();
|
|
|
|
|
|
#endif
|