// 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_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 { 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 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 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 _PlotItemInfos; CIslandCollision _IslandCollision; // bool _SerializeUIConfig; TMode _Mode; TAccessMode _AccessMode; typedef std::map 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 _ClassNameToIndex; // Map each class name to an unique index (filled at init) NLMISC::CArray2D _KindOfTable; // Table to test if one class derives from another (filled at init) std::vector _BaseClassIndices; // for each class, give index of thebase class (vector ordered by classes indices) typedef CHashMultiMap 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 TInstanceToIter; // TInstanceByName _ByName; TInstanceToIter _InstanceToIter; }; // typedef TInstanceByDispName; // for usage by CInstance // list of instances for each classes (ordered by class index) std::vector _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 TCookieList; // for a single instance, map each key to its value typedef std::map 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 > _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 _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 TNameSet; typedef std::map TGeneratedNameMap; TGeneratedNameMap _LocalGeneratedNames; // instance observers typedef std::multimap TInstanceObserverMap; TInstanceObserverMap _InstanceObservers; typedef std::map 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 wich 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 _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 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 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 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(obj); if (!inst) { nlwarning(" 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 void enumFromLua(const CLuaObject &value, std::pair *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