// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" #include #include "nel/misc/path.h" #include "nel/misc/i18n.h" #include "nel/misc/sheet_id.h" #include "nel/misc/polygon.h" #include "nel/misc/time_nl.h" // #include "nel/3d/u_driver.h" #include "nel/3d/u_text_context.h" // #include "nel/ligo/ligo_config.h" #include "nel/net/unified_network.h" #include "nel/net/module_manager.h" #include "nel/pacs/u_global_retriever.h" // #include "r2_lua.h" #include "game_share/r2_share_itf.h" #include "game_share/r2_messages.h" #include "game_share/scenario_entry_points.h" // #include "dmc/dmc.h" // client interface to the dynamic scenario service #include "dmc/client_edition_module.h" #include "dmc/property_accessor.h" #include "dmc/com_lua_module.h" #include "game_share/dms.h" // #include "../client_sheets/item_sheet.h" #include "editor.h" // #include "../interface_v3/lua_helper.h" #include "../interface_v3/group_tree.h" #include "../interface_v3/interface_manager.h" #include "../contextual_cursor.h" #include "../cursor_functions.h" #include "../entities.h" #include "../events_listener.h" #include "../interface_v3/group_list.h" #include "../interface_v3/event_descriptor.h" #include "../interface_v3/group_tree.h" #include "../client_cfg.h" #include "../interface_v3/lua_ihm.h" #include "../interface_v3/lua_object.h" #include "../global.h" #include "../connection.h" #include "../main_loop.h" #include "../interface_v3/people_interraction.h" #include "../time_client.h" #include "../pacs_client.h" #include "../interface_v3/lua_ihm.h" #include "../actions.h" #include "../actions_client.h" #include "object_factory_client.h" #include "../weather.h" #include "../light_cycle_manager.h" #include "../dummy_progress.h" #include "../continent_manager.h" #include "../world_database_manager.h" #include "../init_main_loop.h" #include "../net_manager.h" #include "../interface_v3/input_handler_manager.h" #include "../connection.h" #include "../init_main_loop.h" #include "../interface_v3/group_editbox.h" #include "../landscape_poly_drawer.h" #include "../input.h" #include "../motion/user_controls.h" #include "../game_context_menu.h" #include "../interface_v3/macrocmd_manager.h" // #include "../player_r2_cl.h" #include "palette_node.h" #include "tool_create_entity.h" #include "tool_select_move.h" #include "tool_new_vertex.h" #include "displayer_visual_entity.h" #include "displayer_visual_group.h" #include "displayer_visual_shape.h" #include "displayer_visual_activity_sequence.h" #include "displayer_lua.h" #include "verbose_clock.h" #include "r2_config.h" #include "entity_sorter.h" // #include "tool_draw_prim.h" #include "tool_select_move.h" #include "tool_select_rotate.h" #include "tool_choose_pos_lua.h" // #include "../sheet_manager.h" #include "../session_browser_impl.h" #include "../far_tp.h" using namespace NLMISC; using namespace NLNET; using namespace NL3D; extern CEventsListener EventsListener; extern CLog g_log; extern CGameContextMenu GameContextMenu; extern void badXMLParseMessageBox(); R2::TUserRole UserRoleInSession; #define OPERATOR_EQUAL(X,Y) X==Y //#define OPERATOR_EQUAL(X, Y) ::operator==(X,Y) namespace R2 { class CEditorCheck { public: CEditorCheck() { check(); } ~CEditorCheck() { check(); } void check() { if (!EditorCreated) return; // avoid infinite loop if (getEditor().getMode() != CEditor::NotInitialized) { nlassert(getEditor().getEnv().isValid()); } } static bool EditorCreated; }; bool CEditorCheck::EditorCreated = false; #ifdef NL_DEBUG #define CHECK_EDITOR CEditorCheck __ec; #else #define CHECK_EDITOR (void) 0; #endif const char *DEFAULT_CURSOR = "curs_default.tga"; bool ResetWanted = false; bool ReloadUIFlag = true; // by default, CEditor loads its own UI bool ResetScenarioWanted = false; bool ReloadScenarioWanted = false; bool ConnectionWanted = false; std::string CEditor::_ScenarioToLoadWhenEntreringIntoAnimation=""; bool CEditor::_IsStartingScenario=false; // ********************************************************************************************************* /** this class forward modification events from the network so that the editor can * update its state to match the current state of the map */ class CDynamicMapClientEventForwarder : public CDynamicMapClient { public: CDynamicMapClientEventForwarder(const std::string &eid, NLNET::IModuleSocket * clientGateway, lua_State *luaState); virtual void nodeErased(const std::string& instanceId, const std::string& attrName, sint32 position); virtual void nodeSet(const std::string& instanceId, const std::string& attrName, CObject* value); virtual void nodeInserted(const std::string& instanceId, const std::string& attrName, sint32 position, const std::string& key, CObject* value); virtual void nodeMoved(const std::string& instanceId, const std::string& attrName, sint32 position, const std::string& destInstanceId, const std::string& destAttrName, sint32 destPosition); virtual void scenarioUpdated(CObject* highLevel, bool willTP, uint32 initialActIndex); virtual void onAnimationModeConnected(const CClientMessageAdventureUserConnection& connected); virtual void onEditionModeConnected( uint32 userSlotId, uint32 adventureId, CObject* highLevel, const std::string& versionName, bool willTP, uint32 initialActIndex); virtual void onEditionModeDisconnected(); virtual void onResetEditionMode(); virtual void onTestModeConnected(); virtual void onTestModeDisconnected(TSessionId sessionId, uint32 lasAct, TScenarioSessionType sessionType); }; CDynamicMapClientEventForwarder::CDynamicMapClientEventForwarder(const std::string &eid, NLNET::IModuleSocket * clientGateway, lua_State *luaState) : CDynamicMapClient(eid, clientGateway, luaState) { } void CDynamicMapClientEventForwarder::nodeErased(const std::string& instanceId, const std::string& attrName, sint32 position) { //H_AUTO(R2_CDynamicMapClientEventForwarder_nodeErased) if (!getEditor().getMode() == CEditor::EditionMode) return; getEditor().nodeErased(instanceId, attrName, position); } void CDynamicMapClientEventForwarder::nodeSet(const std::string& instanceId, const std::string& attrName, CObject* value) { //H_AUTO(R2_CDynamicMapClientEventForwarder_nodeSet) if (!getEditor().getMode() == CEditor::EditionMode) return; getEditor().nodeSet(instanceId, attrName, value); } void CDynamicMapClientEventForwarder::nodeInserted(const std::string& instanceId, const std::string& attrName, sint32 position, const std::string& key, CObject* value) { //H_AUTO(R2_CDynamicMapClientEventForwarder_nodeInserted) if (!getEditor().getMode() == CEditor::EditionMode) return; getEditor().nodeInserted(instanceId, attrName, position, key, value); } void CDynamicMapClientEventForwarder::nodeMoved( const std::string& instanceId, const std::string& attrName, sint32 position, const std::string& destInstanceId, const std::string& destAttrName, sint32 destPosition) { //H_AUTO(R2_CDynamicMapClientEventForwarder_nodeMoved) if (!getEditor().getMode() == CEditor::EditionMode) return; getEditor().nodeMoved(instanceId, attrName, position, destInstanceId, destAttrName, destPosition); } void CDynamicMapClientEventForwarder::scenarioUpdated(CObject* highLevel, bool willTP, uint32 initialActIndex) { //H_AUTO(R2_CDynamicMapClientEventForwarder_scenarioUpdated) if (!getEditor().getMode() == CEditor::EditionMode) return; getEditor().scenarioUpdated(highLevel, willTP, initialActIndex); } void CDynamicMapClientEventForwarder::onEditionModeConnected( uint32 userSlotId, uint32 adventureId, CObject* highLevel, const std::string& versionName, bool willTP, uint32 initialActIndex ) { //H_AUTO(R2_CDynamicMapClientEventForwarder_onEditionModeConnected) getEditor().onEditionModeConnected(userSlotId, adventureId, highLevel, versionName, willTP, initialActIndex); } void CDynamicMapClientEventForwarder::onEditionModeDisconnected() { //H_AUTO(R2_CDynamicMapClientEventForwarder_onEditionModeDisconnected) getEditor().onEditionModeDisconnected(); } void CDynamicMapClientEventForwarder::onResetEditionMode() { //H_AUTO(R2_CDynamicMapClientEventForwarder_onResetEditionMode) getEditor().onResetEditionMode(); } void CDynamicMapClientEventForwarder::onTestModeConnected() { //H_AUTO(R2_CDynamicMapClientEventForwarder_onTestModeConnected) getEditor().onTestModeConnected(); } void CDynamicMapClientEventForwarder::onTestModeDisconnected(TSessionId sessionId, uint32 lastAct, TScenarioSessionType sessionType) { //H_AUTO(R2_CDynamicMapClientEventForwarder_onTestModeDisconnected) getEditor().onTestModeDisconnected(sessionId, lastAct, sessionType); } void CDynamicMapClientEventForwarder::onAnimationModeConnected(const CClientMessageAdventureUserConnection& connected) { //H_AUTO(R2_CDynamicMapClientEventForwarder_onAnimationModeConnected) getEditor().onAnimationModeConnected(connected); } // ********************************************************************************************************* CEditor::CEditor() { //H_AUTO(R2_CEditor_CEditor) _SelectedInstance = NULL; _FocusedInstance = NULL; _Scenario = NULL; _EnteredInSetSelectedInstance = false; _DecalRefTime = 0; _Initialized = false; //vianney?? _LastUIModificationDate = 0; _InstanceObserverHandleCounter = 0; // module are initialized one time and never reconnected _DMC = NULL; _DMS = NULL; _Mode = NotInitialized; _AccessMode = AccessModeUnknown; _SerializeUIConfig = true; _LastAutoSaveTime = NLMISC::CTime::getLocalTime();// wait ClientCfg.R2EDAutoSaveWait setForceDesktopReset(false); CEditorCheck::EditorCreated = true; _ClearingContent = false; _Season = UnknownSeason; _IsWaitingTPForSeasonChange = true; _UpdatingScenario = false; _WillTP = false; _FixedLighting = false; // _ScenarioReceivedFlag = false; _TPReceivedFlag = false; _WaitScenarioScreenWanted = false; _WaitScenarioScreenActive = false; _NewScenario = NULL; _NewScenarioInitialAct = 0; _EditionModeDisconnectedFlag = true; _PostponeScenarioUpdated = false; _EntitySorter = NULL; _MaxVisibleEntityExceededFlag = false; } // ********************************************************************************************************* void CEditor::setForceDesktopReset(bool force) { //H_AUTO(R2_CEditor_setForceDesktopReset) CHECK_EDITOR std::fill(_ForceDesktopReset, _ForceDesktopReset + sizeofarray(_ForceDesktopReset), force); } // ********************************************************************************************************* void CEditor::autoConfigInit(bool serverIsRingSession) { //H_AUTO(R2_CEditor_autoConfigInit) // R2ED enabled ? if (ClientCfg.R2EDEnabled) { // Editor and Animator (DM) modes initModules(true); TMode initialMode; TAccessMode initialAccessMode; switch (UserRoleInSession.getValue()) { case R2::TUserRole::ur_editor: initialMode = R2::CEditor::EditionMode; initialAccessMode = R2::CEditor::AccessEditor; break; case R2::TUserRole::ur_animator: initialMode = R2::CEditor::AnimationModeDm; initialAccessMode = R2::CEditor::AccessDM; break; case R2::TUserRole::ur_outland_owner: initialMode = R2::CEditor::AnimationModeDm; initialAccessMode = R2::CEditor::AccessOutlandOwner; break; case R2::TUserRole::ur_player: initialMode = R2::CEditor::AnimationModePlay; // NOTE: I'm not sure if 'editor' is the right mode here - but I can't find anything better for now // If I try 'AccessModeUnknown' the code asserts later on. initialAccessMode = R2::CEditor::AccessEditor; break; default: nlwarning( "Unexpected user role %u while editor enabled", (uint)(UserRoleInSession.getValue()) ); initialMode = R2::CEditor::EditionMode; initialAccessMode = R2::CEditor::AccessEditor; } init(initialMode, initialAccessMode); } else { // Normal player and Ring player modes ActionsContext.setContext("game"); if (serverIsRingSession) initModules(false); if (UserRoleInSession.getValue() != R2::TUserRole::ur_player) nlwarning( "Unexpected user role %u while editor disabled", (uint)(UserRoleInSession.getValue()) ); } } // ********************************************************************************************************* void CEditor::autoConfigRelease(bool serverIsRingSession) { //H_AUTO(R2_CEditor_autoConfigRelease) if (ClientCfg.R2EDEnabled) { R2::getEditor().release(); R2::getEditor().releaseModules(); } else if (serverIsRingSession) R2::getEditor().releaseModules(); } // ********************************************************************************************************* void CEditor::initModules(bool connectDMC) { //H_AUTO(R2_CEditor_initModules) CHECK_EDITOR IModuleManager &mm = IModuleManager::getInstance(); IModule *gateway = mm.createModule("StandardGateway", "clientGw", ""); nlassert(gateway != NULL); NLNET::IModuleSocket *socketClientGw = mm.getModuleSocket("clientGw"); nlassert(socketClientGw != NULL); // connect the client gateway to the server CCommandRegistry::getInstance().execute("clientGw.transportAdd FEClient fec", *InfoLog); if (!ClientCfg.Local) { CCommandRegistry::getInstance().execute("clientGw.transportCmd fec(open)", *InfoLog); } if (ClientCfg.Local) { _DMS = new CDynamicMapService(ClientCfg.ConfigFile, socketClientGw); _DMS->init(); } else { _DMS = 0; } if (connectDMC) _DMC = new CDynamicMapClientEventForwarder("Client0",socketClientGw, getLua().getStatePointer()); else _DMC = new CDynamicMapClient("Client0",socketClientGw, getLua().getStatePointer()); _DMC->setInstantFeedBackFlag(true); } // ********************************************************************************************************* void CEditor::releaseModules() { //H_AUTO(R2_CEditor_releaseModules) CHECK_EDITOR delete _DMC; // must have a virtual destructor if (ClientCfg.Local) delete _DMS; _DMC = NULL; _DMS = NULL; IModuleManager &mm = IModuleManager::getInstance(); NLNET::IModule* clientGw = mm.getLocalModule("clientGw"); if (clientGw) { mm.deleteModule(clientGw); } _Initialized = false; } // ********************************************************************************************************* void CEditor::inGameSelection(const CLFECOMMON::TCLEntityId &/* slot */) { //H_AUTO(R2_CEditor_inGameSelection) switch(_Mode) { case DMMode: case AnimationModeDm: // reset current selection options getEditor().callEnvMethod("updateAnimBarActions", 0); getLua().push(""); getEditor().callEnvMethod("dssTarget", 1); break; default: // no-op break; } } // ********************************************************************************************************* CEditor::~CEditor() { release(); } // *************************************************************** void CEditor::requestSetLocalNode(const std::string& instanceId, const std::string& attrName, const CObject *value) { //H_AUTO(R2_CEditor_requestSetLocalNode) CHECK_EDITOR CObject* obj = NULL; { CObject *src = _DMC->find(instanceId); if (!src) { nlwarning("Can't find object with id %s", instanceId.c_str()); return; } if (!attrName.empty()) { CObject *subObj = src->getAttr(attrName); if (!subObj) { CObject * valueBase = (CObject *) getObject(src, attrName); // from Base if (!valueBase) { nlwarning("Can't find attribute %s inside object (or is base) with InstanceId = %s", attrName.c_str(), instanceId.c_str()); return; } CObject *valueClone = valueBase->clone(); valueClone->setParent(0); src->setObject(attrName, valueClone); subObj = src->getAttr(attrName); } if (!subObj) { nlwarning("Can't find attribute %s inside object with InstanceId = %s", attrName.c_str(), instanceId.c_str()); return; } obj = subObj; } } getDMC().getPropertyAccessor().shadowValue(obj, value->clone()); // trigger handlers onAttrModified(obj); } // ***************************************** void CEditor::requestCommitLocalNode(const std::string& instanceId, const std::string& attrName) { //H_AUTO(R2_CEditor_requestCommitLocalNode) CHECK_EDITOR CObject *obj = _DMC->find(instanceId, attrName); if (!obj) { nlwarning("(while calling requestSetLocalNode"); return; } //nlwarning("**** current unshadowed value"); //obj->dump(); /* // when requestSetNode is called, the old value can't be retrieved because it will // be erased by 'commitValue', so backup old value CObject *oldValueBackup = obj->clone(); // send net request getDMC().getPropertyAccessor().commitValue(obj); getDMC().requestSetNode(instanceId, attrName, obj); */ CObject *localValue = getDMC().getPropertyAccessor().getShadowingValue(obj); if (!localValue) { nlwarning("value is not shadowed : "); obj->dump(); return; } CObject *localValueBackup = localValue->clone(); getDMC().getPropertyAccessor().rollbackValue(obj); getDMC().requestSetNode(instanceId, attrName, localValueBackup); } // *************************************************************** void CEditor::requestRollbackLocalNode(const std::string& instanceId, const std::string& attrName) { //H_AUTO(R2_CEditor_requestRollbackLocalNode) CHECK_EDITOR CObject *obj = _DMC->find(instanceId, attrName); if (!obj) { nlwarning("(while calling requestSetLocalNode"); return; } getDMC().getPropertyAccessor().rollbackValue(obj); // force the displayer to update their state from the shadowed value in place of the local value onAttrModified(obj); } // *************************************************************** CInterfaceManager &CEditor::getUI() { //H_AUTO(R2_CEditor_getUI) CHECK_EDITOR CInterfaceManager *im = CInterfaceManager::getInstance(); nlassert(im); return *im; } // ********************************************************************************************************* CLuaState &CEditor::getLua() { //H_AUTO(R2_CEditor_getLua) CHECK_EDITOR CLuaState *ls = getUI().getLuaState(); nlassert(ls); return *ls; } // ********************************************************************************************************* CLuaObject &CEditor::getEnv() { //H_AUTO(R2_CEditor_getEnv) nlassert(_Env.isValid()); return _Env; } // ********************************************************************************************************* CLuaObject CEditor::getConfig() { //H_AUTO(R2_CEditor_getConfig) CHECK_EDITOR return getEnv()["Config"]; } // ********************************************************************************************************* void CEditor::clearDebugWindow() { //H_AUTO(R2_CEditor_clearDebugWindow) CHECK_EDITOR getUI().flushDebugWindow(); CGroupList *gl = dynamic_cast(getUI().getElementFromId("ui:interface:debug_info:content:cb:text_list")); if (gl) { gl->deleteAllChildren(); } } // ********************************************************************************************************* // callback to reload the editor when one of the config files changed void CEditor::reloadEditorCallback(const std::string &filename) { //H_AUTO(R2_CEditor_reloadEditorCallback) CHECK_EDITOR ResetWanted = true; if (nlstricmp(CFile::getExtension(filename), "xml") == 0 || nlstricmp(CFile::getExtension(filename), "uxt") == 0) { ReloadUIFlag = true; } } // ********************************************************************************************************* void CEditor::setFixedLighting(bool enabled) { //H_AUTO(R2_CEditor_setFixedLighting) if (_FixedLighting == enabled) return; _FixedLighting = enabled; LightCycleManager.touch(); } // ********************************************************************************************************* int CEditor::luaGetSelectedInstanceId(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetSelectedInstanceId) CHECK_EDITOR if (!getEditor().getSelectedInstance()) { ls.pushNil(); } else { ls.push(toString(getEditor().getSelectedInstance()->getId())); } return 1; } void CEditor::setStartingAnimationFilename(const std::string& filename) { //H_AUTO(R2_CEditor_setStartingAnimationFilename) _ScenarioToLoadWhenEntreringIntoAnimation = filename; } // ********************************************************************************************************* int CEditor::luaGetStartingAnimationFilename(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetStartingAnimationFilename) ls.push( _ScenarioToLoadWhenEntreringIntoAnimation); return 1; } // ********************************************************************************************************* int CEditor::luaSetSelectedInstanceId(CLuaState &ls) { //H_AUTO(R2_CEditor_luaSetSelectedInstanceId) CHECK_EDITOR CLuaIHM::checkArgCount(ls, "setSelectedInstance (method)", 2); CLuaIHM::checkArgType(ls, "setSelectedInstance", 2, LUA_TSTRING); if (strcmp(ls.toString(2), "") == 0) { getEditor().setSelectedInstance(NULL); return 0; } CInstance *inst = getEditor().getInstanceFromId(ls.toString(2)); if (!inst) { nlwarning("setSelectedInstance : Instance with Id %s not found", ls.toString(2)); return 0; } if (!inst->getSelectableFromRoot()) { nlwarning("Instance with id %s or one of its ancestor is not selectable", ls.toString(2)); return 0; } getEditor().setSelectedInstance(inst); return 0; } // ********************************************************************************************************* int CEditor::luaSetCurrentTool(CLuaState &ls) { //H_AUTO(R2_CEditor_luaSetCurrentTool) CHECK_EDITOR CLuaIHM::check(ls, ls.getTop() == 2 || ls.getTop() == 3, "luaSetCurrentTool (method)"); CLuaIHM::checkArgType(ls, "setCurrentTool", 2, LUA_TSTRING); if (ls.strlen(2) == 0) { getEditor().setCurrentTool(NULL); } else { CTool *tool = createObjectFromClassName(ls.toString(2)); if (tool) { if (ls.getTop() == 3) { CLuaObject initParams(ls); tool->init(initParams); } getEditor().setCurrentTool(tool); } } return 0; } // ********************************************************************************************************* int CEditor::luaGetCurrentTool(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetCurrentTool) CHECK_EDITOR CLuaIHM::checkArgCount(ls, "getCurrentTool", 1); // 1 because of the method call CLuaIHM::pushReflectableOnStack(ls, getEditor().getCurrentTool()); return 1; } // ********************************************************************************************************* int CEditor::luaGetSelectedInstance(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetSelectedInstance) CHECK_EDITOR if (!getEditor().getSelectedInstance()) { ls.pushNil(); } else { getEditor().projectInLua(getEditor().getSelectedInstance()->getObjectTable()); } return 1; } // ********************************************************************************************************* int CEditor::luaGetInstanceFromId(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetInstanceFromId) CHECK_EDITOR const char *funcName = "getInstanceFromId"; CLuaIHM::checkArgCount(ls, funcName, 2); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); CInstance *inst = getEditor().getInstanceFromId(ls.toString(2)); if (inst == NULL) { ls.pushNil(); return 1; } else { getEditor().projectInLua(inst->getObjectTable()); return 1; } return 0; } // ******************************************************************************************************** int CEditor::luaGetVisualPropertiesFromInstanceId(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetVisualPropertiesFromInstanceId) const char *funcName = "getVisualPropertiesFromInstanceId"; CLuaIHM::checkArgCount(ls, funcName, 1); CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING); // instance sheetClient CInstance *inst = getEditor().getInstanceFromId(ls.toString(1)); if (!inst) { return 0; } CObject *object = inst->getObjectTable(); if (!object) { return 0; } SPropVisualA vA; SPropVisualB vB; SPropVisualC vC; bool ok = getEditor().getVisualPropertiesFromObject(object, vA, vB, vC); if (!ok) { return 0; //no param } uint64 uVPA = vA.get(); uint64 uVPB = vB.get(); uint64 uVPC = vC.get(); std::string strVPABC = NLMISC::toString( "VPA:%016.16"NL_I64"x\nVPB:%016.16"NL_I64"x\nVPC:%016.16"NL_I64"x", uVPA, uVPB, uVPC ); ls.push(strVPABC); return 1; //3 string } // ********************************************************************************************************* int CEditor::luaDisplayContextMenu(CLuaState &ls) { //H_AUTO(R2_CEditor_luaDisplayContextMenu) CHECK_EDITOR CLuaIHM::checkArgCount(ls, "displayContextMenu", 1); // this is a method getEditor().displayContextMenu(); return 0; } // ********************************************************************************************************* int CEditor::luaConnectAsCreator(CLuaState &ls) { //H_AUTO(R2_CEditor_luaConnectAsCreator) CHECK_EDITOR CLuaIHM::checkArgCount(ls, "connectAsCreator", 1); // this is a method //getEditor().connectAsCreator(); return 0; } // ********************************************************************************************************* int CEditor::luaDofile(CLuaState &ls) { //H_AUTO(R2_CEditor_luaDofile) CHECK_EDITOR CLuaIHM::checkArgCount(ls, "doFile", 1); CLuaIHM::checkArgType(ls, "doFile", 1, LUA_TSTRING); getEditor().doLuaScript(ls.toString(-1), ls.toString(-1)); return 0; } // ********************************************************************************************************* int CEditor::luaTryFile(CLuaState &/* ls */) { //H_AUTO(R2_CEditor_luaTryFile) /* CHECK_EDITOR CLuaIHM::checkArgCount(ls, "doFile", 1); CLuaIHM::checkArgType(ls, "doFile", 1, LUA_TSTRING); getEditor().doLuaScript(ls.toString(-1), ls.toString(-1));*/ return 0; } // ********************************************************************************************************* int CEditor::luaSetEntityCustomSelectBox(CLuaState &ls) { //H_AUTO(R2_CEditor_luaSetEntityCustomSelectBox) CHECK_EDITOR CLuaIHM::checkArgCount(ls, "setEntityCustomSelectBox", 3); // 3 because of method call CLuaIHM::checkArgType(ls, "setEntityCustomSelectBox", 2, LUA_TSTRING); CLuaIHM::checkArgType(ls, "setEntityCustomSelectBox", 3, LUA_TTABLE); std::string sheetName = ls.toString(2); CEntityCustomSelectBox selectBox; CLuaObject obj(ls); selectBox.fromTable(obj); TEntityCustomSelectBoxMap &boxMap = getEditor().getEntityCustomSelectBoxMap(); boxMap[sheetName] = selectBox; /* static volatile bool dumpMap = false; if (dumpMap) { for (TEntityCustomSelectBoxMap::const_iterator watchIt = boxMap.begin(); watchIt != boxMap.end(); ++ watchIt) { nlwarning(watchIt->first.c_str()); } } */ return 0; } // ********************************************************************************************************* int CEditor::luaGetEntityCustomSelectBox(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetEntityCustomSelectBox) CHECK_EDITOR CLuaIHM::checkArgCount(ls, "getEntityCustomSelectBox", 2); // 3 because of method call CLuaIHM::checkArgType(ls, "getEntityCustomSelectBox", 2, LUA_TSTRING); std::string sheetName = ls.toString(2); TEntityCustomSelectBoxMap boxMap = getEditor().getEntityCustomSelectBoxMap(); if (boxMap.count(sheetName)) { ls.newTable(); CLuaObject result(ls); boxMap[sheetName].toTable(result); result.push(); } else { ls.pushNil(); } return 1; } // ********************************************************************************************************* int CEditor::luaChoosePos(CLuaState &ls) { //H_AUTO(R2_CEditor_luaChoosePos) CHECK_EDITOR const char *funcName = "choosePos"; CLuaIHM::checkArgMin(ls, funcName, 5); CLuaIHM::checkArgMax(ls, funcName, 10); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); CLuaIHM::checkArgType(ls, funcName, 5, LUA_TSTRING); std::string toolName = ls.toString(5); std::string cursValid = "curs_create.tga"; std::string cursInvalid = "curs_stop.tga"; if (ls.getTop() >= 6) { CLuaIHM::checkArgType(ls, funcName, 6, LUA_TSTRING); cursValid = ls.toString(6); } if (ls.getTop() >= 7) { CLuaIHM::checkArgType(ls, funcName, 7, LUA_TSTRING); cursInvalid = ls.toString(7); } // method param 1 -> sheet id // method param 2 -> ok function // method param 3 -> cancel function ls.pushValue(3); CLuaObject validFunc(ls); // pop valid function ls.pushValue(4); CLuaObject cancelFunc(ls); // pop cancel function // sint ghostSlot; if (ls.strlen(2) != 0) { CSheetId sheetId(ls.toString(2)); if (sheetId == CSheetId::Unknown) { nlwarning("Can't get sheet %s", ls.toString(2)); return 0; } ghostSlot = 1; // TMP TMP if (!createEntity(ghostSlot, sheetId, CVector::Null, 0.f)) { nlwarning("Cannot create entity for sheet %s", ls.toString(2)); return 0; } } else { ghostSlot = -1; // no representation in scene } // additionnal polys displayed by the choose pos std::vector polys; CPrimLook validLook; CPrimLook invalidLook; if (ls.getTop() >= 8) { CLuaStackRestorer lsr(&ls, ls.getTop()); CLuaIHM::checkArgType(ls, funcName, 8, LUA_TTABLE); ls.pushValue(8); CLuaObject poly; poly.pop(ls); ENUM_LUA_TABLE(poly, it) { NLMISC::CPolygon2D newPoly; it.nextValue().push(); CLuaIHM::getPoly2DOnStack(ls, -1, newPoly); ls.pop(); polys.push_back(newPoly); } } if (ls.getTop() >= 9) { ls.pushValue(9); CLuaObject look; look.pop(ls); validLook.init(look); } if (ls.getTop() >= 10) { ls.pushValue(10); CLuaObject look; look.pop(ls); invalidLook.init(look); } // getEditor().setCurrentTool(new CToolChoosePosLua(ghostSlot, validFunc, cancelFunc, toolName, cursValid, cursInvalid, polys, validLook, invalidLook)); return 0; } // ********************************************************************************************************* int CEditor::luaSnapPosToGround(CLuaState &ls) { //H_AUTO(R2_CEditor_luaSnapPosToGround) CHECK_EDITOR const char *funcName = "snapPosToGround"; CLuaIHM::checkArgCount(ls, funcName, 3); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TNUMBER); CLuaIHM::checkArgType(ls, funcName, 3, LUA_TNUMBER); // /*NLPACS::UGlobalPosition gpos = GR->retrievePosition(CVector((float) ls.toNumber(2), (float) ls.toNumber(3), 2000.f)); if (gpos.InstanceId != -1) { CVector snappedPos = GR->getGlobalPosition(gpos); ls.push((double) snappedPos.x); ls.push((double) snappedPos.y); ls.push((double) snappedPos.z); return 3; } else { // default z to 0 ls.push(0.f); return 3; }*/ // new algo : to avoid to snapping to a cliff, start from the approximate (valid) height found in the height map CVector inter; if (CTool::computeWorldMapIntersection((float) ls.toNumber(2), (float) ls.toNumber(3), inter) != CTool::NoIntersection) { ls.push((double) inter.x); ls.push((double) inter.y); ls.push((double) inter.z); return 3; } else { // default z to 0 ls.push(0.f); return 3; } } // ********************************************************************************************************* int CEditor::luaGetUserEntityPosition(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetUserEntityPosition) CHECK_EDITOR const char *funcName = "getUserEntityPosition"; CLuaIHM::checkArgCount(ls, funcName, 1); ls.push((double) UserEntity->pos().x); ls.push((double) UserEntity->pos().y); ls.push((double) UserEntity->pos().z); return 3; } // ********************************************************************************************************* int CEditor::luaGetUserEntityFront(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetUserEntityFront) CHECK_EDITOR const char *funcName = "getUserEntityPosition"; CLuaIHM::checkArgCount(ls, funcName, 1); ls.push((double) UserEntity->front().x); ls.push((double) UserEntity->front().y); return 2; } // ********************************************************************************************************* int CEditor::luaRequestSetLocalNode(CLuaState &ls) { //H_AUTO(R2_CEditor_luaRequestSetLocalNode) CHECK_EDITOR const char *funcName = "requestSetLocalNode"; CLuaIHM::checkArgCount(ls, funcName, 3); CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); CObject *object = CComLuaModule::getObjectFromLua(ls.getStatePointer()); if (!object) { CLuaIHM::fails(ls, "%s : can't read object from parameter 3", funcName); } getEditor().requestSetLocalNode(ls.toString(1), ls.toString(2), object); delete object; return 0; } // ********************************************************************************************************* int CEditor::luaRequestCommitLocalNode(CLuaState &ls) { //H_AUTO(R2_CEditor_luaRequestCommitLocalNode) CHECK_EDITOR const char *funcName = "requestCommitLocalNode"; CLuaIHM::checkArgCount(ls, funcName, 2); CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); getEditor().requestCommitLocalNode(ls.toString(1), ls.toString(2)); return 0; } // ********************************************************************************************************* int CEditor::luaRequestRollbackLocalNode(CLuaState &ls) { //H_AUTO(R2_CEditor_luaRequestRollbackLocalNode) CHECK_EDITOR const char *funcName = "requestRollbackLocalNode"; CLuaIHM::checkArgCount(ls, funcName, 2); CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); getEditor().requestRollbackLocalNode(ls.toString(1), ls.toString(2)); return 0; } // ********************************************************************************************************* int CEditor::luaSetCurrentActFromId(CLuaState &ls) { //H_AUTO(R2_CEditor_luaSetCurrentActFromId) CHECK_EDITOR const char *funcName = "setCurrentActFromId"; CLuaIHM::checkArgCount(ls, funcName, 2); // this is a method CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); CInstance *act = getEditor().getInstanceFromId(ls.toString(2)); if (getEditor()._ClearingContent) { return 0; } if (!act) { nlwarning("%s : act with id %s not found", funcName, ls.toString(1)); return 0; } if (!act->isKindOf("Act")) { nlwarning("%s : instance with id %s is not an act", funcName, ls.toString(1)); return 0; } getEditor().setCurrentAct(act); return 0; } // ********************************************************************************************************* int CEditor::luaGetCurrentAct(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetCurrentAct) CHECK_EDITOR const char *funcName = "getCurrentAct"; CLuaIHM::checkArgCount(ls, funcName, 1); // this is a method if (getEditor().getCurrentAct()) { getEditor().projectInLua(getEditor().getCurrentAct()->getObjectTable()); } else { ls.pushNil(); } return 1; } // ********************************************************************************************************* int CEditor::luaGenInstanceName(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGenInstanceName) CHECK_EDITOR const char *funcName = "genInstanceName"; CLuaIHM::checkArgCount(ls, funcName, 2); // this is a method CLuaIHM::checkArgTypeUCString(ls, funcName, 2); // name ucstring baseName; nlverify(CLuaIHM::getUCStringOnStack(ls, 2, baseName)); CLuaIHM::push(ls, getEditor().genInstanceName(baseName)); return 1; } // ********************************************************************************************************* int CEditor::luaIsPostFixedByNumber(CLuaState &ls) { //H_AUTO(R2_CEditor_luaIsPostFixedByNumber) CHECK_EDITOR const char *funcName = "isPostFixedByNumber"; CLuaIHM::checkArgCount(ls, funcName, 2); // this is a method CLuaIHM::checkArgTypeUCString(ls, funcName, 2); // name ucstring baseName; nlverify(CLuaIHM::getUCStringOnStack(ls, 2, baseName)); ls.push(getEditor().isPostFixedByNumber(baseName)); return 1; } // ********************************************************************************************************* int CEditor::luaIsClearingContent(CLuaState &ls) { //H_AUTO(R2_CEditor_luaIsPostFixedByNumber) CHECK_EDITOR const char *funcName = "isClearingContent"; CLuaIHM::checkArgCount(ls, funcName, 1); // this is a method ls.push(getEditor().isClearingContent()); return 1; } // ********************************************************************************************************* int CEditor::luaSetCookie(CLuaState &ls) { //H_AUTO(R2_CEditor_luaSetCookie) CHECK_EDITOR const char *funcName = "setCookie"; CLuaIHM::checkArgCount(ls, funcName, 4); // this is a method (self + 3 params) CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); // instance id CLuaIHM::checkArgType(ls, funcName, 3, LUA_TSTRING); // key // last parameters may have any type CLuaObject value(ls); // pop it getEditor().setCookie(ls.toString(2), ls.toString(3), value); return 0; } // ********************************************************************************************************* int CEditor::luaIsCreature(CLuaState &ls) { //H_AUTO(R2_CEditor_luaIsCreature) CHECK_EDITOR const char *funcName = "isCreature"; CLuaIHM::checkArgCount(ls, funcName, 1); CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING); // instance sheetClient CInstance *inst = getEditor().getInstanceFromId(ls.toString(1)); if (!inst) return 0; const CCharacterSheet *sheet = dynamic_cast(SheetMngr.get(NLMISC::CSheetId(inst->getSheet()))); if (!sheet) return 0; bool isNPC = sheet->Race < EGSPD::CPeople::Creature; // neither NPC, not Kami, nor Karavan bool creature = !(isNPC || (!isNPC && (sheet->Race==EGSPD::CPeople::Kami || sheet->Race==EGSPD::CPeople::Karavan))); ls.push(creature); return 1; } // ********************************************************************************************************* int CEditor::luaSetEditorSeason(CLuaState &ls) { //H_AUTO(R2_CEditor_luaSetEditorSeason) CHECK_EDITOR const char *funcName = "setEditorSeason"; if (getEditor().getMode() != CEditor::EditionMode && getEditor().getMode() != CEditor::AnimationModeLoading ) { CLuaIHM::fails(ls, "%s : cannot set weather value outside of edit mode", funcName); } CLuaIHM::checkArgCount(ls, funcName, 2); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); // this is a method (self + 1 param) if (strcmp(ls.toString(2), "Automatic") == 0) getEditor()._Season = Automatic; else if (strcmp(ls.toString(2), "Spring") == 0) getEditor()._Season = Spring; else if (strcmp(ls.toString(2), "Summer") == 0) getEditor()._Season = Summer; else if (strcmp(ls.toString(2), "Autumn") == 0) getEditor()._Season = Autumn; else if (strcmp(ls.toString(2), "Winter") == 0) getEditor()._Season = Winter; else CLuaIHM::fails(ls, "%s, invalid season name : %s", ls.toString(2)); // If season is changed while scenario is being received, we do not have received the tp command yet, // so signal the weather system that the season should not be changed for now if ((getEditor()._UpdatingScenario && getEditor()._WillTP) || getEditor().getMode() != CEditor::AnimationModeLoading ) { getEditor()._IsWaitingTPForSeasonChange = true; } else { getEditor()._IsWaitingTPForSeasonChange = false; } return 0; } // ********************************************************************************************************* int CEditor::luaSetFixedLighting(CLuaState &ls) { //H_AUTO(R2_CEditor_luaSetFixedLighting) const char *funcName = "setEditorSeason"; CLuaIHM::checkArgCount(ls, funcName, 2); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TBOOLEAN); // this is a method (self + 1 param) getEditor().setFixedLighting(ls.toBoolean(2)); return 0; } // ********************************************************************************************************* int CEditor::luaGetFixedLighting(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetFixedLighting) const char *funcName = "getEditorSeason"; CLuaIHM::checkArgCount(ls, funcName, 1); // this is a method ls.push(getEditor().getFixedLighting()); return 1; } // ********************************************************************************************************* int CEditor::luaSetPlotItemInfos(CLuaState &ls) { //H_AUTO(R2_CEditor_luaSetPlotItemInfos) const char *funcName = "setPlotItemInfos"; CLuaIHM::checkArgCount(ls, funcName, 5); // a method with 4 args CLuaIHM::checkArgType(ls, funcName, 2, LUA_TNUMBER); CLuaIHM::checkArgTypeUCString(ls, funcName, 3); CLuaIHM::checkArgTypeUCString(ls, funcName, 4); CLuaIHM::checkArgTypeUCString(ls, funcName, 5); CItemSheet *item = dynamic_cast(SheetMngr.get(CSheetId((uint32) ls.toNumber(2)))); if (!item || item->Family != ITEMFAMILY::SCROLL_R2) { CLuaIHM::fails(ls, "%s : bad sheet, r2 plot item required", funcName); } R2::TMissionItem mi; mi.SheetId = (uint32) ls.toNumber(2); CLuaIHM::getUCStringOnStack(ls, 3, mi.Name); CLuaIHM::getUCStringOnStack(ls, 4, mi.Description); CLuaIHM::getUCStringOnStack(ls, 5, mi.Comment); getEditor().setPlotItemInfos(mi); return 0; } // ********************************************************************************************************* int CEditor::luaIsCurrentSelectionPlayer(CLuaState &ls) { //H_AUTO(R2_CEditor_luaIsCurrentSelectionPlayer) const char *funcName = "isCurrentSelectionPlayer"; CLuaIHM::checkArgCount(ls, funcName, 1); // method with 0 args CPlayerCL *player = dynamic_cast(EntitiesMngr.entity(UserEntity->getTargetSlotNoLag())); ls.push(player != NULL); return 1; } // ********************************************************************************************************* int CEditor::luaFindEmptyPlace(CLuaState &ls) { //H_AUTO(R2_CEditor_luaFindEmptyPlace) const char *funcName = "findEmptyPlace"; CLuaIHM::checkArgCount(ls, funcName, 3); // method with 2 args CLuaIHM::checkArgType(ls, funcName, 2, LUA_TNUMBER); CLuaIHM::checkArgType(ls, funcName, 3, LUA_TNUMBER); CVector result; if (getEditor().getIslandCollision().findEmptyPlace(CVector2f((float) ls.toNumber(2), (float) ls.toNumber(3)), result)) { ls.push(result.x); ls.push(result.y); ls.push(result.z); return 3; } return 0; } // ********************************************************************************************************* int CEditor::luaIsInIslandRect(CLuaState &ls) { //H_AUTO(R2_CEditor_luaIsInIslandRect) const char *funcName = "isInIslandRect"; CLuaIHM::checkArgCount(ls, funcName, 3); // method with 2 args CLuaIHM::checkArgType(ls, funcName, 2, LUA_TNUMBER); CLuaIHM::checkArgType(ls, funcName, 3, LUA_TNUMBER); const R2::CScenarioEntryPoints::CCompleteIsland *island = getEditor().getIslandCollision().getCurrIslandDesc(); if (!island) { ls.push(true); return 1; } float x = (float) ls.toNumber(2); float y = (float) ls.toNumber(3); ls.push(x >= island->XMin && y >= island->YMin && x <= island->XMax && y <= island->YMax); return 1; } // ********************************************************************************************************* int CEditor::luaGetCurrentIslandName(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetCurrentIslandName) const char *funcName = "getCurrentIslandName"; CLuaIHM::checkArgCount(ls, funcName, 1); // method with no args R2::CScenarioEntryPoints::CCompleteIsland *ci = getEditor().getIslandCollision().getCurrIslandDesc(); ls.push(ci ? ci->Island.c_str() : ""); return 1; } // ********************************************************************************************************* int CEditor::luaKickCharacter(CLuaState &ls) { //H_AUTO(R2_CEditor_luaKickCharacter) const char *funcName = "kickCharacter"; CLuaIHM::checkArgCount(ls, funcName, 2); // this is a method (self + 1 params) CLuaIHM::checkArgType(ls, funcName, 2, LUA_TNUMBER); CSessionBrowserImpl &sb = CSessionBrowserImpl::getInstance(); sb.kickCharacter(sb.getCharId(), R2::getEditor().getDMC().getEditionModule().getCurrentAdventureId(), (uint32)ls.toNumber(2)); if(!sb.waitOneMessage(sb.getMessageName("on_invokeResult"))) nlwarning("kickCharacter callback return false"); return 0; } // ********************************************************************************************************* int CEditor::luaUnkickCharacter(CLuaState &ls) { //H_AUTO(R2_CEditor_luaUnkickCharacter) const char *funcName = "unkickCharacter"; CLuaIHM::checkArgCount(ls, funcName, 2); // this is a method (self + 1 params) CLuaIHM::checkArgType(ls, funcName, 2, LUA_TNUMBER); CSessionBrowserImpl &sb = CSessionBrowserImpl::getInstance(); sb.unkickCharacter(sb.getCharId(), R2::getEditor().getDMC().getEditionModule().getCurrentAdventureId(), (uint32)ls.toNumber(2)); if(!sb.waitOneMessage(sb.getMessageName("on_invokeResult"))) nlwarning("unkickCharacter callback return false"); return 0; } // ********************************************************************************************************* int CEditor::luaTeleportToCharacter(CLuaState &ls) { //H_AUTO(R2_CEditor_luaTeleportToCharacter) const char *funcName = "teleportToCharacter"; CLuaIHM::checkArgCount(ls, funcName, 2); // this is a method (self + 1 params) CLuaIHM::checkArgType(ls, funcName, 2, LUA_TNUMBER); CClientEditionModule & cem = R2::getEditor().getDMC().getEditionModule(); cem.requestTeleportOneCharacterToAnother(cem.getCurrentAdventureId(), CSessionBrowserImpl::getInstance().getCharId(), (uint32)ls.toNumber(2)); return 0; } // ********************************************************************************************************* int CEditor::luaWaitScenarioScreen(CLuaState &ls) { //H_AUTO(R2_CEditor_luaWaitScenarioScreen) const char *funcName = "waitScenarioScreen"; CLuaIHM::checkArgCount(ls, funcName, 1); // method with no args if (!ClientCfg.Local) { getEditor()._WaitScenarioScreenWanted = true; } return 0; } // ********************************************************************************************************* int CEditor::luaIsScenarioUpdating(CLuaState &ls) { //H_AUTO(R2_CEditor_luaIsScenarioUpdating) const char *funcName = "isScenarioUpdating"; CLuaIHM::checkArgCount(ls, funcName, 1); // method with no args ls.push( (double)getEditor()._UpdatingScenario ); return 1; } /* // ********************************************************************************************************* int CEditor::luaIsValidPoly(CLuaState &ls) { //H_AUTO(R2_CEditor_luaIsValidPoly) const char *funcName = "isValidPoly"; CLuaIHM::checkArgCount(ls, funcName, 2); // method with no args CLuaIHM::checkArgType(ls, funcName, 3, LUA_TTABLE); // method with no args ls.pushValue(3); CLuaObject poly; NLMISC::CPolygon2D poly2D; poly.pop(luaState); ENUM_LUA_TABLE(table, it) { NLMISC::CVector2f pos; if (!CLuaIHM::pop(ls, pos)) { CLuaIHM::fails(ls, "%s expects CVector2f for poly coordinates); } poly2D.Vertices.push_back(pos); } ls.push(getEditor().getIslandCollision().isValidPoly(poly2D); return 1; } // ********************************************************************************************************* int CEditor::luaIsValidPosition(CLuaState &ls) { CHECK_EDITOR const char *funcName = "snapPosToGround"; CLuaIHM::checkArgCount(ls, funcName, 3); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TNUMBER); CLuaIHM::checkArgType(ls, funcName, 3, LUA_TNUMBER); // // NLPACS::UGlobalPosition gpos = GR->retrievePosition(CVector((float) ls.toNumber(2), (float) ls.toNumber(3), 2000.f)); if (gpos.InstanceId != -1) { CVector snappedPos = GR->getGlobalPosition(gpos); ls.push((double) snappedPos.x); ls.push((double) snappedPos.y); ls.push((double) snappedPos.z); return 3; } else { // default z to 0 ls.push(0.f); return 3; } // new algo : to avoid to snapping to a cliff, start from the approximate (valid) height found in the height map CVector inter; if (CTool::computeWorldMapIntersection((float) ls.toNumber(2), (float) ls.toNumber(3), inter) != CTool::NoIntersection) { ls.push((double) inter.x); ls.push((double) inter.y); ls.push((double) inter.z); } */ // ********************************************************************************************************* int CEditor::luaCanUndo(CLuaState &ls) { //H_AUTO(R2_CEditor_luaCanUndo) const char *funcName = "canUndo"; CLuaIHM::checkArgCount(ls, funcName, 1); // method with no args ls.push(getEditor().getDMC().getActionHistoric().canUndo()); return 1; } // ********************************************************************************************************* int CEditor::luaCanRedo(CLuaState &ls) { //H_AUTO(R2_CEditor_luaCanRedo) const char *funcName = "canRedo"; CLuaIHM::checkArgCount(ls, funcName, 1); // method with no args ls.push(getEditor().getDMC().getActionHistoric().canRedo()); return 1; } // ********************************************************************************************************* int CEditor::luaGetUserEntityName(CLuaState &ls) { //H_AUTO(R2_CEditor_luaGetUserEntityName) const char *funcName = "getUserEntityName"; CLuaIHM::checkArgCount(ls, funcName, 1); // this is a method if (UserEntity) { ucstring name = UserEntity->getEntityName()+PlayerSelectedHomeShardNameWithParenthesis; ls.push( std::string( name.toUtf8() ) ); } else { ls.push(std::string("")); } return 1; } static CLuaString lstr_Next("next"); // ********************************************************************************************************* int CEditor::luaEnumInstances(CLuaState &ls) { //H_AUTO(R2_CEditor_luaEnumInstances) CLuaStackChecker lsc(&ls, 1); // return an enumerator that allows to iterate over instance, by kind const char *funcName = "enumInstances"; CLuaIHM::checkArgCount(ls, funcName, 2); // method with no args CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); struct CInstanceEnumerator { CSortedInstances *InstMap; TInstanceByName::iterator Current; static int next(CLuaState &ls) { const char *funcName = "InstanceEnumerator:next"; CLuaIHM::checkArgCount(ls, funcName, 1); CLuaIHM::checkArgType(ls, funcName, 1, LUA_TUSERDATA); CInstanceEnumerator *ie = (CInstanceEnumerator *) ls.toUserData(1); CEditor &ed = getEditor(); while(ie->Current != ie->InstMap->end()) { CInstance *inst = ie->Current->second; if (!inst->getGhost()) { // object must be in current act or base act CInstance *parentAct = inst->getParentAct(); if (!parentAct || parentAct == ed.getCurrentAct() || parentAct == ed.getBaseAct()) { inst->getLuaProjection().push(); ++ ie->Current; return 1; } } ++ ie->Current; } return 0; } // static int gc(CLuaState &ls) { const char *funcName = "InstanceEnumerator:next"; CLuaIHM::checkArgCount(ls, funcName, 1); CLuaIHM::checkArgType(ls, funcName, 1, LUA_TUSERDATA); CInstanceEnumerator *ie = (CInstanceEnumerator *) ls.toUserData(1); ie->~CInstanceEnumerator(); // no real effect for now... return 0; } static int index(CLuaState &ls) { const char *funcName = "InstanceEnumerator.__index"; CLuaIHM::checkArgCount(ls, funcName, 2); CLuaIHM::checkArgType(ls, funcName, 1, LUA_TUSERDATA); CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); if (ls.toString(2) == lstr_Next) { ls.push(CInstanceEnumerator::next); return 1; } return 0; } }; sint classIndex = getEditor().classToIndex(ls.toString(2)); if (classIndex < 0) { CLuaIHM::fails(ls, "Trying to iterate over unknown class : %s", ls.toString(2)); } ls.newTable(); CLuaObject mt; mt.pop(ls); ls.push(CInstanceEnumerator::index); mt.setValue("__index", CLuaObject(ls)); ls.push(CInstanceEnumerator::gc); mt.setValue("__gc", CLuaObject(ls)); // void *newIter = ls.newUserData(sizeof(CInstanceEnumerator)); CInstanceEnumerator *ie = new (newIter) CInstanceEnumerator; ie->InstMap = &getEditor()._InstancesByDispName[classIndex]; ie->Current = ie->InstMap->begin(); mt.push(); ls.setMetaTable(-2); // return 1; } // ********************************************************************************************************* void CEditor::waitScenarioScreen() { //H_AUTO(R2_CEditor_waitScenarioScreen) if (ClientCfg.Local) return; if (_Mode==EditionMode) { setMode(GoingToEditionMode); } getUI().hideAllWindows(); CInterfaceGroup *waitScreen = dynamic_cast(getUI().getElementFromId("ui:interface:r2ed_connecting")); if (waitScreen) { waitScreen->setActive(true); getUI().setTopWindow(waitScreen); } // enum TState { WaitingScenario, WaitingTP, DoExit }; TState state = WaitingScenario; // getEditor()._ScenarioReceivedFlag = false; getEditor()._TPReceivedFlag = false; bool firewallTimeout = false; // ActionsContext.setContext("waiting_network"); TGameCycle serverTick = NetMngr.getCurrentServerTick(); getUI().setCaptureKeyboard(NULL); getUI().setDefaultCaptureKeyboard(NULL); loadBackgroundBitmap (StartBackground); // patch for the 'sys info that pop' prb (cause unknown for now ...) CInterfaceElement *sysInfo = getUI().getElementFromId("ui:interface:system_info"); bool sysInfoActive = false; if (sysInfo) sysInfoActive = sysInfo->getActive(); for (;;) { switch(state) { case WaitingScenario: if (getEditor()._ScenarioReceivedFlag) { if (getEditor()._WillTP) { state = WaitingTP; } else { state = DoExit; } } break; case WaitingTP: // no-op break; default: nlassert(0); break; } if (state == DoExit) { break; } // Update network. try { if ( ! firewallTimeout ) NetMngr.update(); } catch (const EBlockedByFirewall&) { if ( NetMngr.getConnectionState() == CNetManager::Disconnect ) { firewallTimeout = true; } else { // Display the firewall alert string CViewText *pVT = dynamic_cast(getUI().getElementFromId("ui:interface:r2ed_connecting:title")); if (pVT != NULL) pVT->setText(CI18N::get("uiFirewallAlert")+ucstring("...")); // The mouse and fullscreen mode should be unlocked for the user to set the firewall permission nlSleep( 30 ); // 'nice' the client, and prevent to make too many send attempts } } CCDBNodeBranch::flushObserversCalls(); // check if we can send another dated block if (NetMngr.getCurrentServerTick() != serverTick) { // serverTick = NetMngr.getCurrentServerTick(); NetMngr.send(serverTick); } else { // Send dummy info NetMngr.send(); } // update module manager NLNET::IModuleManager::getInstance().updateModules(); // Update the DT T0 and T1 global variables updateClientTime(); CInputHandlerManager::getInstance()->pumpEvents(); Driver->clearBuffers(CRGBA::Black); Driver->setMatrixMode2D11(); drawLoadingBitmap (1); if ( state==WaitingTP && getEditor()._TPReceivedFlag) { break; // don't want an additionnal swap buffer here, so exit now (the loading bitmap is good, // has just been reloaded by the tp screen... } // Interface handling & displaying (processes clicks...) getUI().updateFrameEvents(); if (waitScreen) { getUI().setTopWindow(waitScreen); } if (sysInfo) sysInfo->setActive(false); getUI().updateFrameViews(NULL); CCDBNodeBranch::flushObserversCalls(); // Movie shooter globalMenuMovieShooter(); // Force the client to sleep a bit. if(ClientCfg.Sleep >= 0) { nlSleep(ClientCfg.Sleep); } // if (!waitScreen) { TextContext->setShaded(true); TextContext->setFontSize(40); TextContext->setColor(CRGBA::White); // TOP LEFT // //----------// TextContext->setHotSpot(NL3D::UTextContext::MiddleMiddle); TextContext->printfAt(0.5f, 0.5f, "(UI NOT FOUND) Waiting scenario from server."); } // Display Driver->swapBuffers(); // if (NetMngr.getConnectionState() == CNetManager::Disconnect) { if ( firewallTimeout ) { // Display the firewall error string instead of the normal failure string CViewText *pVT = dynamic_cast(getUI().getElementFromId("ui:interface:r2ed_connecting:title")); if (pVT != NULL) { pVT->setMultiLine( true ); pVT->setText(CI18N::get("uiFirewallFail")+ucstring(".\n")+ CI18N::get("uiFirewallAlert")+ucstring(".")); } } } } if (sysInfo) sysInfo->setActive(sysInfoActive); destroyLoadingBitmap (); if (_Mode == GoingToEditionMode) { setMode(EditionMode); } return; } // ********************************************************************************************************* // lua instance observer class CInstanceObserverLua : public CEditor::IInstanceObserver { public: CInstanceObserverLua(CLuaObject &receiver); ~CInstanceObserverLua(); CLuaObject &getReceiver() { return _Receiver; } // from IInstanceObserver 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 onAttrModified(CInstance &instance, const std::string &attrName, sint32 attrIndex); private: CLuaObject _Receiver; // table that will receive the notifications static uint _Count; }; uint CInstanceObserverLua::_Count = 0; CInstanceObserverLua::CInstanceObserverLua(CLuaObject &receiver) : _Receiver(receiver) { ++ _Count; } CInstanceObserverLua::~CInstanceObserverLua() { #ifdef NL_DEBUG nlassert(!getEditor().isInstanceObserver(this)); #endif nlassert(_Count < 1000000000); -- _Count; } void CInstanceObserverLua::onInstanceCreated(CInstance &instance) { //H_AUTO(R2_CInstanceObserverLua_onInstanceCreated) nlassert(_Receiver.isValid()); if (_Receiver["onInstanceCreated"].isNil()) return; // no-op if not handled getEditor().projectInLua(instance.getObjectTable()); _Receiver.callMethodByNameNoThrow("onInstanceCreated", 1, 0); } void CInstanceObserverLua::onInstanceErased(CInstance &instance) { //H_AUTO(R2_CInstanceObserverLua_onInstanceErased) nlassert(_Receiver.isValid()); if (_Receiver["onInstanceErased"].isNil()) return; // no-op if not handled getEditor().projectInLua(instance.getObjectTable()); _Receiver.callMethodByNameNoThrow("onInstanceErased", 1, 0); } void CInstanceObserverLua::onInstancePreHrcMove(CInstance &instance) { //H_AUTO(R2_CInstanceObserverLua_onInstancePreHrcMove) nlassert(_Receiver.isValid()); if (_Receiver["onInstanceCreated"].isNil()) return; // no-op if not handled getEditor().projectInLua(instance.getObjectTable()); _Receiver.callMethodByNameNoThrow("onInstancePreHrcMove", 1, 0); } void CInstanceObserverLua::onInstancePostHrcMove(CInstance &instance) { //H_AUTO(R2_CInstanceObserverLua_onInstancePostHrcMove) nlassert(_Receiver.isValid()); if (_Receiver["onInstanceCreated"].isNil()) return; // no-op if not handled getEditor().projectInLua(instance.getObjectTable()); _Receiver.callMethodByNameNoThrow("onInstancePostHrcMove", 1, 0); } void CInstanceObserverLua::onPreHrcMove(CInstance &instance) { //H_AUTO(R2_CInstanceObserverLua_onPreHrcMove) nlassert(_Receiver.isValid()); if (_Receiver["onPreHrcMove"].isNil()) return; // no-op if not handled getEditor().projectInLua(instance.getObjectTable()); _Receiver.callMethodByNameNoThrow("onPreHrcMove", 1, 0); } void CInstanceObserverLua::onPostHrcMove(CInstance &instance) { //H_AUTO(R2_CInstanceObserverLua_onPostHrcMove) nlassert(_Receiver.isValid()); if (_Receiver["onPostHrcMove"].isNil()) return; // no-op if not handled getEditor().projectInLua(instance.getObjectTable()); _Receiver.callMethodByNameNoThrow("onPostHrcMove", 1, 0); } void CInstanceObserverLua::onAttrModified(CInstance &instance, const std::string &attrName, sint32 attrIndex) { //H_AUTO(R2_CInstanceObserverLua_onAttrModified) nlassert(_Receiver.isValid()); if (_Receiver["onAttrModified"].isNil()) return; // no-op if not handled getEditor().projectInLua(instance.getObjectTable()); getEditor().getLua().push(attrName); getEditor().getLua().push((double) attrIndex); _Receiver.callMethodByNameNoThrow("onAttrModified", 3, 0); } // ********************************************************************************************************* int CEditor::luaAddInstanceObserver(CLuaState &ls) { //H_AUTO(R2_CEditor_luaAddInstanceObserver) CHECK_EDITOR const char *funcName = "addInstanceObserver"; CLuaIHM::checkArgCount(ls, funcName, 3); // this is a method (self + 2 params) CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); // instance id CLuaIHM::checkArgType(ls, funcName, 3, LUA_TTABLE); // receiver CLuaObject receiver(ls); // pop the receiver ls.push((double) getEditor().addInstanceObserver(ls.toString(2), new CInstanceObserverLua(receiver))); return 1; } // ********************************************************************************************************* int CEditor::luaRemoveInstanceObserver(CLuaState &ls) { //H_AUTO(R2_CEditor_luaRemoveInstanceObserver) CHECK_EDITOR const char *funcName = "removeInstanceObserver"; CLuaIHM::checkArgCount(ls, funcName, 2); // this is a method (self + 1 params) CLuaIHM::checkArgType(ls, funcName, 2, LUA_TNUMBER); // instance id IInstanceObserver *observer = getEditor().getInstanceObserver((TInstanceObserverHandle) ls.toNumber(2)); if (observer == NULL) { CLuaIHM::fails(ls, "Instance observer not found for handle = %d", (int) ls.toNumber(2)); } CInstanceObserverLua *luaObserver = dynamic_cast(observer); if (luaObserver == NULL) { CLuaIHM::fails(ls, "Instance observer found for handle %d, but has bad type, it wasn't registered from lua.", (int) ls.toNumber(2)); } getEditor().removeInstanceObserver((TInstanceObserverHandle) ls.toNumber(2)); CLuaObject receiver = luaObserver->getReceiver(); delete luaObserver; receiver.push(); return 1; } // ********************************************************************************************************* bool CEditor::isInstanceObserver(IInstanceObserver *observer) const { //H_AUTO(R2_CEditor_isInstanceObserver) for(TInstanceObserverMap::const_iterator it = _InstanceObservers.begin(); it != _InstanceObservers.end(); ++it) { if (it->second == observer) return true; } return false; } // ********************************************************************************************************* const CObjectTable *CEditor::getObjectTableFromId(const TInstanceId &id) const { //H_AUTO(R2_CEditor_getObjectTableFromId) CHECK_EDITOR nlassert(_DMC); const CObject *obj = _DMC->find(id); if (!obj) return NULL; nlassert(obj->isTable()); return (CObjectTable *) obj; } // ********************************************************************************************************* CInstance *CEditor::getInstanceFromObject(const CObject *obj) const { //H_AUTO(R2_CEditor_getInstanceFromObject) CHECK_EDITOR if (!obj->isTable()) return NULL; TInstanceMap::const_iterator it = _Instances.find((CObjectTable *) obj); if (it != _Instances.end()) { return it->second; } return NULL; } // ********************************************************************************************************* void CEditor::registerEnvMethod(const char *name,TLuaWrappedFunction func) { //H_AUTO(R2_CEditor_registerEnvMethod) CHECK_EDITOR nlassert(name); getEnv().setValue(name, func); } // ********************************************************************************************************* void CEditor::registerEnvFunction(const char *name, TLuaWrappedFunction func) { //H_AUTO(R2_CEditor_registerEnvFunction) CHECK_EDITOR nlassert(name); getEnv().setValue(name, func); } // ********************************************************************************************************* void CEditor::registerLuaFunc() { //H_AUTO(R2_CEditor_registerLuaFunc) CHECK_EDITOR registerEnvMethod("getSelectedInstanceId", luaGetSelectedInstanceId); registerEnvMethod("setSelectedInstanceId", luaSetSelectedInstanceId); registerEnvMethod("setCurrentTool", luaSetCurrentTool); registerEnvMethod("getCurrentTool", luaGetCurrentTool); registerEnvMethod("getVisualPropertiesFromInstanceId", luaGetVisualPropertiesFromInstanceId); registerEnvMethod("getInstanceFromId", luaGetInstanceFromId); registerEnvMethod("displayContextMenu", luaDisplayContextMenu); registerEnvMethod("getSelectedInstance", luaGetSelectedInstance); registerEnvMethod("connectAsCreator", luaConnectAsCreator); registerEnvMethod("doFile", luaDofile); registerEnvMethod("tryFile", luaTryFile); registerEnvMethod("setEntityCustomSelectBox", luaSetEntityCustomSelectBox); registerEnvMethod("getEntityCustomSelectBox", luaGetEntityCustomSelectBox); registerEnvMethod("choosePos", luaChoosePos); registerEnvMethod("snapPosToGround", luaSnapPosToGround); registerEnvMethod("getUserEntityPosition", luaGetUserEntityPosition); registerEnvMethod("getUserEntityFront", luaGetUserEntityFront); registerEnvMethod("setCurrentActFromId", luaSetCurrentActFromId); registerEnvMethod("getCurrentAct", luaGetCurrentAct); registerEnvMethod("genInstanceName", luaGenInstanceName); registerEnvMethod("isPostFixedByNumber", luaIsPostFixedByNumber); registerEnvMethod("setCookie", luaSetCookie); registerEnvMethod("addInstanceObserver", luaAddInstanceObserver); registerEnvMethod("removeInstanceObserver", luaRemoveInstanceObserver); registerEnvFunction("requestSetLocalNode", luaRequestSetLocalNode); registerEnvFunction("requestCommitLocalNode", luaRequestCommitLocalNode); registerEnvFunction("requestRollbackLocalNode", luaRequestRollbackLocalNode); registerEnvMethod("isCreature", luaIsCreature); registerEnvMethod("setEditorSeason", luaSetEditorSeason); registerEnvMethod("setFixedLighting", luaSetFixedLighting); registerEnvMethod("getFixedLighting", luaGetFixedLighting); registerEnvMethod("setPlotItemInfos", luaSetPlotItemInfos); registerEnvMethod("isCurrentSelectionPlayer", luaIsCurrentSelectionPlayer); registerEnvMethod("findEmptyPlace", luaFindEmptyPlace); registerEnvMethod("isInIslandRect", luaIsInIslandRect); registerEnvMethod("getCurrentIslandName", luaGetCurrentIslandName); registerEnvMethod("waitScenarioScreen", luaWaitScenarioScreen); registerEnvMethod("isScenarioUpdating", luaIsScenarioUpdating); registerEnvMethod("canUndo", luaCanUndo); registerEnvMethod("canRedo", luaCanRedo); registerEnvMethod("getUserEntityName", luaGetUserEntityName); registerEnvMethod("getStartingAnimationFilename", luaGetStartingAnimationFilename); registerEnvMethod("kickCharacter", luaKickCharacter); registerEnvMethod("unkickCharacter", luaUnkickCharacter); registerEnvMethod("teleportToCharacter", luaTeleportToCharacter); registerEnvMethod("enumInstances", luaEnumInstances); registerEnvMethod("isClearingContent", luaIsClearingContent); } /* static void polyUnitTest() { CPolygon2D poly; poly.Vertices.push_back(CVector2f(0.f, 0.f)); poly.Vertices.push_back(CVector2f(1.f, 0.f)); poly.Vertices.push_back(CVector2f(1.f, 1.f)); poly.Vertices.push_back(CVector2f(0.f, 1.f)); nlassert(poly.contains(CVector2f(0.f, 0.f), false)); nlassert(poly.contains(CVector2f(1.f, 0.f), false)); nlassert(poly.contains(CVector2f(1.f, 1.f), false)); nlassert(poly.contains(CVector2f(0.f, 1.f), false)); nlassert(poly.contains(CVector2f(0.5f, 0.f), false)) nlassert(poly.contains(CVector2f(1.f, 0.5f), false)); nlassert(poly.contains(CVector2f(0.5f, 1.f), false)); nlassert(poly.contains(CVector2f(0.f, 0.5f), false)); nlassert(poly.contains(CVector2f(0.5f, 0.5f), false)); // nlassert(!poly.contains(CVector2f(-0.5f, 0.5f), false)); nlassert(!poly.contains(CVector2f(1.5f, 0.5f), false)); nlassert(!poly.contains(CVector2f(0.5f, -0.5f), false)); nlassert(!poly.contains(CVector2f(0.5f, 1.5f), false)); } */ // ********************************************************************************************************* void CEditor::loadLanguageFile() { //H_AUTO(R2_CEditor_loadLanguageFile) CHECK_EDITOR static bool reload = false; CI18N::ILoadProxy *oldLoadProxy = CI18N::getLoadProxy(); CI18N::setLoadProxy(NULL); string lc = ClientCfg.LanguageCode; nlinfo("Reloading the r2_%s.uxt", lc.c_str()); CI18N::loadFromFilename("r2_"+lc+".uxt", reload); CI18N::setLoadProxy(oldLoadProxy); reload = true; } // ********************************************************************************************************* void CEditor::loadKeySet(const std::string &keySet) { //H_AUTO(R2_CEditor_loadKeySet) CHECK_EDITOR if (keySet.empty()) return; CVerboseClock clock("Parsing of keyset " + keySet); // remove all existing keys ActionsContext.removeAllCombos(); CMacroCmdManager::getInstance()->removeAllMacros(); // parse keys that are specific to r2ed std::vector xmlFilesToParse; // Does the r2ed keys file exist ? std::string userKeyFileName = "save/" + keySet + "_"+PlayerSelectedFileName+".xml"; if (CFile::fileExists(userKeyFileName) && CFile::getFileSize(userKeyFileName) > 0) { // Load the user key file xmlFilesToParse.push_back (userKeyFileName); } // Load the default key (but don't replace existings bounds, see keys.xml "key_def_no_replace") xmlFilesToParse.push_back (keySet + ".xml"); if (!CInterfaceManager::getInstance()->parseInterface (xmlFilesToParse, true)) { badXMLParseMessageBox(); } CActionsManager *actionManager = ActionsContext.getActionsManager(); if (actionManager) { // if some mode are unavailable, forbid the associated shortcuts /*if (!_ModeEnabled[EditionMode]) { actionManager->removeBaseAction("r2ed_stop_test"); } if (!_ModeEnabled[TestMode] || !_ModeEnabled[DMMode]) { actionManager->removeBaseAction("r2ed_try_go_test"); }*/ } } // ********************************************************************************************************* void CEditor::saveCurrentKeySet() { //H_AUTO(R2_CEditor_saveCurrentKeySet) CHECK_EDITOR std::string prefix = getKeySetPrefix(getMode()); if (prefix.empty()) return; getUI().saveKeys ("save/" + prefix + "_" + PlayerSelectedFileName + ".xml"); } // ********************************************************************************************************* std::string CEditor::getKeySetPrefix(TMode mode) { //H_AUTO(R2_CEditor_getKeySetPrefix) CHECK_EDITOR switch(mode) { case EditionMode: return "keys_r2ed"; case GoingToDMMode: case GoingToEditionMode: case AnimationModeLoading: case AnimationModeWaitingForLoading: case AnimationModeGoingToDm: case AnimationModeGoingToPlay: return ""; case AnimationModePlay: return "keys"; default: return "keys"; } return std::string(); } // ********************************************************************************************************* void CEditor::setUIMode(uint8 mode) { //H_AUTO(R2_CEditor_setUIMode) CHECK_EDITOR getUI().setMode(mode); if (_ForceDesktopReset[mode]) { // force to call reset when reloading the ui getLua().push((double) mode); callEnvMethod("resetDesktop", 1, 0); _ForceDesktopReset[mode] = false; } getLua().push((double) mode); callEnvMethod("onChangeDesktop", 1, 0); } // ********************************************************************************************************* void CEditor::loadStandardUI() { //H_AUTO(R2_CEditor_loadStandardUI) getUI().setMode(0); // force to reload the standard interface ClientCfg.R2EDEnabled = false; loadUIConfig(""); ClientCfg.R2EDEnabled = true; getUI().updateAllLocalisedElements(); } // ********************************************************************************************************* bool CEditor::isDMing() const { //H_AUTO(R2_CEditor_isDMing) if (getMode() == AnimationModePlay) return false; // masterless mode -> not a dm return getMode() == DMMode || getMode() == AnimationModeDm || ( getMode() != NotInitialized && getAccessMode() == AccessDM ); } // ********************************************************************************************************* void CEditor::setMode(TMode mode) { switch(mode) { case NotInitialized: nlwarning("*R2* Setting mode to NotInitialized"); break; case EditionMode: nlwarning("*R2* Setting mode to EditionMode"); break; case GoingToDMMode: nlwarning("*R2* Setting mode to GoingToDMMode"); break; case GoingToEditionMode: nlwarning("*R2* Setting mode to GoingToEditionMode"); break; case TestMode: nlwarning("*R2* Setting mode to TestMode"); break; case DMMode: nlwarning("*R2* Setting mode to DMMode"); break; case AnimationModeLoading: nlwarning("*R2* Setting mode to AnimationModeLoading"); break; case AnimationModeWaitingForLoading: nlwarning("*R2* Setting mode to AnimationModeWaitingForLoading"); break; case AnimationModeDm: nlwarning("*R2* Setting mode to AnimationModeDm"); break; case AnimationModePlay: nlwarning("*R2* Setting mode to AnimationModePlay"); break; case AnimationModeGoingToDm: nlwarning("*R2* Setting mode to AnimationModeGoingToDm"); break; case AnimationModeGoingToPlay: nlwarning("*R2* Setting mode to AnimationModeGoingToPlay"); break; default: nlwarning("Setting mode to 'unknown'"); break; } if (mode != EditionMode) { delete _EntitySorter; _EntitySorter = NULL; } //H_AUTO(R2_CEditor_setMode) CHECK_EDITOR //nlassert(_ModeEnabled[mode]); if (mode == _Mode && !_ForceDesktopReset[mode]) return; if (mode != _Mode) { if ((_Mode == AnimationModeDm || _Mode == AnimationModePlay) && !((mode == AnimationModeDm || mode == AnimationModePlay))) { // remove the chat windows when leaving animation mode PeopleInterraction.removeAllFreeTellers(); } CVerboseClock clock("Saving windows & key at a mode change ..."); if (_Mode != NotInitialized) { saveCurrentKeySet(); } //if (_Mode != NotInitialized) saveUIConfig(); if (_Mode == EditionMode || _Mode == AnimationModeLoading) { getDMC().getActionHistoric().clear(); // no backup of undo/redo for now clearContent(); // when leaving edition, remove content from tree } else if (mode == EditionMode || mode == GoingToEditionMode) { removeAllEntitySlots(); // when leaving test or dm, remove the entities slots } if (mode == DMMode || mode == AnimationModeDm) { getEditor().getDMC().getEditionModule().askUpdateCharMode(TCharMode::Dm); } else if (mode == EditionMode) { getEditor().getDMC().getEditionModule().askUpdateCharMode(TCharMode::Editer); } else if (mode == AnimationModePlay) { if(_Mode != NotInitialized) { if (_SerializeUIConfig) { saveUIConfig(); } } getEditor().getDMC().getEditionModule().askUpdateCharMode(TCharMode::Player); // if access mode is DM, then load the whole ui if (_AccessMode == AccessDM) { loadStandardUI(); } } else if (mode == TestMode) { getEditor().getDMC().getEditionModule().askUpdateCharMode(TCharMode::Tester); } else // Other like going to dm ... { } } // reset any selection if ((!IngameDbMngr.initInProgress()) && UserEntity) // prevent from calling selection() if coming from a FarTP, otherwise the server will mess up the action counter because it can't know our datasetrow so early (initInProgress(): heuristic to ensure the setTarget() will work on the EGS) { UserEntity->selection(CLFECOMMON::INVALID_SLOT); } // ContextCur.release(); _Mode = mode; loadKeySet(getKeySetPrefix(_Mode)); getUI().disableModalWindow(); getUI().setCapturePointerLeft(NULL); getUI().setCapturePointerRight(NULL); getUI().setCaptureKeyboard(NULL); // Season is now unknown, until server force it (in test mode), or first set act set it (in edit mode) _Season = UnknownSeason; // switch(_Mode) { case EditionMode: if (!_EntitySorter) { _EntitySorter = new CEntitySorter; } ActionsContext.setContext("r2ed"); // set new mode in lua _Env.setValue("Mode", "Edit"); setUIMode(0); ::IgnoreEntityDbUpdates = true; ::SlotUnderCursor = CLFECOMMON::INVALID_SLOT; /** * nb we manage mouse bitmap ourselves, so there's only one cursor context here, and it never changes: * its sole purpose is to route messages to the current tool */ ContextCur.add(false, "STAND BY", DEFAULT_CURSOR, 0.0f, CEditor::checkCursor, CEditor::mouseClick); forceSetSelectedInstance(NULL); initReferencePlotItems(); // force speed to "run" ("walk" not available in edition) if (!UserEntity->running()) { UserEntity->switchVelocity(); } break; case TestMode: ActionsContext.setContext("r2ed_anim_test"); // set new mode in lua _Env.setValue("Mode", "Test"); setUIMode(1); ::IgnoreEntityDbUpdates = false; ::initContextualCursor(); nlassert(CDisplayerBase::ObjCount == 0); resetPlotItems(); break; case DMMode: ActionsContext.setContext("r2ed_anim_dm"); // set new mode in lua _Env.setValue("Mode", "DM"); setUIMode(2); ::IgnoreEntityDbUpdates = false; ::initContextualCursor(); nlassert(CDisplayerBase::ObjCount == 0); // when in local mode, init fake plot item to test the 'dm gift' interface initDummyPlotItems(); break; case GoingToDMMode: ActionsContext.setContext("waiting_network"); // set new mode in lua _Env.setValue("Mode", "GoingToDM"); setUIMode(3); connexionMsg(_ConnexionMsg); // update connexion window ::IgnoreEntityDbUpdates = false; ::initContextualCursor(); nlassert(CDisplayerBase::ObjCount == 0); resetPlotItems(); break; case GoingToEditionMode: nlassert(CDisplayerBase::ObjCount == 0); ActionsContext.setContext("waiting_network"); // set new mode in lua _Env.setValue("Mode", "BackToEditing"); setUIMode(3); connexionMsg(_ConnexionMsg); // update connexion window ::IgnoreEntityDbUpdates = true; ::initContextualCursor(); resetPlotItems(); break; case AnimationModeLoading: nlassert(CDisplayerBase::ObjCount == 0); ActionsContext.setContext("waiting_network"); // set new mode in lua _Env.setValue("Mode", "AnimationModeLoading"); setUIMode(3); connexionMsg(_ConnexionMsg); // update connexion window // ::IgnoreEntityDbUpdates = true; ::initContextualCursor(); resetPlotItems(); break; case AnimationModeWaitingForLoading: nlassert(CDisplayerBase::ObjCount == 0); ActionsContext.setContext("waiting_network"); // set new mode in lua _Env.setValue("Mode", "AnimationModeWaitingForLoading"); setUIMode(3); connexionMsg(_ConnexionMsg); // update connexion window // ::IgnoreEntityDbUpdates = true; ::initContextualCursor(); resetPlotItems(); break; case AnimationModeDm: PeopleInterraction.updateAllFreeTellerHeaders(); // if invitation were received during loading, add 'invite' button to the new windows nlassert(CDisplayerBase::ObjCount == 0); ActionsContext.setContext("r2ed_anim_dm"); // set new mode in lua _Env.setValue("Mode", "AnimationModeDm"); setUIMode(2); ::IgnoreEntityDbUpdates = false; ::initContextualCursor(); resetPlotItems(); break; case AnimationModePlay: { // animation mode play is still visible in masterless mode nlassert(CDisplayerBase::ObjCount == 0); // no op here, just a player ActionsContext.setContext("game"); //ActionsContext.setContext("r2ed_anim_test"); // set new mode in lua _Env.setValue("Mode", "r2ed_anim_test"); // hide all ring windows that may remain hideRingWindows(); // setUIMode(1); // don't change desktop here, because virtual desktops are allowed in this mode getUI().setMode(0); // default to desktop 0 as with standard game ::IgnoreEntityDbUpdates = false; ::initContextualCursor(); resetPlotItems(); if (getDMC().getEditionModule().isSessionOwner()) { // if we are not a player, pop the ui to pop control player window & to quit callEnvMethod("popDMToolbarWindow", 0, 0); } else { callEnvMethod("playerModeUIFix", 0, 0); } } break; case AnimationModeGoingToDm: nlassert(CDisplayerBase::ObjCount == 0); ActionsContext.setContext("waiting_network"); //set new mode in lua _Env.setValue("Mode", "AnimationModeGoingToDM"); setUIMode(3); connexionMsg(_ConnexionMsg); // update connexion window ::IgnoreEntityDbUpdates = false; ::initContextualCursor(); resetPlotItems(); break; default: nlassert(0); break; } ClientCfg.ManualWeatherSetup = false; ContextCur.context("STAND BY"); _Mode = mode; // warn lua that mode has changed callEnvMethod("onModeChanged", 0, 0); // setCurrentTool(NULL); // display the bars over the player (not available during edition, but available at test time) UserEntity->buildInSceneInterface(); if (_Mode == EditionMode) { // special context menu for edition GameContextMenu.init("game_context_menu_edition"); } else { GameContextMenu.init(""); } } // ********************************************************************************************************* void CEditor::hideRingWindows() { //H_AUTO(R2_CEditor_hideRingWindows) static const char *ringWindows[] = { "r2ed_palette", "r2ed_connect", "r2ed_property_sheet_no_selection", "r2ed_property_sheet_no_properties", "r2ed_bbox_edit", "r2ed_toolbar", "r2ed_windows_dm_bar", "r2ed_toolbar_window", "r2ed_windows_bar", "r2ed_main_bl", "r2ed_animation_loading", "r2ed_animation_waiting", "dm_controlled_entities", "r2ed_current_session", "r2ed_toolbar_admin", "lua_inspector", "r2ed_npc", "r2ed_select_bar", "r2ed_main_menu_button", "r2ed_contextual_toolbar_new", "feature_help", "r2ed_logic_entities", "r2ed_events", "r2ed_activities", "r2ed_edit_activity_sequence", "r2ed_dialogs", "r2ed_edit_chat_sequence", "r2ed_mini_activity_view", "r2ed_acts", "r2ed_connecting", "r2ed_scenario", "r2ed_dm_gift", "r2ed_scenario_filter", "r2ed_testbar" }; for (uint k = 0; k < sizeofarray(ringWindows); ++k) { std::string id = "ui:interface:" + std::string(ringWindows[k]); CInterfaceElement *grp = getUI().getElementFromId(id); if (grp) { grp->setActive(false); } else { nlwarning("Can't find group with id : %s", id.c_str()); } } } // ********************************************************************************************************* void CEditor::init(TMode initialMode, TAccessMode accessMode) { nlwarning("*R2* init"); _EntitySorter = NULL; //H_AUTO(R2_CEditor_init) CNiceInputAuto niceInputs; nlassert((uint) initialMode < ModeCount); nlassert((uint) accessMode < AccessModeCount); //nlassert(_ModeEnabled[initialMode]; // mode not enabled ... CHECK_EDITOR CVerboseClock clock("Init of editor "); _Initialized = true; _AccessMode = accessMode; // add a list of files for which modifications will trigger R2ED reset for(uint k = 0; k < ClientCfg.R2EDReloadFiles.size(); ++k) { CFile::removeFileChangeCallback(ClientCfg.R2EDReloadFiles[k]); // avoid to add twice when a reset ... CFile::addFileChangeCallback(ClientCfg.R2EDReloadFiles[k], reloadEditorCallback); } // CLuaStackChecker lsc(&getLua()); getLua().pushValue(LUA_GLOBALSINDEX); _Globals.pop(getLua()); getLua().pushValue(LUA_REGISTRYINDEX); _Registry.pop(getLua()); // create R2 environment, and keep a reference on it _Env = _Globals.newTable(R2_LUA_PATH); _Config = _Env.newTable("Config"); // _Env.setValue("InClient", true); // TMP : signal to the script that it is initialised by the client // switch (accessMode) { case AccessEditor: _Env.setValue("AccessMode", "Editor"); break; case AccessDM: _Env.setValue("AccessMode", "DM"); break; case AccessOutlandOwner: _Env.setValue("AccessMode", "OutlandOwner"); break; default: nlassert(0); break; } // Load the UI files if (ReloadUIFlag) { loadLanguageFile(); reloadUI(); // setForceDesktopReset is done below ReloadUIFlag = false; } // registerDisplayers(); registerTools(); // std::vector emptyArgsList; // Calling window init proc registerLuaFunc(); // _DMC->init(getLua().getStatePointer()); // initObjectProjectionMetatable(); // init system to access to scenary objects from lua // init client/server stuffs CConfigVarBase::getConfigFileTimeStamp() ++; // invalidate all var taken from the config file // load r2 features & components { CVerboseClock clock("Execution of r2_core.lua"); doLuaScript("r2_core.lua", "r2ed common functions and definitions"); } initClassInheritanceTable(); // { CLuaStackChecker lsc4(&getLua()); setCurrentTool(NULL); // force the default tool (select / move) } initDecals(); // TMP nico : unit test for newpoly stuff //polyUnitTest(); _Initialized = true; _LuaUIMainLoop = _Env["UIMainLoop"]; if (_SerializeUIConfig) { // load virtual desktops configs bool configLoaded = false; { CVerboseClock clock("Load of ui config from disk "); // if access mode is DM, then load the whole ui (else it has been previuously loaded) if (initialMode == AnimationModePlay) { loadStandardUI(); } else { configLoaded = loadUIConfig(getUIPrefix(initialMode)); } } // if not found then reset all virtual desktops (one for each mode of the editor) if (!configLoaded) { nlwarning("No interface config found, resetting editor windows to their default"); setForceDesktopReset(true); } } setMode(initialMode); { CVerboseClock clock("Update of localized elements"); getUI().updateAllLocalisedElements(); } } // ********************************************************************************************************* std::string CEditor::getUIPrefix(TMode mode) const { //H_AUTO(R2_CEditor_getUIPrefix) switch(mode) { case AnimationModePlay: return ""; break; default: return "r2ed_interface_"; break; } } // ********************************************************************************************************* uint CEditor::getMaxNumPlotItems() { //H_AUTO(R2_CEditor_getMaxNumPlotItems) uint ret; fromString(getUI().getDefine("r2ed_max_num_plot_item_sheets"), ret); return ret; } // ********************************************************************************************************* CCDBNodeLeaf *CEditor::getRefPlotItemSheetDBLeaf(uint index) { //H_AUTO(R2_CEditor_getRefPlotItemSheetDBLeaf) return getUI().getDbProp(toString("LOCAL:R2:REFERENCE_PLOT_ITEMS:%d:SHEET", (int) index), false); } // ********************************************************************************************************* CCDBNodeLeaf *CEditor::getPlotItemSheetDBLeaf(uint index) { //H_AUTO(R2_CEditor_getPlotItemSheetDBLeaf) return getUI().getDbProp(toString("LOCAL:R2:PLOT_ITEMS:%d:SHEET", (int) index), false); } // ********************************************************************************************************* void CEditor::setReferencePlotItemSheet(uint index, uint32 sheetId) { //H_AUTO(R2_CEditor_setReferencePlotItemSheet) CCDBNodeLeaf *leaf = getRefPlotItemSheetDBLeaf(index); if (leaf) { leaf->setValue32(sheetId); } leaf = getUI().getDbProp(toString("LOCAL:R2:AVAILABLE_PLOT_ITEMS:%d:SHEET", (int) index), false); if (leaf) { leaf->setValue32(sheetId); } } // ********************************************************************************************************* void CEditor::initDummyPlotItems() { //H_AUTO(R2_CEditor_initDummyPlotItems) uint maxNumPlotItems = getMaxNumPlotItems(); uint numItems = std::min(10u, maxNumPlotItems); uint k; for(k = 0; k < numItems; ++k) { CCDBNodeLeaf *destLeaf = getPlotItemSheetDBLeaf(k); if (!destLeaf) continue; CCDBNodeLeaf *srcLeaf = getRefPlotItemSheetDBLeaf(k); destLeaf->setValue32(srcLeaf ? srcLeaf->getValue32() : 0u); } for(; k < maxNumPlotItems; ++k) { CCDBNodeLeaf *destLeaf = getPlotItemSheetDBLeaf(k); if (!destLeaf) continue; destLeaf->setValue32(0); } } // ********************************************************************************************************* void CEditor::resetPlotItems() { //H_AUTO(R2_CEditor_resetPlotItems) uint maxNumPlotItems = getMaxNumPlotItems(); // uint numItems = std::min(10u, maxNumPlotItems); for(uint k = 0; k < maxNumPlotItems; ++k) { CCDBNodeLeaf *destLeaf = getPlotItemSheetDBLeaf(k); if (!destLeaf) continue; destLeaf->setValue32(0); } _PlotItemInfos.clear(); } // ********************************************************************************************************* void CEditor::initReferencePlotItems() { //H_AUTO(R2_CEditor_initReferencePlotItems) { CVerboseClock clock("InitReferencePlotItems"); uint maxNumPlotItems = getMaxNumPlotItems(); clamp(maxNumPlotItems, 0u, 1024u); uint currIndex = 0; // look for plot item sheets const CSheetManager::TEntitySheetMap &sheets = SheetMngr.getSheets(); for(CSheetManager::TEntitySheetMap::const_iterator it = sheets.begin(); it != sheets.end(); ++it) { if (it->second.EntitySheet && it->second.EntitySheet->Type == CEntitySheet::ITEM) { std::string name = it->second.EntitySheet->Id.toString(); if (strstr(name.c_str(), "r2_plot_item")) { uint32 sheetId = it->second.EntitySheet->Id.asInt(); setReferencePlotItemSheet(currIndex, sheetId); ++ currIndex; } } } for (; currIndex < maxNumPlotItems; ++currIndex) { setReferencePlotItemSheet(currIndex, 0); } resetPlotItems(); } } // ********************************************************************************************************* bool CEditor::loadUIConfig(const std::string &prefix) { //H_AUTO(R2_CEditor_loadUIConfig) CHECK_EDITOR return getUI().loadConfig("save/" + prefix + PlayerSelectedFileName + ".icfg"); } // ********************************************************************************************************* void CEditor::saveUIConfig() { //H_AUTO(R2_CEditor_saveUIConfig) CHECK_EDITOR // if there are dirt desktop, update all of them if (_Mode != AnimationModePlay) { bool restoreMode = false; uint8 oldMode = getUI().getMode(); for (uint k = 0; k < 4; ++k) { if (_ForceDesktopReset[k]) { setUIMode(k); restoreMode = true; _ForceDesktopReset[k] = false; } if (restoreMode) { setUIMode(oldMode); } } } getUI().saveConfig("save/" + getUIPrefix(_Mode) + PlayerSelectedFileName + ".icfg"); } // ********************************************************************************************************* void CEditor::initDecals() { //H_AUTO(R2_CEditor_initDecals) CHECK_EDITOR // highlight CLuaObject config = getEnv()["Config"]; CLuaObject objHighlight = config["HightlightDecalLook"]; CLuaObject objSelect = config["SelectDecalLook"]; CLuaObject objSelecting = config["SelectingDecalLook"]; CLuaObject objPioneer = config["PionneerDecalLook"]; _HighlightDecalAnim.buildFromLuaTable(objHighlight); _SelectDecalAnim.buildFromLuaTable(objSelect); _SelectingDecalAnim.buildFromLuaTable(objSelecting); _PionneerDecalAnim.buildFromLuaTable(objPioneer); _HighlightDecal.setClipDownFacing(true); _SelectDecal.setClipDownFacing(true); _PionneerDecal.setClipDownFacing(true); // CPrimLook boxLook; boxLook.init(getEnv()["PrimRender"]["SelectBoxLook"]); _SelectBox.setLook(boxLook); boxLook.init(getEnv()["PrimRender"]["HighlightBoxLook"]); _HighlightBox.setLook(boxLook); } // ********************************************************************************************************* void CEditor::showPrimRender(CPrimRender &dest, const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat, const CDecalAnim &refDecalAnim) { //H_AUTO(R2_CEditor_showPrimRender) CHECK_EDITOR static NLMISC::CPolygon2D poly2D; poly2D.Vertices.resize(4); CVector pmin = localBox.getMin(); CVector pmax = localBox.getMax(); poly2D.Vertices[0] = worldMat * CVector(pmin.x, pmin.y, pmin.z); poly2D.Vertices[1] = worldMat * CVector(pmax.x, pmin.y, pmin.z); poly2D.Vertices[2] = worldMat * CVector(pmax.x, pmax.y, pmin.z); poly2D.Vertices[3] = worldMat * CVector(pmin.x, pmax.y, pmin.z); NLMISC::CAABBox bbox; CLandscapePolyDrawer::computeBBoxFromPolygon(poly2D, bbox); dest.setVertices(poly2D.Vertices); //dest.setWorldMapPolyColor(polyColor); float animRatio = refDecalAnim.DurationInMs == 0 ? 0.f : (T1 % refDecalAnim.DurationInMs) / (float) refDecalAnim.DurationInMs; animRatio = 0.5f * cosf(2.f * (float) Pi * animRatio) + 0.5f; CRGBA color = blend(refDecalAnim.StartDiffuse, refDecalAnim.EndDiffuse, animRatio); CPrimLook pl = dest.getLook(); pl.EdgeLook.DecalColor = pl.VertexLook.DecalColor = pl.FirstVertexLook.DecalColor = color; dest.setLook(pl); dest.addDecalsToRenderList(); static volatile bool showPoly = true; if (showPoly) { CLandscapePolyDrawer::getInstance().addPoly(poly2D, dest.getLook().EdgeLook.WorldMapColor, bbox); } } // ********************************************************************************************************* void CEditor::showSelectBox(const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat) { //H_AUTO(R2_CEditor_showSelectBox) CHECK_EDITOR showPrimRender(_SelectBox, localBox, worldMat, _SelectDecalAnim); } // ********************************************************************************************************* void CEditor::showHighlightBox(const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat) { //H_AUTO(R2_CEditor_showHighlightBox) CHECK_EDITOR showPrimRender(_HighlightBox, localBox, worldMat, _HighlightDecalAnim); } // ********************************************************************************************************* void CEditor::showSelectDecal(const NLMISC::CVector &pos, float scale) { //H_AUTO(R2_CEditor_showSelectDecal) CHECK_EDITOR updateDecalBlendRegion(_SelectDecal, pos); showDecal(pos, scale, _SelectDecal, _SelectDecalAnim); } // ********************************************************************************************************* void CEditor::showHighlightDecal(const NLMISC::CVector &pos, float scale) { //H_AUTO(R2_CEditor_showHighlightDecal) CHECK_EDITOR updateDecalBlendRegion(_HighlightDecal, pos); showDecal(pos, scale, _HighlightDecal, _HighlightDecalAnim); } // ********************************************************************************************************* void CEditor::showDecal(const NLMISC::CVector2f &pos, float scale, CDecal &decal, const CDecalAnim &decalAnim) { //H_AUTO(R2_CEditor_showDecal) CHECK_EDITOR float animRatio = decalAnim.DurationInMs == 0 ? 0.f : ((T1 - _DecalRefTime) % decalAnim.DurationInMs) / (float) decalAnim.DurationInMs; animRatio = 0.5f * cosf(2.f * (float) Pi * animRatio) + 0.5f; decalAnim.updateDecal(pos, animRatio, decal, scale); decal.addToRenderList(); } // ********************************************************************************************************* void CEditor::addSelectingDecal(const NLMISC::CVector &pos, float scale) { //H_AUTO(R2_CEditor_addSelectingDecal) CHECK_EDITOR CSelectingDecal *decal = NULL; // see if there's an unused decal in the list for(uint k = 0; k < _SelectingDecals.size(); ++k) { if (_SelectingDecals[k]->EndDate < T1) { decal = _SelectingDecals[k]; break; } } if (decal == NULL) { _SelectingDecals.push_back(new CSelectingDecal); decal = _SelectingDecals.back(); } decal->EndDate = T1 + _SelectingDecalAnim.DurationInMs; decal->Pos = pos; decal->Scale = scale; // updateDecalBlendRegion(decal->Decal, pos); _DecalRefTime = T1; } // ********************************************************************************************************* void CEditor::updateSelectingDecals() { //H_AUTO(R2_CEditor_updateSelectingDecals) CHECK_EDITOR if (_SelectingDecalAnim.DurationInMs == 0) return; for(uint k = 0; k < _SelectingDecals.size(); ++k) { CSelectingDecal *decal = _SelectingDecals[k]; if (decal) { if (decal->EndDate > T1) { float animRatio = (float) (T1 - (decal->EndDate - _SelectingDecalAnim.DurationInMs)) / (float) _SelectingDecalAnim.DurationInMs; _SelectingDecalAnim.updateDecal(decal->Pos, animRatio, decal->Decal, decal->Scale); decal->Decal.addToRenderList(); } } } } // ******************************************************************************************************* void CEditor::reset() { //H_AUTO(R2_CEditor_reset) if (ClientCfg.R2EDExtendedDebug) Driver->setWindowTitle(ucstring("Resetting R2ED editor ...")); CHECK_EDITOR _SerializeUIConfig = false; // prevent reloading of ui for speed _WantedActOnInit.clear(); if (getCurrentAct()) { R2::CObjectTable* act = getCurrentAct()->getObjectTable(); if (act->isString("Name")) { _WantedActOnInit = act->toString("Name"); } else { _WantedActOnInit = act->toString("Title"); //obsolete } } CVerboseClock clock("Reset of editor "); TMode oldMode = _Mode; TAccessMode oldAccessMode = _AccessMode; if (_Mode == EditionMode) { R2::getEditor().getLua().executeScriptNoThrow("r2.Version.save(\"save/r2_buffer.dat\")"); } release(); clearDebugWindow(); if (ReloadUIFlag) { loadLanguageFile(); reloadUI(); setForceDesktopReset(true); // next changes of desktop should force a reset, since ui has been reloaded ReloadUIFlag = false; } init(oldMode, oldAccessMode); if (oldMode == EditionMode) { CVerboseClock clock("Request reconnection "); getLua().executeScriptNoThrow("r2.requestReconnection()"); } // try to return to the act with the same title _SerializeUIConfig = true; if (ClientCfg.R2EDExtendedDebug) { Driver->setWindowTitle(CI18N::get("TheSagaOfRyzom")); // Show the window Driver->showWindow(); } getUI().displaySystemInfo(CI18N::get("uiR2EDEditorReseted"), "BC"); } // ********************************************************************************************************* void CEditor::reloadUI() { //H_AUTO(R2_CEditor_reloadUI) CHECK_EDITOR CVerboseClock clock("Reload ui"); // reload same list than at startup (given in client.cfg) for (uint k = 0; k < ClientCfg.XMLR2EDInterfaceFiles.size(); ++k) { reloadUI(ClientCfg.XMLR2EDInterfaceFiles[k].c_str()); } } // ********************************************************************************************************* void CEditor::reloadUI(const char *filename) { //H_AUTO(R2_CEditor_reloadUI) CHECK_EDITOR CVerboseClock clock("Loading ui " + std::string(filename)); CLuaStackChecker ls(&getLua()); std::string path = CPath::lookup(filename, false); if (path.empty()) { nlwarning("File %s not found, r2 ui not (re)loaded.", filename); return; } uint32 fileDate = CFile::getFileModificationDate(path); if (_LastUIModificationDate.count(filename) == 0) { _LastUIModificationDate[filename] = 0; } uint32 &lastUIModificationDate = _LastUIModificationDate[filename]; if (fileDate > lastUIModificationDate || !ClientCfg.R2EDDontReparseUnchangedUIFiles) // TMP (init not complete else ...) { // reload the ui files NLMISC::ICommand::execute(toString("loadui %s", filename).c_str(), g_log); lastUIModificationDate = fileDate; } else { if (!ClientCfg.R2EDDontReparseUnchangedUIFiles) { nlwarning("ui file %s date unchanged, not reloading.", filename); } } } // ********************************************************************************************************* void CEditor::connectAsCreator() { //H_AUTO(R2_CEditor_connectAsCreator) CHECK_EDITOR nlassert(_DMC); { CLuaStackChecker lsc2(&getLua()); _DMC->testConnectionAsCreator(); } } // string cached into lua for fast comparison static CLuaString lstr_isNil("isNil"); static CLuaString lstr_Parent("Parent"); static CLuaString lstr_ParentInstance("ParentInstance"); static CLuaString lstr_IndexInParent("IndexInParent"); static CLuaString lstr_User("User"); static CLuaString lstr_Size("Size"); static CLuaString lstr_DisplayerUI("DisplayerUI"); static CLuaString lstr_DisplayerVisual("DisplayerVisual"); static CLuaString lstr_DisplayerProperties("DisplayerProperties"); // ********************************************************************************************************* void CEditor::initObjectProjectionMetatable() { //H_AUTO(R2_CEditor_initObjectProjectionMetatable) CHECK_EDITOR _ObjectProjectionMetatable = getEnv().newTable("_instance_projection_metatable"); // projection functions struct CProjector { static bool checkTag(CLuaState &ls) { CLuaStackChecker lsc(&ls); ls.getMetaTable(1); ls.push("tag"); ls.getTable(-2); bool ok = false; if (ls.isString(-1)) { if (strcmp(ls.toString(-1), "CObjectTable") == 0) { ok = true; } } ls.pop(2); if (!ok) { nlwarning("metatable error : Editor object expected"); } return ok; } static int index(CLuaState &ls) { nlassert(ls.getTop() == 2); #ifdef NL_DEBUG if (!checkTag(ls)) return false; #endif CObjectTable::TRefPtrConst &obj= *(CObjectTable::TRefPtrConst *) ls.toUserData(1); if (OPERATOR_EQUAL(obj, NULL)) //NLMISC::operator==(obj, NULL)) { std::string index = ls.toString(2); // special members if ( index == "isNil") //if (std::operator==(index "isNil") { ls.push(true); return 1; } CLuaIHM::dumpCallStack(); // object has been deleted but the script maintains a reference on it throw ELuaWrappedFunctionException(&ls, "Attempt to access an erased object"); } if (ls.isNumber(2)) { // index is a number const CObject *other = obj->getValue((uint32) ls.toNumber(2)); if (other) { pushValue(ls, other); return 1; } else { // 'bad index' message already printed by CObject::getValue CLuaIHM::dumpCallStack(); } } if (!ls.isString(2)) { nlwarning("String expected when accessing an object property, %s found instead", ls.getTypename(2)); CLuaIHM::dumpCallStack(0); return 0; } const char *strId = ls.toString(2); // strings are shared in lua, so a pointer comparison is sufficient // special members // NB nico : this was added before the R2::CInstance class derived from CReflectable, // -> see if some of the following properties may better fit into R2::CInstance exported properties if (OPERATOR_EQUAL( lstr_isNil, strId)) { ls.push(false); return 1; } else if (OPERATOR_EQUAL(strId, lstr_Parent)) { if (!obj->getParent()) { ls.pushNil(); return 1; } nlassert(obj->getParent()->isTable()); getEditor().projectInLua((CObjectTable *) obj->getParent()); return 1; } else if (OPERATOR_EQUAL(strId, lstr_ParentInstance)) { // look for parent instance (that is, not the CObject *parent = obj->getParent(); while (parent) { if (parent->findAttr("InstanceId")) { getEditor().projectInLua((CObjectTable *) parent); return 1; } parent = parent->getParent(); } ls.pushNil(); return 1; } if (OPERATOR_EQUAL(strId, lstr_IndexInParent)) { if (!obj->getParent()) { ls.pushNil(); return 1; } sint32 index = obj->getParent()->findIndex(obj); if (obj->getParent()->getKey(index).empty()) { ls.push((double) index); } else { ls.push((double) -1); } return 1; } else if (OPERATOR_EQUAL(strId, lstr_User)) { // push user table on stack CEditor::getLuaUserTableFromObject(ls, *obj); return 1; } else if (OPERATOR_EQUAL(strId, lstr_Size)) { if (obj->isTable()) { ls.push((double) obj->getSize()); } else { ls.pushNil(); } return 1; } else if (OPERATOR_EQUAL(strId, lstr_DisplayerUI)) { CInstance *instance = getEditor().getInstanceFromObject(obj); if (!instance) { ls.pushNil(); return 1; } else { if (instance->getDisplayerUI()) { instance->getDisplayerUI()->pushLuaAccess(ls); } else { ls.pushNil(); } return 1; } } else if (OPERATOR_EQUAL(strId, lstr_DisplayerVisual)) { CInstance *instance = getEditor().getInstanceFromObject(obj); if (!instance) { ls.pushNil(); return 1; } else { if (instance->getDisplayerVisual()) { instance->getDisplayerVisual()->pushLuaAccess(ls); } else { ls.pushNil(); } return 1; } } else if (OPERATOR_EQUAL(strId, lstr_DisplayerProperties)) { CInstance *instance = getEditor().getInstanceFromObject(obj); if (!instance) { ls.pushNil(); return 1; } else { if (instance->getDisplayerProperties()) { instance->getDisplayerProperties()->pushLuaAccess(ls); } else { ls.pushNil(); } return 1; } } // else tries with a string std::string index = ls.toString(2); const CObject *other = getObject(obj, index); if (other) { pushValue(ls, other); return 1; } // this is not an attribute of the class // -> maybe this is a method ? // look in the parent class is there is a function with the wanted name // NOTE : calling a method on an editor object in the script can be done by doing // r2:getClass(instance).methodName(instance, parameters ...) // with this trick, one can do instance:methodName(parameters ...) CObject *className = obj->findAttr("Class"); if (className) { CLuaObject method = getEditor().getClasses()[className->toString()][index]; if (method.isFunction()) { method.push(); return 1; } } // Try with a property exported from CInstance // Is is ok to test thos properties after those defined in lua classes definition // because the init check that no "native" property is declared by the user CInstance *instance = getEditor().getInstanceFromObject(obj); if (instance) { const CReflectedProperty *prop = instance->getReflectedProperty(index, false); if (prop) { CLuaIHM::luaValueFromReflectedProperty(ls, *instance, *prop); return 1; } } // unknown attribute ls.pushNil(); return 1; } static int newIndex(CLuaState &ls) { nlassert(ls.getTop() == 3); #ifdef NL_DEBUG if (!checkTag(ls)) return false; #endif CObjectTable::TRefPtrConst &obj= *(CObjectTable::TRefPtrConst *) ls.toUserData(1); if (obj == NULL) { throw ELuaWrappedFunctionException(&ls, "Trying to set a property in an erased object, returning nil"); } #ifdef NL_DEBUG if (!checkTag(ls)) return false; #endif // try with a native (local ..) property exported from CInstance CInstance *instance = getEditor().getInstanceFromObject(obj); if (instance) { const CReflectedProperty *prop = instance->getReflectedProperty(ls.toString(2)); if (prop) { CLuaIHM::luaValueToReflectedProperty(ls, 3, *instance, *prop); return 1; } } // other instances properties are read only !! throw ELuaWrappedFunctionException(&ls, "Property %s of editor object is read-only. You must use 'r2.requestSetNode' function to change it", ls.toString(2)); } static int gc(CLuaState &ls) { #ifdef NL_DEBUG if (!checkTag(ls)) return false; #endif // TODO : maybe not useful ... CObjectTable::TRefPtrConst &obj= *(CObjectTable::TRefPtrConst *) ls.toUserData(1); typedef CObjectTable::TRefPtrConst TRefPtrConst; obj.~TRefPtrConst(); return 0; } // tool function used by 'next' // TODO : put this code in a better place ... static void pushValue(CLuaState &ls, const CObject *obj) { if (!obj) { ls.pushNil(); return; } if (obj->isString()) { ls.push(obj->toString()); } else if (obj->isNumber()) { ls.push(obj->toNumber()); } else if (obj->isTable()) { getEditor().projectInLua((CObjectTable *) obj); } else { nlassert(0); // type not supported ... } } // helper function used by 'next' : push a key static void pushKey(CLuaState &ls, const CObject *obj, sint32 index) { if (!obj) { ls.pushNil(); return; } if (obj->getKey(index).empty()) { ls.push((double) index); } else { ls.push(obj->getKey(index)); } } // special : we redefine the 'next' global function found in the standard // lua library in order to allow fields traversal for the projected object. // This is done in lua in the 'r2_core.lua' script // The new 'next' function will look into the object metatable // If a __next function is found, then it will be called for traversal // otherwise, the standard 'next' function is called instead. // This allow to have a traversal for objects that are not tables static int next(CLuaState &ls) { if (ls.getTop() != 2) { CLuaIHM::fails(ls, "__next metamethod require 2 arguments (table & key)"); } #ifdef NL_DEBUG if (!checkTag(ls)) return false; #endif CObjectTable::TRefPtrConst &obj= *(CObjectTable::TRefPtrConst *) ls.toUserData(1); if (obj == NULL) { throw ELuaWrappedFunctionException(&ls, "editor object '__next' metatmethod : Attempt to access an erased object"); } if (ls.isNil(2)) { // key is nil -> start of traversal if(obj->getSize() == 0) { ls.remove(-2); ls.pushNil(); return 2; // let (nil, nil) on the stack } else { // look for duplicated keys (ignoring empty keys, because in this case th index is the key) std::set keys; for(uint k = 0; k < obj->getSize(); ++k) { std::string key = obj->getKey((sint32) k); if (!key.empty()) { if (keys.count(key)) { nlwarning("Duplicated key of type string found while attempting to enumerate an instance content."); nlwarning("key is %s", key.c_str()); CLuaIHM::dumpCallStack(1); CLuaIHM::fails(ls, "Aborting to avoid infinite loop."); } keys.insert(key); } } ls.pop(2); pushKey(ls, obj, 0); pushValue(ls, obj->getValue(0)); return 2; } } else { // continuation of traversal // -> retrieve index from the key sint32 index; if (ls.isNumber(2)) { index = (uint32) ls.toNumber(2); } else { if (!ls.isString(2)) { nlwarning("__next metamethod : string expected"); ls.pop(); ls.pushNil(); return 2; } else { index = obj->findIndex(ls.toString(2)); } } if (index == -1) { nlwarning("__next metamethod : key not found"); ls.pop(2); ls.pushNil(); ls.pushNil(); return 2; } // retrieve next key sint32 newIndex = (uint32) (index + 1); if (newIndex == (sint32) obj->getSize()) { // this was the last element, returns nil ls.pop(2); ls.pushNil(); ls.pushNil(); return 2; } else { ls.pop(2); pushKey(ls, obj, newIndex); pushValue(ls, obj->getValue(newIndex)); return 2; } } return 0; } static int equal(CLuaState &ls) { // TMP TMP TMP static volatile bool from = false; if (from) { CLuaIHM::dumpCallStack(0); } nlassert(ls.getTop() == 2); if (!checkTag(ls)) return false; CObjectTable::TRefPtrConst &lhs= *(CObjectTable::TRefPtrConst *) ls.toUserData(1); CObjectTable::TRefPtrConst &rhs= *(CObjectTable::TRefPtrConst *) ls.toUserData(2); ls.push(OPERATOR_EQUAL(lhs, rhs)); return 1; } }; // affect functions to the metatable getLua().push(CProjector::index); _ObjectProjectionMetatable.setValue("__index", CLuaObject(getLua())); getLua().push(CProjector::newIndex); _ObjectProjectionMetatable.setValue("__newindex", CLuaObject(getLua())); getLua().push(CProjector::gc); _ObjectProjectionMetatable.setValue("__gc", CLuaObject(getLua())); getLua().push(CProjector::next); _ObjectProjectionMetatable.setValue("__next", CLuaObject(getLua())); getLua().push(CProjector::equal); _ObjectProjectionMetatable.setValue("__eq", CLuaObject(getLua())); // tag to mark that the user data is a CObjectTable ref ptr _ObjectProjectionMetatable.setValue("tag", std::string("CObjectTable")); } // ********************************************************************************************************* void CEditor::getLuaUserTableFromObject(CLuaState &ls, const CObjectTable &table) { //H_AUTO(R2_CEditor_getLuaUserTableFromObject) CHECK_EDITOR CLuaStackChecker lsc(&ls, 1); // read write environement accessible from lua ls.pushLightUserData((void *) &table); ls.getTable(LUA_REGISTRYINDEX); if (ls.isNil()) { ls.pop(); // no user table created yet ?... create it ls.pushLightUserData((void *) &table); ls.newTable(); ls.pushValue(-1); // saves the table ls.insert(-3); ls.setTable(LUA_REGISTRYINDEX); // table remains on the stack } } // ********************************************************************************************************* void CEditor::setCurrentAct(CInstance *act) { //H_AUTO(R2_CEditor_setCurrentAct) CHECK_EDITOR if (!_ScenarioInstance) return; if (_ClearingContent) return; bool currentActSelected = _SelectedInstance && (_SelectedInstance == _CurrentAct); CInstance* previousAct = _CurrentAct; _CurrentAct = act ? act : getBaseAct(); // struct CPreActChangedVisitor : public IInstanceVisitor { virtual void visit(CInstance &inst) { inst.onPreActChanged(); } }; CPreActChangedVisitor preActChangedVisitor; _ScenarioInstance->visit(preActChangedVisitor); struct CActChangedVisitor : public IInstanceVisitor { virtual void visit(CInstance &inst) { inst.onActChanged(); } }; CActChangedVisitor actChangedVisitor; _ScenarioInstance->visit(actChangedVisitor); //TP if necesary // warn lua that current act has changed if (!previousAct) { getLua().pushNil(); } else { previousAct->getLuaProjection().push(); } if (!_CurrentAct) { getLua().pushNil(); } else { _CurrentAct->getLuaProjection().push(); } callEnvMethod("onActChanged", 2, 0); setSelectedInstance(currentActSelected ? _CurrentAct : NULL); setCurrentTool(NULL); } // ********************************************************************************************************* void CEditor::setCurrentActFromTitle(const std::string &wantedTitle) { //H_AUTO(R2_CEditor_setCurrentActFromTitle) CHECK_EDITOR if (!_Scenario) { nlwarning("Scenario initialisation failed, can't set current act"); return; } CObject *actTable = _Scenario->getAttr("Acts"); if (actTable->isTable()) { for(uint k = 0; k < actTable->getSize(); ++k) { R2::CObject *act = actTable->getValue(k); nlassert(act); std::string actTitle; if (act->isString("Name")) { actTitle = act->toString("Name"); } else { actTitle = act->toString("Title"); //obsolete } if (actTitle == wantedTitle) { setCurrentAct(getInstanceFromObject(act)); break; } } } else { nlwarning("'Acts' field in scenario should be a table"); } } // ********************************************************************************************************* void CEditor::projectInLua(const CObjectTable *table) { //H_AUTO(R2_CEditor_projectInLua) CHECK_EDITOR nlassert(table); const CObjectTableClient *otc = NLMISC::safe_cast(table); otc->pushOnLuaStack(getLua(), _ObjectProjectionMetatable); } // ********************************************************************************************************* void CEditor::backupRequestCommands() { //H_AUTO(R2_CEditor_backupRequestCommands) nlassert(!_OldLuaRequestInsertNode.isValid()); // restoreRequestCommands not called ? nlassert(!_OldLuaRequestInsertGhostNode.isValid()); nlassert(!_OldLuaRequestSetNode.isValid()); nlassert(!_OldLuaRequestEraseNode.isValid()); nlassert(!_OldLuaRequestMoveNode.isValid()); _OldLuaRequestInsertNode = getEnv()["requestInsertNode"]; _OldLuaRequestInsertGhostNode = getEnv()["requestInsertGhostNode"]; _OldLuaRequestSetNode = getEnv()["requestSetNode"]; _OldLuaRequestEraseNode = getEnv()["requestEraseNode"]; _OldLuaRequestMoveNode = getEnv()["requestMoveNode"]; struct CIgnoreRequestCall { static int requestSomething(CLuaState &) { nlwarning("PAS BIEN C MAL !!!"); /* CLuaIHM::debugInfo("Can't call a 'r2.request' command while object are erased in displayers, or are just being created (in this case, use onPostCreate instead) !"); CLuaIHM::debugInfo("Callstack is :"); CLuaIHM::dumpCallStack(1);*/ return 0; } }; // While we delete the object tree, displayers will be triggered // Make sure that they don't call any r2.requestSomething commands, end if so, // print an error message getEnv().setValue("requestInsertNode", CIgnoreRequestCall::requestSomething); getEnv().setValue("requestInsertGhostNode", CIgnoreRequestCall::requestSomething); getEnv().setValue("requestSetNode", CIgnoreRequestCall::requestSomething); getEnv().setValue("requestEraseNode", CIgnoreRequestCall::requestSomething); getEnv().setValue("requestMoveNode", CIgnoreRequestCall::requestSomething); } // ********************************************************************************************************* void CEditor::restoreRequestCommands() { //H_AUTO(R2_CEditor_restoreRequestCommands) nlassert(_OldLuaRequestInsertNode.isValid()); // backupRequestCommands not called ? nlassert(_OldLuaRequestInsertGhostNode.isValid()); nlassert(_OldLuaRequestSetNode.isValid()); nlassert(_OldLuaRequestEraseNode.isValid()); nlassert(_OldLuaRequestMoveNode.isValid()); // restore old functions getEnv().setValue("requestInsertNode", _OldLuaRequestInsertNode); getEnv().setValue("requestInsertGhostNode", _OldLuaRequestInsertGhostNode); getEnv().setValue("requestSetNode", _OldLuaRequestSetNode); getEnv().setValue("requestEraseNode", _OldLuaRequestEraseNode); getEnv().setValue("requestMoveNode", _OldLuaRequestMoveNode); // _OldLuaRequestInsertNode.release(); _OldLuaRequestInsertGhostNode.release(); _OldLuaRequestSetNode.release(); _OldLuaRequestEraseNode.release(); _OldLuaRequestMoveNode.release(); } // ********************************************************************************************************* void CEditor::clearContent() { nlwarning("*R2* clear content"); setMaxVisibleEntityExceededFlag(false); //H_AUTO(R2_CEditor_clearContent) if (_Mode != EditionMode && _Mode != AnimationModeLoading) return; _ClearingContent = true; CHECK_EDITOR setSelectedInstance(NULL); setFocusedInstance(NULL); // backup all "requestCommand" if (_CurrentTool) _CurrentTool->cancel(); CTool::releaseMouse(); delete _NewScenario; // will be not NULL only if quit is called during the scenario connection screen _NewScenario = NULL; eraseScenario(); _InstancesByDispName.clear(); _InstancesByDispName.resize(_ClassNameToIndex.size()); _InstanceObservers.clear(); _InstanceObserverHandles.clear(); _Cookies.clear(); _LocalGeneratedNames.clear(); _Instances.clear(); _BaseAct = NULL; _CurrentAct = NULL; _SelectedInstance = NULL; _FocusedInstance = NULL; _ScenarioInstance = NULL; _Scenario = NULL; _SelectingDecals.clear(); _InstanceObservers.clear(); _InstanceObserverHandles.clear(); _LastInstanceUnderPos = NULL; _CurrentTool = NULL; removeAllEntitySlots(); // for safety... _DMC->CDynamicMapClient::scenarioUpdated(NULL, false, 1); // clear scenario for real _ClearingContent = false; _PlotItemInfos.clear(); PeopleInterraction.removeAllFreeTellers(); } // ********************************************************************************************************* void CEditor::setPlotItemInfos(const TMissionItem &infos) { //H_AUTO(R2_CEditor_setPlotItemInfos) _PlotItemInfos[infos.SheetId.asInt()] = infos; } // ********************************************************************************************************* const TMissionItem *CEditor::getPlotItemInfos(uint32 sheetId) const { //H_AUTO(R2_CEditor_getPlotItemInfos) std::map::const_iterator it = _PlotItemInfos.find(sheetId); if (it == _PlotItemInfos.end()) return NULL; return &it->second; } // ********************************************************************************************************* void CEditor::release() { nlwarning("*R2* release"); //H_AUTO(R2_CEditor_release) GameContextMenu.init(""); setFixedLighting(false); CHECK_EDITOR if (_Mode == NotInitialized) { // nothing more to do there return; } // warn lua of final release callEnvMethod("onFinalRelease", 0, 0); if (_SerializeUIConfig) { // serialization disabled when resetting the editor (for speed) saveCurrentKeySet(); saveUIConfig(); } getUI().hideAllWindows(); // make sure all action handlers are called while the r2 lua environment is still active clearContent(); _EntityCustomSelectBoxMap.clear(); // must do this after clearContent, which sets the default tool if (_CurrentTool) { _CurrentTool->cancel(); _CurrentTool = NULL; } // clear the environment if (getUI().getLuaState()) { getLua().push(R2_LUA_PATH); getLua().pushNil(); getLua().setTable(LUA_GLOBALSINDEX); _Globals.release(); _Registry.release(); _ObjectProjectionMetatable.release(); // AJM _Env.release(); _Config.release(); CEditorCheck::EditorCreated = false; // force a garbage collection to free the mem for real getLua().setGCThreshold(0); } // stop at login if (_DMC) { _DMC->release(); } // nlassert(CDisplayerBase::ObjCount == 0); _Initialized = false; CEditorCheck::EditorCreated = false; // AJM _Mode = NotInitialized; _AccessMode = AccessModeUnknown; ::IgnoreEntityDbUpdates = false; // _IslandCollision.release(); // _LastUIModificationDate.clear(); _InstancesByDispName.clear(); // delete _EntitySorter; _EntitySorter = NULL; } // ********************************************************************************************************* void CEditor::removeAllEntitySlots() { //H_AUTO(R2_CEditor_removeAllEntitySlots) CHECK_EDITOR for (uint k = 1; k < 255; ++k) { if (EntitiesMngr.entity(k) != NULL) { EntitiesMngr.remove(k, false); } } } // ********************************************************************************************************* void CEditor::registerDisplayers() { //H_AUTO(R2_CEditor_registerDisplayers) CHECK_EDITOR static bool registered = false; if (registered) return; NLMISC_REGISTER_CLASS(R2::CDisplayerVisualActivitySequence); NLMISC_REGISTER_CLASS(R2::CDisplayerVisualEntity); NLMISC_REGISTER_CLASS(R2::CDisplayerVisualGroup) NLMISC_REGISTER_CLASS(R2::CDisplayerVisualShape) NLMISC_REGISTER_CLASS(R2::CDisplayerLua) registered = true; } // ********************************************************************************************************* void CEditor::registerTools() { //H_AUTO(R2_CEditor_registerTools) CHECK_EDITOR static bool registered = false; if (registered) return; NLMISC_REGISTER_CLASS(R2::CToolDrawPrim); NLMISC_REGISTER_CLASS(R2::CToolSelectMove); NLMISC_REGISTER_CLASS(R2::CToolSelectRotate); NLMISC_REGISTER_CLASS(R2::CToolNewVertex); registered = true; } // ********************************************************************************************************* void CEditor::setSelectedInstance(CInstance *inst) { //H_AUTO(R2_CEditor_setSelectedInstance) CHECK_EDITOR if (inst == _SelectedInstance) return; forceSetSelectedInstance(inst); } // ********************************************************************************************************* void CEditor::forceSetSelectedInstance(CInstance *inst) { //H_AUTO(R2_CEditor_forceSetSelectedInstance) CHECK_EDITOR if (_EnteredInSetSelectedInstance) return; // prevent recursive call (may happen because selection highlight an item in a menu, which trigger selection in turn) if (inst) { nlassert(inst->getSelectableFromRoot()); } _EnteredInSetSelectedInstance = true; if (inst) { nlassert(hasInstance(inst)); // must have been added in the editor ! } if (_SelectedInstance) { _SelectedInstance->onSelect(false); } _SelectedInstance = inst; if (_SelectedInstance) { _SelectedInstance->onSelect(true); } // call r2 method 'onSelectInstance' if (!_SelectedInstance) { getLua().pushNil(); } else { _SelectedInstance->getLuaProjection().push(); } callEnvMethod("onSelectInstance", 1, 0); _EnteredInSetSelectedInstance = false; } // ********************************************************************************************************* CInstance *CEditor::getSelectedInstance() const { //H_AUTO(R2_CEditor_getSelectedInstance) CHECK_EDITOR return _SelectedInstance; } // ********************************************************************************************************* void CEditor::setFocusedInstance(CInstance *inst) { //H_AUTO(R2_CEditor_setFocusedInstance) CHECK_EDITOR if (inst == _FocusedInstance) return; if (inst) { nlassert(hasInstance(inst)); // must have been added in the editor ! } if (_FocusedInstance) { _FocusedInstance->onFocus(false); } _FocusedInstance = inst; if (_FocusedInstance) { _FocusedInstance->onFocus(true); } } // ********************************************************************************************************* CInstance *CEditor::getFocusedInstance() const { //H_AUTO(R2_CEditor_getFocusedInstance) CHECK_EDITOR return _FocusedInstance; } // ********************************************************************************************************* bool CEditor::hasInstance(const CInstance *instance) const { //H_AUTO(R2_CEditor_hasInstance) CHECK_EDITOR // TMP : slow test for debug for(TInstanceMap::const_iterator it = _Instances.begin(); it != _Instances.end(); ++it) { if (it->second == instance) return true; } return false; } // ********************************************************************************************************* bool CEditor::hasInstanceWithId(const TInstanceId &id) const { //H_AUTO(R2_CEditor_hasInstanceWithId) CHECK_EDITOR return getInstanceFromId(id) != NULL; } // ********************************************************************************************************* CInstance *CEditor::getInstanceFromId(const TInstanceId &id) const { //H_AUTO(R2_CEditor_getInstanceFromId) CHECK_EDITOR const CObjectTable *objTable = getObjectTableFromId(id); if (!objTable) return NULL; return getInstanceFromObject(objTable); } // ********************************************************************************************************* sint CEditor::getGeneratedNameIndex(const std::string &nameUtf8, const std::string &baseNameUtf8) { //H_AUTO(R2_CEditor_getGeneratedNameIndex) CHECK_EDITOR if (nameUtf8.size() >= baseNameUtf8.size() + 2) { if (nameUtf8.substr(0, baseNameUtf8.size()) == baseNameUtf8) { std::string::const_iterator strIt = nameUtf8.begin() + baseNameUtf8.size(); std::string::const_iterator endStrIt = nameUtf8.end(); if (*strIt == ' ') { ++ strIt; const char *numberStart = &*strIt; for (; strIt != endStrIt && isdigit(*strIt); ++strIt) {} if (strIt == endStrIt) { sint ret; fromString(numberStart, ret); return ret; } } } } return -1; } // ********************************************************************************************************* bool CEditor::isPostFixedByNumber(const ucstring &baseName) { //H_AUTO(R2_CEditor_isPostFixedByNumber) // strip number & spaces at the end of the name sint lastIndex = (sint)baseName.length() - 1; while (lastIndex > 0) { int currChar = (int) baseName[lastIndex]; if (!isdigit(currChar) && currChar != ' ' && currChar != '\t') { break; } -- lastIndex; } return lastIndex != (sint) baseName.length() - 1; } // ********************************************************************************************************* ucstring CEditor::genInstanceName(const ucstring &baseName) { //H_AUTO(R2_CEditor_genInstanceName) CHECK_EDITOR uint maxIndex = 0; // strip number & spaces at the end of the name ucstring strippedName = baseName; sint lastIndex = (sint)strippedName.length() - 1; while (lastIndex > 0) { int currChar = (int) strippedName[lastIndex]; if (!isdigit(currChar) && currChar != ' ' && currChar != '\t') { break; } -- lastIndex; } strippedName = strippedName.substr(0, lastIndex + 1); std::string baseNameUtf8 = strippedName.toUtf8(); // for(TInstanceMap::const_iterator it = _Instances.begin(); it != _Instances.end(); ++it) { maxIndex = (uint) std::max((sint) maxIndex, getGeneratedNameIndex(it->second->getName(), baseNameUtf8)); } nlinfo("CEditor::genInstanceName newVersion"); // Old part : used when there was a delay between net command and effect // did not work in some parts if genInstance is not followed by creation of an instance with that name // cf RT 11560 /* // now, look in local generated names (instance that have not been already added to the scene) TGeneratedNameMap::iterator nameSetIt = _LocalGeneratedNames.find(baseNameUtf8); if (nameSetIt != _LocalGeneratedNames.end()) { if (!nameSetIt->second.empty()) { maxIndex = std::max(maxIndex, *(nameSetIt->second.rbegin())); // take bigger element of the set (the last element) } } // add max index in the map (will be removed when object is truly created) _LocalGeneratedNames[baseNameUtf8].insert(maxIndex + 1); */ return strippedName + " " + toString(maxIndex + 1); } // ********************************************************************************************************* void CEditor::setCookie(const TInstanceId &instanceId, const std::string &key, CLuaObject &value) { //H_AUTO(R2_CEditor_setCookie) CHECK_EDITOR TCookieList &cl = _Cookies[instanceId]; cl.push_front(CCookie()); CCookie &cookie = cl.front(); // don't build struct and push to avoid costly copy of a CLuaObject cookie.Key = key; cookie.Value = value; } // ********************************************************************************************************* void CEditor::setCookie(const TInstanceId &instanceId, const std::string &key, bool value) { //H_AUTO(R2_CEditor_setCookie) CHECK_EDITOR getLua().push(value); CLuaObject obj(getLua()); setCookie(instanceId, key, obj); } // ********************************************************************************************************* void CEditor::setCurrentTool(CTool *tool) { //H_AUTO(R2_CEditor_setCurrentTool) CHECK_EDITOR if (_CurrentTool) { _CurrentTool->cancel(); } else { if (tool) { // setting a tool in non R2 mode force to change the contextual cursor ContextCur.release(); ContextCur.add(false, "STAND BY", DEFAULT_CURSOR, 0.0f, CEditor::checkCursor, CEditor::mouseClick); ContextCur.context("STAND BY"); } } CTool::setContextHelp(ucstring("")); if (tool == NULL) { if (_Mode == EditionMode) { _CurrentTool = new CToolSelectMove; } else { _CurrentTool = NULL; ContextCur.release(); ::initContextualCursor(); ContextCur.context("STAND BY"); CTool::setMouseCursor(DEFAULT_CURSOR); } } else { _CurrentTool = tool; } if (_CurrentTool) { _CurrentTool->onActivate(); } { CLuaStackChecker lsc(&getLua()); // activate tool in the ui getLua().push(_CurrentTool ? _CurrentTool->getToolUIName() : ""); getEnv()["ToolUI"].callMethodByNameNoThrow("setActiveToolUIByName", 1, 0); } } // ********************************************************************************************************* CLuaObject CEditor::getClasses() throw(ELuaError) { //H_AUTO(R2_getClasses_throw) CHECK_EDITOR return getEnv().at("Classes"); } // ********************************************************************************************************* bool CEditor::callEnvFunc(const char *funcName, int numArgs, int numRet /*=0*/) { //H_AUTO(R2_CEditor_callEnvFunc) CHECK_EDITOR static volatile bool dumpStackWanted = false; if (dumpStackWanted) getLua().dumpStack(); nlassert(funcName); nlassert(getLua().getTop() >= numArgs); int initialStackSize = getLua().getTop(); getEnv().push(); if (dumpStackWanted) getLua().dumpStack(); getLua().insert(-1 - numArgs); // put the table before the args if (dumpStackWanted) getLua().dumpStack(); int result = getLua().pcallByName(funcName, numArgs, numRet, -1 - numArgs); if (dumpStackWanted) getLua().dumpStack(); if (result != 0) { nlwarning("Error while calling function %s : %s", funcName, getLua().toString()); getLua().setTop(initialStackSize - numArgs + numRet); // clean the stack return false; } // remove the R2 table from the stack int newSize = getLua().getTop(); if (dumpStackWanted) getLua().dumpStack(); nlassert(newSize == initialStackSize + 1 - numArgs + numRet); // -1 is because of the r2 table if (dumpStackWanted) getLua().dumpStack(); getLua().remove(- numRet - 1); // remove the 'R2' table if (dumpStackWanted) getLua().dumpStack(); return true; // results remains on the stack } // ********************************************************************************************************* bool CEditor::callEnvMethod(const char *funcName, int numArgs, int numRet /*= 0*/) { //H_AUTO(R2_CEditor_callEnvMethod) CHECK_EDITOR nlassert(funcName); nlassert(getLua().getTop() >= numArgs); getEnv().push(); getLua().insert(-1 - numArgs); // put the table before the args (self parameter) return callEnvFunc(funcName, numArgs + 1, numRet); } // ********************************************************************************************************* bool CEditor::doLuaScript(const char *filename, const char *fileDescText) { //H_AUTO(R2_CEditor_doLuaScript) CHECK_EDITOR CVerboseClock clock("parsing of " + std::string(filename)); // load the classes definition file std::string filePath = NLMISC::CPath::lookup(filename, false, true); if (filePath.empty()) { nlwarning("Can't find %s : %s", fileDescText, filename); return false; } if( 0 && FINAL_VERSION == 1) // deactivated for the moment because there are lua file that must be loaded from example { const static std::string path = "data_common.bnp@"; const static std::string::size_type len= path.size(); if (filePath.size() < len || filePath.substr(0, len) != path) { nlwarning("Can't find %s : %s in ('%s')", fileDescText, filename, path.c_str()); return false; } } try { if (!getLua().executeFile(filePath)) { nlwarning("Couldn't open file %s : %s for R2 is not loaded", filename, fileDescText); } CLuaStackChecker ls(&getLua()); return true; } catch(const NLMISC::EStream &e) { nlwarning("Error while loading R2 %s (file = %s) : %s", fileDescText, filename, e.what()); } catch(const ELuaError &e) { //char filename[MAX_PATH]; std::string msg = e.what(); if (testWildCard(msg, "*.lua:*:*")) // TODO nico : more accurate testing for filename format { std::string::size_type extPos = msg.find(".lua"); nlassert(extPos != std::string::npos); std::string filename = msg.substr(0, extPos + 4); // extract filename including extension int line; fromString(&*(msg.begin() + extPos + 5), line); // line number follows nlwarning((CLuaIHM::createGotoFileButtonTag(filename.c_str(), line) + e.what()).c_str()); } else { // no filename / line in the result, so maybe the parse error was in an 'inline' script nlwarning("Error in R2 %s (file = %s) : %s", fileDescText, filename, e.what()); } } return false; } /////////////////////// // CONTEXTUAL CURSOR // /////////////////////// // ********************************************************************************************************* void CEditor::checkCursor() { //H_AUTO(R2_CEditor_checkCursor) CHECK_EDITOR // no-op there : we delegate management of mouse to the CTool derived classes } // ********************************************************************************************************* void CEditor::mouseClick(bool rightButton, bool /* dblClick */) { //H_AUTO(R2_CEditor_mouseClick) CHECK_EDITOR CTool *currentTool = getEditor().getCurrentTool(); if (!currentTool) return; if (!rightButton) { // nb : result not handled there (no defaut action for left click) currentTool->onMouseLeftButtonClicked(); } else { bool handled = currentTool->onMouseRightButtonClicked(); if (!handled) { getEditor().displayContextMenu(); } } } // ********************************************************************************************************* void CEditor::updatePreCamera() { //H_AUTO(R2_CEditor_updatePreCamera) if (_Mode == EditionMode) { static uint32 loop = 0; ++loop; if (loop % 200 == 0) // minimal wait between to save = 20 seconds { if ( (CTime::getLocalTime() -_LastAutoSaveTime)/1000 > ClientCfg.R2EDAutoSaveWait) // 5 minutes if not change in Confile { autoSave(); } loop = 0; } // if there's no pending action, then start a new one at each frame if (!_DMC->getActionHistoric().isPendingActionInProgress()) { _DMC->newAction(CI18N::get("uiR2EDUnamedAction")); } else { _DMC->getActionHistoric().flushPendingAction(); } } } // ********************************************************************************************************* void CEditor::updatePrimitiveContextualVisibility() { //H_AUTO(R2_CEditor_updatePrimitiveContextualVisibility) if (!_SelectedInstance) return; // if selected instance remains in last contextual prim list, then they may remain visible bool ok = false; for(uint k = 0; k < _LastContextualPrims.size(); ++k) { if (_LastContextualPrims[k] == _SelectedInstance || _SelectedInstance->isSonOf(_LastContextualPrims[k]) ) { ok = true; break; } } _LastContextualPrims.clear(); bool isLogicEntity = _SelectedInstance->isKindOf("LogicEntity"); if (!ok && !isLogicEntity) return; if (isLogicEntity) { _LastContextualLogicEntity = _SelectedInstance; } if (!_LastContextualLogicEntity) return; // CObject *seq = _LastContextualLogicEntity->getGroupSelectedSequence(); if (!seq) return; if (seq) { CObjectTable *activities = seq->toTable("Components"); if (activities) { // get first world object parent to get start position for(uint k = 0; k < activities->getSize(); ++k) { // search next zone of activity CObjectTable *activity = activities->getValue(k)->toTable(); if (!activity) continue; std::string zoneId = getString(activity, "ActivityZoneId"); CInstance *primitive = getInstanceFromId(zoneId); if (primitive) { CDisplayerVisualGroup *primDisp = dynamic_cast(primitive->getDisplayerVisual()); if (primDisp) { _LastContextualPrims.push_back(primitive); primDisp->setContextualVisibilityDate(T1); } } } } } } // ********************************************************************************************************* CEntitySorter *CEditor::getEntitySorter() const { return _EntitySorter; } // ********************************************************************************************************* void CEditor::updateBeforeRender() { //H_AUTO(R2_CEditor_updateBeforeRender) CHECK_EDITOR // _IslandCollision.updateCurrPackedIsland(); // update contextual visibility of primitive from current selection updatePrimitiveContextualVisibility(); // if (ConnectionWanted) return; // TMP special case for connection if (_CurrentTool) { _CurrentTool->updateBeforeRender(); } for(TInstanceMap::iterator it = _Instances.begin(); it != _Instances.end(); ++it) { if (it->second->getDisplayerVisual()) { it->second->getDisplayerVisual()->onPreRender(); } } updateSelectingDecals(); // hide or show user depending on the mode if (UserEntity) { if (_Mode == EditionMode || isDMing()) { updateDecalBlendRegion(_PionneerDecal, UserEntity->pos()); showDecal(CVector2f((float) UserEntity->pos().x, (float) UserEntity->pos().y), 1.f, _PionneerDecal, _PionneerDecalAnim); } } if ((_SelectedInstance && _SelectedInstance->maxVisibleEntityExceeded()) || (_FocusedInstance && _FocusedInstance->maxVisibleEntityExceeded())) { setMaxVisibleEntityExceededFlag(true); } else { setMaxVisibleEntityExceededFlag(false); } } // ********************************************************************************************************* void CEditor::updateDecalBlendRegion(CDecal &decal, const NLMISC::CVector &pos) { //H_AUTO(R2_CEditor_updateDecalBlendRegion) float topBlendDist = CV_DecalTopBlendStartDist.get(); float bottomBlendDist = CV_DecalBottomBlendStartDist.get(); float blendLength = CV_DecalBlendLength.get(); decal.setBottomBlend(pos.z - bottomBlendDist - blendLength, pos.z - bottomBlendDist); decal.setTopBlend(pos.z + topBlendDist, pos.z + topBlendDist + blendLength); } // ********************************************************************************************************* void CEditor::updateBeforeSwapBuffer() { //H_AUTO(R2_CEditor_updateBeforeSwapBuffer) CHECK_EDITOR if (_WaitScenarioScreenWanted) { waitScenario(); _WaitScenarioScreenWanted= false; } } // ********************************************************************************************************* void CEditor::waitScenario() { //H_AUTO(R2_CEditor_waitScenario) _EditionModeDisconnectedFlag = false; _WaitScenarioScreenActive = true; waitScenarioScreen(); _WaitScenarioScreenActive = false; if (_PostponeScenarioUpdated) { scenarioUpdated(_NewScenario, false, _NewScenarioInitialAct); _PostponeScenarioUpdated = false; _NewScenario = NULL; } } // ********************************************************************************************************* void CEditor::updateAfterRender() { if (_EntitySorter) _EntitySorter->clipEntitiesByDist(); //H_AUTO(R2_CEditor_updateAfterRender) _IslandCollision.updateCurrPackedIsland(); CHECK_EDITOR if (ConnectionWanted) { connect(); return; } // if (_CurrentTool) { _CurrentTool->updateAfterRender(); } if (!_LuaUIMainLoop.isNil()) { _LuaUIMainLoop.callMethodByNameNoThrow("onPostSceneRender", 0, 0); } // for(TInstanceMap::iterator it = _Instances.begin(); it != _Instances.end(); ++it) { if (it->second->getDisplayerVisual()) { it->second->getDisplayerVisual()->onPostRender(); } } } // ********************************************************************************************************* void CEditor::autoSave() { //H_AUTO(R2_CEditor_autoSave) _LastAutoSaveTime = NLMISC::CTime::getLocalTime(); uint32 maxAutoSave = ClientCfg.R2EDAutoSaveSlot; std::string lastFile = toString("autosave_%u", maxAutoSave); uint32 i = maxAutoSave -1; for ( ; i != 0 ; --i ) { std::string current = NLMISC::toString("autosave_%02u.r2", i); std::string next = NLMISC::toString("autosave_%02u.r2", i+1); if (CFile::fileExists(current)) { if (CFile::fileExists(next)) // true only for i = maxAutoSave -1 { CFile::deleteFile(next); } CFile::moveFile(next.c_str(), current.c_str()); } } if (CFile::fileExists("save/r2_buffer.dat")) { CFile::copyFile("autosave_01.r2", "save/r2_buffer.dat"); } R2::getEditor().getLua().executeScriptNoThrow("r2.Version.save(\"save/r2_buffer.dat\")"); } // ********************************************************************************************************* bool CEditor::handleEvent (const CEventDescriptor &eventDesc) { //H_AUTO(R2_CEditor_handleEvent ) CHECK_EDITOR if (ConnectionWanted || !_CurrentTool) return false; // TMP special case for connection if (eventDesc.getType() == CEventDescriptor::system) { const CEventDescriptorSystem &eds = (const CEventDescriptorSystem &) eventDesc; if (eds.getEventTypeExtended() == CEventDescriptorSystem::setfocus) { const CEventDescriptorSetFocus &edsf = (const CEventDescriptorSetFocus &) eds; if (edsf.hasFocus() == false) { // cancel current tool setCurrentTool(NULL); return true; } } } if (_CurrentTool) { return _CurrentTool->handleEvent(eventDesc); } return false; } // ********************************************************************************************************* void CEditor::copy() { //H_AUTO(R2_CEditor_copy) // forward to lua callEnvMethod("copy", 0); } // ********************************************************************************************************* void CEditor::paste() { //H_AUTO(R2_CEditor_paste) // forward to lua callEnvMethod("paste", 0); } // ********************************************************************************************************* /* void CEditor::updateEvents() { if (!_CurrentTool) return; // if(EventsListener.isMouseButtonPushed(leftButton)) { nlwarning("onMouseLeftButtonDown"); _CurrentTool->onMouseLeftButtonDown(); } if(EventsListener.isMouseButtonReleased(leftButton)) { nlwarning("isMouseButtonReleased"); _CurrentTool->onMouseLeftButtonUp(); } if(EventsListener.isMouseButtonPushed(rightButton)) { nlwarning("isMouseButtonPushed"); _CurrentTool->onMouseRightButtonDown(); } if(EventsListener.isMouseButtonReleased(rightButton)) { nlwarning("onMouseRightButtonUp"); _CurrentTool->onMouseRightButtonUp(); } }*/ // ********************************************************************************************************* CEntityCL *CEditor::createEntity(uint slot, const NLMISC::CSheetId &sheetId, const NLMISC::CVector &pos, float heading, const std::string & permanentStatutIcon) { //H_AUTO(R2_CEditor_createEntity) CHECK_EDITOR if (sheetId == NLMISC::CSheetId::Unknown) return NULL; CInterfaceManager *im = CInterfaceManager::getInstance(); if (EntitiesMngr.entity(slot)) { EntitiesMngr.remove(slot, false); } // Create the temporary entity in the entity manager TNewEntityInfo emptyEntityInfo; emptyEntityInfo.reset(); CEntityCL *entity = EntitiesMngr.create(slot, sheetId.asInt(), emptyEntityInfo); if (!entity) { nlwarning("Can't create entity"); return NULL; } // Set the permanent statut icon entity->setPermanentStatutIcon(permanentStatutIcon); // TMP TMP : code taken from /entity command sint64 *prop = 0; CCDBNodeLeaf *node = 0; // Set The property 'CLFECOMMON::PROPERTY_POSITION'. node = im->getDbProp("SERVER:Entities:E" + NLMISC::toString("%d", slot)+":P" + NLMISC::toString("%d", CLFECOMMON::PROPERTY_POSX), false); if(node) { sint64 x = (sint64)(pos.x*1000.0); sint64 y = (sint64)(pos.y*1000.0); sint64 z = (sint64)(pos.z*1000.0); node->setValue64(x); node = im->getDbProp("SERVER:Entities:E"+toString("%d", slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_POSY), false); if(node) { node->setValue64(y); node = im->getDbProp("SERVER:Entities:E"+toString("%d", slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_POSZ), false); if(node) node->setValue64(z); } } // Set The property 'PROPERTY_ORIENTATION'. node = im->getDbProp("SERVER:Entities:E"+toString("%d", slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_ORIENTATION), false); if(node) { union { uint64 heading64; float headingFloat; }; headingFloat = heading; node->setValue64(heading64); } // Set Mode node = im->getDbProp("SERVER:Entities:E"+toString("%d", slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_MODE), false); if(node) { MBEHAV::EMode m = MBEHAV::NORMAL; prop = (sint64 *)&m; node->setValue64(*prop); EntitiesMngr.updateVisualProperty(0, slot, CLFECOMMON::PROPERTY_MODE); } // Set Visual Properties SPropVisualA visualA; //visualA.PropertySubData.LTrail = 1; // Set alternate look SAltLookProp altLookProp; // fill infos for look prop = (sint64 *)&visualA; if(dynamic_cast(entity)) { // visual property A depends on the type of the entity visualA.PropertySubData.Sex = ClientCfg.Sex; } else if(dynamic_cast(entity) == NULL) { // Get the database entry. // Get the old value (not useful since we change the whole property). altLookProp.Summary = 0; altLookProp.Element.ColorTop = 0; altLookProp.Element.ColorBot = 2; altLookProp.Element.WeaponRightHand = 0; altLookProp.Element.WeaponLeftHand = 0; altLookProp.Element.Seed = 100; altLookProp.Element.ColorHair = 4; altLookProp.Element.Hat = 0; // old colors altLookProp.Element.ColorGlove = altLookProp.Element.ColorTop; altLookProp.Element.ColorArm = altLookProp.Element.ColorTop; altLookProp.Element.ColorBoot = altLookProp.Element.ColorBot; // fill alt infos for look prop = (sint64 *)&altLookProp.Summary; } // Set the database. im->getDbProp("SERVER:Entities:E"+toString("%d", slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->setValue64(*prop); // Set Visual Properties SPropVisualB visualB; visualB.PropertySubData.LTrail = 1; // fill infos for look sint64 *propB = 0; propB = (sint64 *)&visualB; // Set the database. im->getDbProp("SERVER:Entities:E"+toString("%d", slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPB))->setValue64(*propB); // Apply Changes. EntitiesMngr.updateVisualProperty(0, slot, CLFECOMMON::PROPERTY_VPA); EntitiesMngr.updateVisualProperty(0, slot, CLFECOMMON::PROPERTY_VPB); return entity; } // ********************************************************************************************************* CInstance *CEditor::getInstanceFromEntity(CEntityCL *entity) const { //H_AUTO(R2_CEditor_getInstanceFromEntity) CHECK_EDITOR for(TInstanceMap::const_iterator it = _Instances.begin(); it != _Instances.end(); ++it) { if (it->second->getEntity() == entity) return it->second; } return NULL; } // ********************************************************************************************************* void CEditor::displayContextMenu() { //H_AUTO(R2_CEditor_displayContextMenu) CHECK_EDITOR if (!_SelectedInstance) { // launch standard context menu (for free look & move options) getUI().launchContextMenuInGame("ui:interface:game_context_menu_edition"); return; } if (getCurrentTool() && getCurrentTool()->isCreationTool()) { setCurrentTool(NULL); } // retrieve the context menu depending on the type of the entity CLuaObject classDesc = _SelectedInstance->getClass(); if (classDesc.isNil()) { nlwarning("Can't retrieve object class"); return; } std::string menu = classDesc["Menu"]; if (menu.empty()) return; // no menu for that instance ? // CLuaObject menuSetupFunction = classDesc["onSetupMenu"]; if (menuSetupFunction.isFunction()) { _SelectedInstance->getLuaProjection().push(); menuSetupFunction.callNoThrow(1, 0); } // getUI().launchContextMenuInGame(menu); } // ********************************************************************************************************* void CEditor::triggerInstanceObserver(const TInstanceId &id, IObserverAction &action) { //H_AUTO(R2_CEditor_triggerInstanceObserver) TInstanceObserverMap::iterator lb = _InstanceObservers.lower_bound(id); TInstanceObserverMap::iterator ub = _InstanceObservers.upper_bound(id); if (lb == ub) return; // must do a copy, because an observer may erase himself from the list when it is triggered static std::vector dest; dest.clear(); for (TInstanceObserverMap::iterator it = lb; it != ub; ++it) { dest.push_back(it->second); } for (std::vector::iterator it = dest.begin(); it != dest.end(); ++it) { if (*it) action.doAction(**it); } } // ********************************************************************************************************* void CEditor::onErase(CObject *object) { //H_AUTO(R2_CEditor_onErase) bool dummyFoundInBase; std::string dummyNameInParent; onErase(object, dummyFoundInBase, dummyNameInParent); } // ********************************************************************************************************* void CEditor::onErase(CObject *root, bool &foundInBase, std::string &nameInParent) { //H_AUTO(R2_CEditor_onErase) foundInBase = false; CHECK_EDITOR if (!root) return; CInstance *inst = getInstanceFromObject(root); // sons if (root->isTable()) { for(uint k = 0; k < root->getSize(); ++k) { CObject *obj = root->getValue(k); if (obj->isTable()) { onErase(obj); } } } // CObject *parent = NULL; // if (inst) { // Add an 'Erased' flag to the object so that // any pending property in the property sheet // won't send a 'requestSetNode' when it is closed try { (*inst).getLuaProjection()["User"].setValue("Erased", true); } catch (const ELuaNotATable &e) { nlwarning(e.what()); } // if object is selected or focused, then clear these flags if (inst == getSelectedInstance()) { setSelectedInstance(NULL); } if (inst == getFocusedInstance()) { setFocusedInstance(NULL); } } // if object can be found in its base, then not really a deletion, but // rather a change of attribute to the "default value' (may happen in an undo operation) parent = root->getParent(); if (parent) { sint32 sonIndex = parent->findIndex(root); if (sonIndex != -1) { nameInParent = parent->getKey(sonIndex); if (!nameInParent.empty() && getDMC().getPropertyAccessor().hasValueInBase(parent, nameInParent)) { foundInBase = true; } } } if (inst && !foundInBase) { // send event to instance & displayers inst->onErase(); // trigger observers class CEraseNotification : public IObserverAction { public: CEraseNotification(CInstance &instance) : Instance(instance) {} virtual void doAction(IInstanceObserver &obs) { obs.onInstanceErased(Instance); } CInstance &Instance; }; CEraseNotification eraseNotification(*inst); triggerInstanceObserver(inst->getId(), eraseNotification); } // if object has a user environment attached to it, remove it from lua if (inst) { CLuaStackChecker lsc(&getLua()); getLua().pushLightUserData((void *) root); getLua().getTable(LUA_REGISTRYINDEX); if (!getLua().isNil()) { getLua().pushLightUserData((void *) root); // key getLua().pushNil(); // value getLua().setTable(LUA_REGISTRYINDEX); // erase } getLua().pop(); } if (root->isTable()) { // special patch: ref ids under this object should not be triggered any more for that object CObjectTable *rootTable = root->toTable(); for (uint32 k = 0; k < rootTable->getSize(); ++k) { CObject *obj = rootTable->getValue(k); CObjectRefIdClient *objRefId = dynamic_cast(obj); if (objRefId) { objRefId->enable(false); // don't observe anything } } } if (!_ClearingContent) { if (inst) { nlassert(_Instances.count((const CObjectTable *) root) == 1); //nlwarning("Instance with id %s deleted, but not inserted", inst->getId().c_str()); } } // really remove object //nlwarning("Removing instance with id %s (table = 0x%s)", inst->getId().c_str(), (int) root); if (inst) { _Instances.erase((const CObjectTable *) root); } } // ********************************************************************************************************* void CEditor::nodeErased(const std::string& instanceId, const std::string& attrName, sint32 position) { //H_AUTO(R2_CEditor_nodeErased) CHECK_EDITOR CObject *obj = _DMC->find(instanceId, attrName, position); if (!obj) return; bool foundInBase; std::string nameInParent; onErase(obj, foundInBase, nameInParent); CObject *parent = obj->getParent(); _DMC->CDynamicMapClient::nodeErased(instanceId, attrName, position); if (foundInBase) { nlassert(parent); // erased, but reading in the base will give a new value, so the real // action from observers standpoint is 'modified' CInstance *parentInstance = getInstanceFromObject(parent); if (parentInstance) { onAttrModified(*parentInstance, nameInParent); } else { nlwarning("Can't found instance in which %s was modified", nameInParent.c_str()); } } // warn the parent that it has been modified onAttrModified(parent); // NB : msg for deleted attribute does not exist yet, so the parent is warned that it is modified, but // 'onTableModified' is not called (because key doesn't exist any more) } void CEditor::onResetEditionMode() { //H_AUTO(R2_CEditor_onResetEditionMode) CHECK_EDITOR // called when a scenario just before a scenario is created // no-op, to clean } void CEditor::onEditionModeConnected( uint32 userSlotId, uint32 adventureId, CObject* highLevel, const std::string& versionName, bool willTP, uint32 initialActIndex) { //H_AUTO(R2_CEditor_onEditionModeConnected) CHECK_EDITOR if (!_WaitScenarioScreenActive) { setMode(EditionMode); } CInterfaceGroup *currentSessionGroup = dynamic_cast(getUI().getElementFromId("ui:interface:r2ed_current_session")); if (currentSessionGroup) { CViewText *text = dynamic_cast(currentSessionGroup->getView("current_session")); if (text) { text->setText(toString("Edition Session = %d (%s)", adventureId, versionName.c_str())); } } setDefaultChatWindow(PeopleInterraction.ChatGroup.Window); _DMC->CDynamicMapClient::onEditionModeConnected(userSlotId, adventureId, highLevel, versionName, willTP, initialActIndex); } void CEditor::setAccessMode(TAccessMode mode) { //H_AUTO(R2_CEditor_setAccessMode) _AccessMode = mode; switch ( mode) { case AccessEditor: _Env.setValue("AccessMode", "Editor"); break; case AccessDM: _Env.setValue("AccessMode", "DM"); if (_Mode == AnimationModePlay) { loadStandardUI(); } break; case AccessOutlandOwner: _Env.setValue("AccessMode", "OutlandOwner"); break; default: nlassert(0); break; } } void CEditor::onAnimationModeConnected(const CClientMessageAdventureUserConnection& connected) { //H_AUTO(R2_CEditor_onAnimationModeConnected) _ScenarioReceivedFlag = true; // end wait screen // TMP PATCH // _WillTP = true; CHECK_EDITOR switch(connected.Mode) { case 0: setMode(AnimationModeLoading); break; case 1: setMode(AnimationModeWaitingForLoading); break; case 2: setMode(AnimationModeDm); break; case 3: setMode(AnimationModePlay); break; default: nlwarning("Unhandled %u in Animation Session", connected.Mode); } CInterfaceGroup *currentSessionGroup = dynamic_cast(getUI().getElementFromId("ui:interface:r2ed_current_session")); if (currentSessionGroup) { CViewText *text = dynamic_cast(currentSessionGroup->getView("current_session")); if (text) { text->setText(toString("Animation Session = %d (%s)", connected.SessionId.asInt(), connected.VersionName.c_str())); } } setDefaultChatWindow(PeopleInterraction.ChatGroup.Window); _DMC->CDynamicMapClient::onAnimationModeConnected(connected); } void CEditor::onEditionModeDisconnected() { //H_AUTO(R2_CEditor_onEditionModeDisconnected) _EditionModeDisconnectedFlag = true; delete _NewScenario; _NewScenario = NULL; CHECK_EDITOR // Useful only for the pionner that does not do requestTranslateFeatures() // Because avec using the button the currentScenario = 0 try { R2::getEditor().getLua().executeScript("r2.Version.save(\"save/r2_buffer.dat\")"); } catch (const std::exception& e) { nlwarning("Can't start Edition Mode", e.what()); } _DMC->CDynamicMapClient::onEditionModeDisconnected(); } void CEditor::onTestModeConnected() { //H_AUTO(R2_CEditor_onTestModeConnected) CHECK_EDITOR // TODO nico : change the name of the function : should rather be 'onAnimationModeConnected' // start as a GM getUI().runActionHandler("r2ed_anim_dm_mode", NULL, ""); _DMC->CDynamicMapClient::onTestModeConnected(); } void CEditor::onTestModeDisconnected(TSessionId sessionId, uint32 lastAct, TScenarioSessionType sessionType) { //H_AUTO(R2_CEditor_onTestModeDisconnected) CHECK_EDITOR _DMC->CDynamicMapClient::onTestModeDisconnected(sessionId, lastAct, sessionType); } // ********************************************************************************************************* void CEditor::nodeInserted(const std::string& instanceId, const std::string& attrName, sint32 position, const std::string& key, CObject* value) { //H_AUTO(R2_CEditor_nodeInserted) CHECK_EDITOR _DMC->CDynamicMapClient::nodeInserted(instanceId, attrName, position, key, value); if (value->isTable()) { std::string id = getString(value, "InstanceId"); if (!id.empty()) { const CObject *clonedValue = _DMC->find(id); // bad insert if (clonedValue) { createNewInstanceForObjectTable(clonedValue); onAttrModified(clonedValue->getParent()); // parent table is modified // so -> only parent of parent table will be notified of change // maybe we need another msg "onInsert" that is sent // to the parent table. } } } } // ********************************************************************************************************* void CEditor::createNewInstanceForObjectTable(const CObject *obj) { //H_AUTO(R2_CEditor_createNewInstanceForObjectTable) CHECK_EDITOR createNewInstanceForObjectTableInternal(obj); onPostCreate(obj); } // ********************************************************************************************************* void CEditor::notifyInstanceObserversOfCreation(CInstance &inst) { //H_AUTO(R2_CEditor_notifyInstanceObserversOfCreation) CHECK_EDITOR // if there are observers watching this instance, warn them const std::string &id = inst.getId(); // trigger observers class CCreationNotification : public IObserverAction { public: CCreationNotification(CInstance &instance) : Instance(instance) {} virtual void doAction(IInstanceObserver &obs) { obs.onInstanceCreated(Instance); } CInstance &Instance; }; CCreationNotification creationNotification(inst); triggerInstanceObserver(id, creationNotification); } // ********************************************************************************************************* void CEditor::onPostCreate(const CObject *obj) { //H_AUTO(R2_CEditor_onPostCreate) CHECK_EDITOR struct CPostCreateVisitor : public IObjectVisitor { virtual void visit(CObjectTable &obj) { // if table has a "InstanceId" field there is a matching editor object CInstance *inst = getEditor().getInstanceFromObject(&obj); if (inst) { inst->onPostCreate(); getEditor().notifyInstanceObserversOfCreation(*inst); } } }; if (obj) { CPostCreateVisitor postCreateVisitor; const_cast(obj)->visit(postCreateVisitor); } } // ********************************************************************************************************* sint CEditor::getLeftQuota() { //H_AUTO(R2_CEditor_getLeftQuota) CLuaState &ls = getLua(); CLuaStackChecker lsc(&ls); callEnvMethod("getLeftQuota", 0, 1); if (!ls.isNumber(-1)) { ls.pop(1); return 0; } sint result = (sint) ls.toNumber(-1); ls.pop(1); return result; } // ********************************************************************************************************* bool CEditor::checkRoomLeft() { //H_AUTO(R2_CEditor_checkRoomLeft) return getLeftQuota() > 0; } // ********************************************************************************************************* void CEditor::makeRoomMsg() { //H_AUTO(R2_CEditor_makeRoomMsg) // delegate ui display to lua callEnvMethod("makeRoomMsg", 0, 0); } // ********************************************************************************************************* bool CEditor::verifyRoomLeft(uint aiCost, uint staticCost) { //H_AUTO(R2_CEditor_verifyRoomLeft) CLuaState &ls = getLua(); if (aiCost) { CLuaStackChecker lsc(&ls); getEditor().getLua().push((lua_Number)aiCost); callEnvMethod("checkAiQuota", 1, 1); if (!ls.isBoolean(-1)) { ls.pop(1); return false; } sint result = (sint) ls.toBoolean(-1); ls.pop(1); return result != 0; } if (staticCost) { CLuaStackChecker lsc(&ls); getEditor().getLua().push((lua_Number)staticCost); callEnvMethod("checkStaticQuota", 1, 1); if (!ls.isBoolean(-1)) { ls.pop(1); return false; } sint result = (sint) ls.toBoolean(-1); ls.pop(1); return result != 0; } return true; } // ********************************************************************************************************* void CEditor::createNewInstanceForObjectTableInternal(const CObject *obj) { //H_AUTO(R2_CEditor_createNewInstanceForObjectTableInternal) CHECK_EDITOR if (!obj) return; if (!obj->isTable()) return; // not a table ... const CObjectTable *table = (const CObjectTable *) obj; std::string id = getString(obj, "InstanceId"); if (!id.empty()) { // CInstance is created only for objects with an instance id CInstance *inst = new CInstance(table, getLua()); nlassert(_Instances.count(table) == 0); _Instances[table] = inst; // if a cookie was created, add in in the instance lua 'User' table TCookieMap::iterator cookieList = _Cookies.find(id); if (cookieList != _Cookies.end()) { if (obj->isTable()) { CLuaState &ls = getLua(); getLuaUserTableFromObject(ls, * (CObjectTable *) obj); CLuaObject userTable(ls); // pop the table into a CLuaObject for convenience for (TCookieList::iterator it = cookieList->second.begin(); it != cookieList->second.end(); ++it) { userTable.setValue(it->Key, it->Value); } } _Cookies.erase(cookieList); } // create displayers CLuaObject classDesc = inst->getClass(); if (!classDesc.isNil()) { CLuaStackChecker lsc(&getLua()); CDisplayerVisual *dispViz= createObjectFromClassName(classDesc["DisplayerVisual"].toString()); if (dispViz) { bool ok = dispViz->init(classDesc["DisplayerVisualParams"]); if (!ok) { nlwarning("Error when calling init on visual displayer of class %s", classDesc["Name"].toString().c_str()); } } inst->setDisplayerVisual(dispViz); // CDisplayerBase *dispUI = createObjectFromClassName(classDesc["DisplayerUI"].toString()); if (dispUI) { bool ok = dispUI->init(classDesc["DisplayerUIParams"]); if (!ok) { nlwarning("Error when calling init on ui displayer of class %s", classDesc["Name"].toString().c_str()); } } inst->setDisplayerUI(dispUI); // CDisplayerBase *dispProp = createObjectFromClassName(classDesc["DisplayerProperties"].toString()); if (dispProp) { bool ok = dispProp->init(classDesc["DisplayerPropertiesParams"]); if (!ok) { nlwarning("Error when calling init on property displayer of class %s", classDesc["Name"].toString().c_str()); } } inst->setDisplayerProperties(dispProp); } // prevent completion of the tree while an instance is being created //backupRequestCommands(); inst->onCreate(); //restoreRequestCommands(); // // if a name was generated locally, erase from the local name map. std::string className = getString(obj, "Class"); std::string name = getString(obj, "Name"); if (!className.empty() && !name.empty()) { for (TGeneratedNameMap::iterator it = _LocalGeneratedNames.begin(); it != _LocalGeneratedNames.end(); ++it) { sint index = getGeneratedNameIndex(name, it->first); if (index != -1) { it->second.erase(index); break; } } } } // do the same on sons for(uint k = 0; k < table->getSize(); ++k) { createNewInstanceForObjectTableInternal(table->getValue(k)); } } // ********************************************************************************************************* void CEditor::onAttrModified(CInstance &parentInstance, const std::string &attrName, sint32 indexInArray) { //H_AUTO(R2_CEditor_onAttrModified) parentInstance.onAttrModified(attrName, indexInArray); class CAttrModifiedNotification : public IObserverAction { public: CAttrModifiedNotification(CInstance &instance, const std::string &key, sint32 indexInArray) : Instance(instance), Key(key), IndexInArray(indexInArray) {} virtual void doAction(IInstanceObserver &obs) { obs.onAttrModified(Instance, Key, IndexInArray); } CInstance &Instance; const std::string &Key; sint32 IndexInArray; }; CAttrModifiedNotification attrModifiedNotification(parentInstance, attrName, indexInArray); triggerInstanceObserver(parentInstance.getId(), attrModifiedNotification); } // ********************************************************************************************************* void CEditor::onAttrModified(const CObject *value) { //H_AUTO(R2_CEditor_onAttrModified) CHECK_EDITOR if (!value) return; const CObject *son = value; const CObject *parent = value->getParent(); sint32 indexInArray = -1; while (parent) { CInstance *parentInstance = getInstanceFromObject(parent); sint32 indexInParent = parent->findIndex(son); nlassert(indexInParent != -1); if (parentInstance) { // we are in an instance (a CObjectTable with an instance id) // TODO nico : a cache for the 'name' in the parent like with CObjectRefId ... std::string key = parent->getKey(indexInParent); onAttrModified(*parentInstance, key, indexInArray); indexInArray = -1; } else { // we are in an array in an instance -> memorize index in that array for next call to "onAttrModified"... indexInArray = indexInParent; } son = parent; parent = parent->getParent(); } } // ********************************************************************************************************* void CEditor::nodeSet(const std::string& instanceId, const std::string& attrName, CObject* value) { //H_AUTO(R2_CEditor_nodeSet) CHECK_EDITOR CObject *obj = _DMC->find(instanceId); if (!obj) return; // erase previous object nlassert(obj->isTable()); // if (!attrName.empty()) { obj = obj->findAttr(attrName); } if (obj) { onErase(obj); } // change the actual value _DMC->CDynamicMapClient::nodeSet(instanceId, attrName, value); // nlassert(!getInstanceFromObject(value)); // this must be a new object... // if (value->isTable()) { std::string id = getString(value, "InstanceId"); if (!id.empty()) { const CObject *clonedValue = _DMC->find(id); if (clonedValue) { createNewInstanceForObjectTable(clonedValue); // if the created object is a table, warn him that he has been created onAttrModified(clonedValue); return; } } } // no instance id for object, retrieve object pointer from parent const CObject *parent = _DMC->find(instanceId); if (!parent) return; onAttrModified(parent->getAttr(attrName)); } // ********************************************************************************************************* void CEditor::eraseScenario() { //H_AUTO(R2_CEditor_eraseScenario) if (_Scenario) { backupRequestCommands(); onErase(_Scenario); restoreRequestCommands(); _Scenario = NULL; } } // ********************************************************************************************************* void CEditor::scenarioUpdated(CObject* highLevel, bool willTP, uint32 initialActIndex) { //H_AUTO(R2_CEditor_scenarioUpdated) CHECK_EDITOR _ScenarioReceivedFlag = true; if (_WaitScenarioScreenActive) { // defer scenario update to the end of the wait screen _NewScenario = highLevel ? highLevel->clone() : NULL; _NewScenarioInitialAct = initialActIndex; _PostponeScenarioUpdated = true; return; } if (!highLevel) { _IsStartingScenario = false; _WillTP = false; callEnvFunc("onEmptyScenarioUpdated", 0); return; } // _WillTP = willTP; _WillTP = false; // TMP TMP _UpdatingScenario = true; _BaseAct = NULL; _CurrentAct = NULL; CLuaStackRestorer lsc(&getLua(), getLua().getTop()); //nlwarning("Scenario updated, start highlevel = "); //highLevel->dump(); eraseScenario(); _IsStartingScenario = false; if (_DMC->getEditionModule().getMustStartScenario()) { _IsStartingScenario = true; } nlassert(_Instances.empty()); nlassert(CDisplayerBase::ObjCount == 0); // _DMC->CDynamicMapClient::scenarioUpdated(highLevel, willTP, initialActIndex); //nlwarning("Scenario updated, content is = "); /* if (_DMC->getHighLevel()) { _DMC->getHighLevel()->dump(); } else { nlwarning("NULL"); } */ createNewInstanceForObjectTableInternal(_DMC->getHighLevel()); nlassert(highLevel->isTable()); _Scenario = (CObjectTable *) _DMC->getHighLevel(); if (_Scenario->getAttr("InstanceId")) { _ScenarioInstance = getInstanceFromId(_Scenario->getAttr("InstanceId")->toString()); } else { _ScenarioInstance = NULL; nlwarning("Can't retrieve scenario (no instance id)"); _Scenario->dump(); } static volatile bool forceDump = false; if (forceDump) { _Scenario->dump(); } // projectInLua(_Scenario); // push on the lua stack getLua().push(float(initialActIndex)); // example reconnect after test in act4 // update value in the framework callEnvFunc("onScenarioUpdated", 2); //nlwarning("Instance list now is :"); //dumpInstances(); CObject *acts = _Scenario->getAttr("Acts"); if (acts) { CObject *baseAct = acts->getValue(0); if (baseAct) { _BaseAct = getInstanceFromId(baseAct->toString("InstanceId")); } } if (!_BaseAct) { nlwarning("Base act not found at scenario update"); } if (_WantedActOnInit.empty()) { if (!_CurrentAct) // if act not currently setted at scenario creation ... { setCurrentAct(_BaseAct); // ...then default to the base act } } else { setCurrentActFromTitle(_WantedActOnInit); _WantedActOnInit.clear(); } _DMC->getActionHistoric().clear(highLevel); // reinit the undo / redo stack onPostCreate(_Scenario); // post creation require that current act has been set //TP is done via the onTpPositionSimulated message /* // teleport in good island if (ClientCfg.Local) { sint locationId = (uint) _ScenarioInstance->getLuaProjection()["Description"]["LocationId"].toNumber(); CScenarioEntryPoints &sep = CScenarioEntryPoints::getInstance(); _IslandCollision.loadEntryPoints(); if (sep.getCompleteIslands().empty()) { nlwarning("Entry points not loaded, teleport not done (local mode)"); } else if (locationId >= (sint) sep.getCompleteIslands().size()) { nlwarning("Bad location id %d", locationId); } else { // check if already in this entry point (no tp if so) const CScenarioEntryPoints::CCompleteIsland &ci = sep.getCompleteIslands()[locationId]; CVectorD playerPos = UserEntity->pos(); // if (playerPos.x <= ci.XMin || playerPos.x >= ci.XMax || playerPos.y <= ci.YMin || playerPos.y >= ci.YMax) { if(ci.EntryPoints.size()>0) { const CScenarioEntryPoints::CShortEntryPoint & shortEntryPoint = ci.EntryPoints[0]; CVector dest((float) shortEntryPoint.X, (float) shortEntryPoint.Y, 0.f); UserEntity->pos(dest); // change position in pacs // Select the closest continent from the new position. beginLoading (LoadingBackground); #define BAR_STEP_TP 2 // fixme : this define is duplicated.... ProgressBar.reset (BAR_STEP_TP); ucstring nmsg("Loading..."); ProgressBar.newMessage ( ClientCfg.buildLoadingString(nmsg) ); ProgressBar.progress(0); ContinentMngr.select(dest, ProgressBar); endLoading(); // Teleport the User. UserEntity->tp(dest); } } } } */ _UpdatingScenario = false; if (_DMC->getEditionModule().getMustStartScenario() && _DMC->getEditionModule().getScenarioUpToDate()) { _DMC->getEditionModule().setMustStartScenario( false); if ( _DMC->getCurrentScenario()->getHighLevel()) { bool result = true; if (ClientCfg.R2EDMustVerifyRingAccessWhileLoadingAnimation) { CLuaState &ls = getLua(); CLuaStackChecker lsc(&ls); callEnvFunc( "verifyScenario", 0, 1); if (!ls.isBoolean(-1)) { nlassert(0 && "verifyScenario return wrong type"); } result = ls.toBoolean(-1); ls.pop(); } if (result) //Start scenario only if allowed { ConnectionWanted = true; // ugly } } } } // ********************************************************************************************************* CInstance *CEditor::getDefaultFeature(CInstance *act) { //H_AUTO(R2_CEditor_getDefaultFeature) CHECK_EDITOR if (!act) return NULL; CObject *defaultFeature = act->getObjectTable()->getAttr("Features"); if (!defaultFeature) return NULL; defaultFeature = defaultFeature->getValue(0); if (!defaultFeature) return NULL; // 0 should be the default feature CInstance *result = getInstanceFromId(defaultFeature->toString("InstanceId")); if (!result) return NULL; if (!result->isKindOf("DefaultFeature")) { nlwarning("Can't retrieve default feature."); } return result; } // ********************************************************************************************************* CInstance *CEditor::getDefaultFeature() { //H_AUTO(R2_CEditor_getDefaultFeature) CHECK_EDITOR return getDefaultFeature(getCurrentAct()); } // ********************************************************************************************************* void CEditor::nodeMoved(const std::string& instanceId, const std::string& attrName, sint32 position, const std::string& destInstanceId, const std::string& destAttrName, sint32 destPosition) { //H_AUTO(R2_CEditor_nodeMoved) CHECK_EDITOR const CObject *src = _DMC->find(instanceId, attrName, position); if (!src) return; // CObject *oldParent = src->getParent(); // CInstance *inst = getInstanceFromObject(src); // tells object that he is about to move if (inst) { inst->onPreHrcMove(); // notify possible observers that this instance will move class CPreHrcMoveNotification : public IObserverAction { public: CPreHrcMoveNotification(CInstance &instance) : Instance(instance) {} virtual void doAction(IInstanceObserver &obs) { obs.onPreHrcMove(Instance); } CInstance &Instance; }; CPreHrcMoveNotification preHrcMoveNotification(*inst); triggerInstanceObserver(inst->getId(), preHrcMoveNotification); } // do the move _DMC->CDynamicMapClient::nodeMoved(instanceId, attrName, position, destInstanceId, destAttrName, destPosition); // warn the previous parent that it has been modified if (src->getParent() != oldParent) { onAttrModified(oldParent); // the old parent is modified so send appropriate msg } // else ... if new parent is the same then send message only at the end (else object believe it has been modified twice in a row) // tells object that he has moved if (inst) { inst->onPostHrcMove(); // notify possible observers that this instance has moved class CPostHrcMoveNotification : public IObserverAction { public: CPostHrcMoveNotification(CInstance &instance) : Instance(instance) {} virtual void doAction(IInstanceObserver &obs) { obs.onPostHrcMove(Instance); } CInstance &Instance; }; CPostHrcMoveNotification postHrcMoveNotification(*inst); triggerInstanceObserver(inst->getId(), postHrcMoveNotification); } onAttrModified(src); // the new parent is modified so send appropriate msg } // ********************************************************************************************************* void CEditor::dumpInstances() { //H_AUTO(R2_CEditor_dumpInstances) CHECK_EDITOR for(TInstanceMap::iterator it = _Instances.begin(); it != _Instances.end(); ++it) { nlwarning("Obj = %p, id = %s, instance = %p", it->first, it->second->getId().c_str(), &(*it->second)); } } // *************************************************************** struct CSortSelectableObject { ISelectableObject *Object; float Depth; CVector RayStart, RayStartClipped, RayEndClipped; NLMISC::CAABBox SelectBox; bool SelectBoxInWorld; bool operator<(const CSortSelectableObject &o) const { return DepthgetLastClip(); } virtual NLMISC::CAABBox getSelectBox() const { return UserEntity->localSelectBox(); } virtual const NLMISC::CMatrix &getInvertedMatrix() const { static CMatrix invertedMatrix; invertedMatrix = UserEntity->dirMatrix(); invertedMatrix.setPos(UserEntity->pos()); invertedMatrix.invert(); return invertedMatrix; } virtual float preciseIntersectionTest(const NLMISC::CVector &worldRayStart, const NLMISC::CVector &worldRayDir) const { if (!UserEntity) return FLT_MAX; return CEditor::preciseEntityIntersectionTest(*UserEntity, worldRayStart, worldRayDir); } virtual CInstance *getInstanceInEditor() const { return NULL; } }; // *************************************************************** static CSelectableUser SelectableUser; // CInstance *CEditor::getInstanceUnderPos(float x, float y, float distSelection, bool &isPlayerUnderCursor) { static volatile bool ignore = false; if (ignore) { return NULL; } //H_AUTO(R2_CEditor_getInstanceUnderPos) CHECK_EDITOR // TODO nico: this code was copied from CEntityManager::getEntityUnderPos // then modified, so some factoring could be made ... uint i; // valid only if bbox still intersect CInstance *precInstanceUnderPos= _LastInstanceUnderPos; bool precInstanceUnderPosValid= false; // reset result isPlayerUnderCursor= false; _LastInstanceUnderPos = NULL; // build the ray CMatrix camMatrix = MainCam.getMatrix(); NL3D::CFrustum camFrust = MainCam.getFrustum(); NL3D::CViewport viewport = Driver->getViewport(); // Get the Ray made by the mouse. CTool::CWorldViewRay worldViewRay; worldViewRay.OnMiniMap = false; worldViewRay.Valid = true; // viewport.getRayWithPoint(x, y, worldViewRay.Origin, worldViewRay.Dir, camMatrix, camFrust); worldViewRay.Dir.normalize(); worldViewRay.Right = camMatrix.getI().normed(); worldViewRay.Up = camMatrix.getK().normed(); // **** Get entities with box intersecting the ray. static std::vector validObjects; validObjects.clear(); static std::vector intersectedObjects; intersectedObjects.clear(); ISelectableObject *precSelectableObject = NULL; validObjects.push_back(&SelectableUser); // add fake object for test with user entity for(TInstanceMap::iterator it = _Instances.begin(); it != _Instances.end(); ++it) { CInstance *instance = it->second; CDisplayerVisual *vd = instance->getDisplayerVisual(); if (!vd) continue; if (!vd->isSelectable()) continue; if (instance == getEditor().getSelectedInstance()) { precSelectableObject = vd; } validObjects.push_back(vd); } // compute intersection of mouse with landscape / batiments for test with projected object like region & decals CVector sceneInter; CTool::TRayIntersectionType rayInterType = CTool::computeLandscapeRayIntersection(worldViewRay, sceneInter); CSortSelectableObject selectObj; ISelectableObject *borderSelected = NULL; for(uint k = 0; k < validObjects.size(); ++k) { ISelectableObject *object = validObjects[k]; // if entity not visible, skip if(object->getLastClip()) continue; float depth = FLT_MAX; ISelectableObject::TSelectionType selectionType = object->getSelectionType(); bool borderSelection = false; switch(selectionType) { case ISelectableObject::GroundProjected: if (rayInterType != CTool::NoIntersection) { CVector2f testPos(sceneInter.x, sceneInter.y); borderSelection = object->isInProjectionBorder(testPos); if (borderSelection || object->isInProjection(testPos)) { depth = (worldViewRay.Origin - sceneInter).norm(); } } break; case ISelectableObject::LocalSelectBox: case ISelectableObject::WorldSelectBox: { const CMatrix &rayMatrix = (selectionType == ISelectableObject::LocalSelectBox) ? object->getInvertedMatrix() : CMatrix::Identity; selectObj.RayStart = rayMatrix * worldViewRay.Origin; selectObj.RayStartClipped = selectObj.RayStart; selectObj.RayEndClipped = rayMatrix * (worldViewRay.Origin + worldViewRay.Dir * distSelection); // if intersect the bbox selectObj.SelectBox = object->getSelectBox(); selectObj.SelectBoxInWorld = false; if (selectObj.SelectBox.clipSegment(selectObj.RayStartClipped, selectObj.RayEndClipped)) { depth = (selectObj.RayStartClipped - selectObj.RayStart).norm(); // is it the last entity under pos? if(object->getInstanceInEditor() ==precInstanceUnderPos) precInstanceUnderPosValid= true; } } break; default: nlassert(0); break; } if (depth != FLT_MAX) { selectObj.Object = object; selectObj.Depth = depth; if (borderSelection) { borderSelected = object; } // add this entity to the list of possible entities intersectedObjects.push_back(selectObj); } } // if no intersected entities, quit if(intersectedObjects.empty()) return NULL; // Compute startDistBox: nearest entity distance, but the user float startDistBox; if(intersectedObjects[0].Object == &SelectableUser) { // if the nearest entity is the user, set res isPlayerUnderCursor= true; // if only player intersected, return NULL! if(intersectedObjects.size()==1) return NULL; // so take the second for startDistBox startDistBox= intersectedObjects[1].Depth; } else { // ok, take it. startDistBox= intersectedObjects[0].Depth; } /*static std::vector projectedObjects; projectedObjects.clear();*/ // **** get best entity according to distance face-camera or box-ray if no face intersection ISelectableObject *objectSelected= NULL; float bestDistBox= FLT_MAX; float bestDistZ= FLT_MAX; for(i=0;igetSelectionType(); switch(selectionType) { case ISelectableObject::GroundProjected: { // if current selection remains selected, keep it bool keepSelection = objectSelected && objectSelected->getSelectionType() == ISelectableObject::GroundProjected && objectSelected == precSelectableObject; if (!keepSelection) { preciseInterFound = true; distZ = intersectedObjects[i].Depth; } } break; case ISelectableObject::LocalSelectBox: case ISelectableObject::WorldSelectBox: // if (!object->intersectionTest(bbox, pos, dir, dist2D, distZ, distSelection)) continue; distZ = object->preciseIntersectionTest(worldViewRay.Origin, worldViewRay.Dir); if (distZ != FLT_MAX) { preciseInterFound = true; } break; default: nlassert(0); break; } // *** if intersect face, then take the best face-intersection, else use box-ray cost // true face-col found? if(preciseInterFound) { // yes, get the nearest if(distZ<=bestDistZ) { bestDistBox= 0; bestDistZ= distZ; objectSelected= object; /*if (selectionType == ISelectableObject::GroundProjected) { projectedObjects.push_back(object); }*/ } } // else else { // if a true face-intersection has not been found for others entities if(bestDistZ==FLT_MAX) { // get the "distance to camera" contribution. // NB: ray & select box are in the same space (local or world) CVector c= selectObj.SelectBox.getCenter(); float distCamCost= intersectedObjects[i].Depth; // get relative to the nearest intersected entity distCamCost-= startDistBox; // take the middle of the clipped segment. suppose that this middle is the "nearest ray point to center" // This is false, but gives better results. CVector m= (selectObj.RayStartClipped + selectObj.RayEndClipped) / 2; // get the distance to center. NB: small entities are preferred since smaller mean lower cost float outBBoxCost= (m - c).norm(); // the final cost is a weighted sum of the both. NB: distCamCost is in meter, // and outBBBoxCost is meters. Hence ClientCfg.SelectionOutBBoxWeight is a factor float boxCost= distCamCost + outBBoxCost * ClientCfg.SelectionOutBBoxWeight; // take the lowest cost if(boxCostgetDisplayerVisual(); } if (objectSelected->getSelectionType() == ISelectableObject::GroundProjected) { if (borderSelected && borderSelected != objectSelected) { if (!objectSelected || !objectSelected->isInProjectionBorder(CVector2f(sceneInter.x, sceneInter.y))) { // when mouse over zone border, preffered over other zones objectSelected = borderSelected; } } // TODO nico: list not really needed here (comes from old code) /*uint k; for(k = 0; k < projectedObjects.size(); ++k) { if (projectedObjects[k] == objectSelected) break; } objectSelected = projectedObjects[(k + 1) % projectedObjects.size()]; */ } // return the best entity _LastInstanceUnderPos = objectSelected ? objectSelected->getInstanceInEditor() : NULL; return _LastInstanceUnderPos; }// getEntityUnderPos // // ********************************************************************************************************* float CEditor::preciseEntityIntersectionTest(CEntityCL &entity, const NLMISC::CVector &worldRayStart, const NLMISC::CVector &worldRayDir) { //H_AUTO(R2_CEditor_preciseEntityIntersectionTest) CHECK_EDITOR // if entity skeleton model was clipped, skip NL3D::USkeleton *skeleton = entity.skeleton(); if(!ClientCfg.Light && skeleton && !skeleton->getLastClippedState()) return FLT_MAX; H_AUTO(RZ_Client_GEUP_face_intersect) // *** Try get face-intersection, result in distZ // if the entity support fast and precise intersection (and if it succeeds) // bool trueIntersectComputed= false; float dist2D, distZ; if(!ClientCfg.Light) { if(skeleton) { if(skeleton->supportFastIntersect() && skeleton->fastIntersect(worldRayStart, worldRayDir, dist2D, distZ, false)) { if (dist2D == 0.f) { return distZ; } } } // get the intersection with the instance (bot object) else if(!entity.instances().empty() && !entity.instances()[0].Current.empty()) { NL3D::UInstance inst= entity.instances()[0].Current; if(inst.supportFastIntersect()) { if (inst.fastIntersect(worldRayStart, worldRayDir, dist2D, distZ, false)) { if (dist2D == 0.f) { return distZ; } } } else { // TMP // precise test was asked, but there's no better test than bbox, so return the dist for bbox middle return (entity.pos().asVector() + entity.localSelectBox().getCenter() - worldRayStart).norm(); } } } return FLT_MAX; } // ********************************************************************************************************* const NLMISC::CAABBox &CEditor::getLocalSelectBox(CEntityCL &entity) const { //H_AUTO(R2_CEditor_getLocalSelectBox) CHECK_EDITOR const TEntityCustomSelectBoxMap &boxMap = getEditor().getEntityCustomSelectBoxMap(); TEntityCustomSelectBoxMap::const_iterator it = boxMap.find(CSheetId(entity.sheetId()).toString()); if (it != boxMap.end()) { if (it->second.Enabled) { return it->second.Box; } } return entity.localSelectBox(); } // ********************************************************************************************************* NLMISC::CAABBox CEditor::getSelectBox(CEntityCL &entity) const { //H_AUTO(R2_CEditor_getSelectBox) CHECK_EDITOR const TEntityCustomSelectBoxMap &boxMap = getEditor().getEntityCustomSelectBoxMap(); TEntityCustomSelectBoxMap::const_iterator it = boxMap.find(CSheetId(entity.sheetId()).toString()); if (it != boxMap.end()) { if (it->second.Enabled) { // box is local, transform in world CMatrix modelMatrix; modelMatrix = entity.dirMatrix(); modelMatrix.setPos(entity.pos().asVector()); return CAABBox::transformAABBox(modelMatrix, it->second.Box); } } return entity.selectBox(); } // ********************************************************************************************************* void CEditor::connexionMsg(const std::string &stringId) { //H_AUTO(R2_CEditor_connexionMsg) CHECK_EDITOR getEditor()._ConnexionMsg = stringId; // ignore if current ui desktop is not the third if (getUI().getMode() != 3) return; // show the connection window CInterfaceGroup *r2ConnectWindow = dynamic_cast(getUI().getElementFromId("ui:interface:r2ed_connect")); if (!r2ConnectWindow) return; if (stringId.empty()) { r2ConnectWindow->setActive(false); return; } else { if (!r2ConnectWindow->getActive()) { // center for the first display r2ConnectWindow->setActive(true); r2ConnectWindow->center(); } CViewText *vt = dynamic_cast(r2ConnectWindow->getView("connexionMsg")); if (vt) { vt->setText(CI18N::get(stringId)); } } } // ********************************************************************************************************* void CEditor::connect() { //H_AUTO(R2_CEditor_connect) CHECK_EDITOR if (_Mode == EditionMode || _Mode == AnimationModeLoading) { bool ok = false; if (_ScenarioInstance) { try { if (_ScenarioInstance->getLuaProjection().callMethodByNameNoThrow("validateForTesting", 0, 1)) { if (getLua().toBoolean(-1) == true) { R2::getEditor().getDMC().getEditionModule().requestStartScenario(); ok = true; } } } catch (const std::exception& ) { // 'ok' still == false ... } } if (!ok) { nlwarning("Can't go live"); } } else if (_Mode == TestMode || _Mode == DMMode) { try { R2::getEditor().getLua().executeScript("r2.requestStopLive()"); if (ClientCfg.Local) { R2::getEditor().setMode(CEditor::EditionMode); getLua().executeScriptNoThrow("r2.requestReconnection()"); } else { R2::getEditor().setMode(CEditor::GoingToEditionMode); } CEditor::connexionMsg("uimR2EDGoToEditingMode"); } catch (const std::exception& e) { nlwarning("Can't go live: %s", e.what()); } } // TODO Nico : reset the good capture keyboard setDefaultChatWindow(PeopleInterraction.ChatGroup.Window); ConnectionWanted = false; return; } // ********************************************************************************************************* CEditor::TInstanceObserverHandle CEditor::addInstanceObserver(const TInstanceId &instanceId, IInstanceObserver *observer) { //H_AUTO(R2_CEditor_addInstanceObserver) CHECK_EDITOR //nlwarning("#adding instance observer 0x%x", (int) observer); nlassert(_InstanceObservers.size() == _InstanceObserverHandles.size()); const TInstanceObserverMap::const_iterator lb = _InstanceObservers.lower_bound(instanceId); const TInstanceObserverMap::const_iterator ub = _InstanceObservers.upper_bound(instanceId); // NB nico : removed the inserted twice stuff below because observer pointers are not used as keys, // so sharing is possible // see if not inserted twice /* for (TInstanceObserverMap::const_iterator it = lb; it != ub; ++it) { if (it->second == observer) { nlwarning("addInstanceObserver : Instance observer inserted twice"); return BadInstanceObserverHandle; } } */ // insert the handle TInstanceObserverHandle handle = _InstanceObserverHandleCounter++; if (_InstanceObserverHandleCounter == BadInstanceObserverHandle) { ++ _InstanceObserverHandleCounter; // avoid bad handle } _InstanceObserverHandles[handle] = _InstanceObservers.insert(TInstanceObserverMap::value_type(instanceId, observer)); nlassert(_InstanceObservers.size() == _InstanceObserverHandles.size()); return handle; } // ********************************************************************************************************* CEditor::IInstanceObserver *CEditor::removeInstanceObserver(TInstanceObserverHandle handle) { //H_AUTO(R2_CEditor_removeInstanceObserver) CHECK_EDITOR nlassert(_InstanceObservers.size() == _InstanceObserverHandles.size()); TInstanceObserverHandleMap::iterator it = _InstanceObserverHandles.find(handle); if (it == _InstanceObserverHandles.end()) { nlwarning("removeInstanceObserver : Instance observer handle not found : %d", (int) handle); return NULL; } IInstanceObserver *observer = it->second->second; //nlwarning("#removing instance observer 0x%x", (int) observer); _InstanceObservers.erase(it->second); // remove from observer map _InstanceObserverHandles.erase(it); nlassert(_InstanceObservers.size() == _InstanceObserverHandles.size()); return observer; } // ********************************************************************************************************* CEditor::IInstanceObserver *CEditor::getInstanceObserver(TInstanceObserverHandle handle) { //H_AUTO(R2_CEditor_getInstanceObserver) CHECK_EDITOR nlassert(_InstanceObservers.size() == _InstanceObserverHandles.size()); TInstanceObserverHandleMap::iterator it = _InstanceObserverHandles.find(handle); if (it == _InstanceObserverHandles.end()) { return NULL; } return it->second->second; } // ********************************************************************************************************* CEditor::TSeason CEditor::getSeason() const { //H_AUTO(R2_CEditor_getSeason) if (_IsWaitingTPForSeasonChange) return UnknownSeason; // as long at the teleport message hasn't been received, don't change // the season for nothing -> pretend that we don't know the season so that it remains unchnged return _Season; } // ********************************************************************************************************* void CEditor::tpReceived() { //H_AUTO(R2_CEditor_tpReceived) _IsWaitingTPForSeasonChange = false; // season can be changed now _TPReceivedFlag = true; } // ********************************************************************************************************* void CEditor::checkMissingCollisions() { //H_AUTO(R2_CEditor_checkMissingCollisions) CScenarioEntryPoints &sep = CScenarioEntryPoints::getInstance(); sep.loadCompleteIslands(); const CScenarioEntryPoints::TCompleteIslands &islands = sep.getCompleteIslands(); for(uint k = 0; k < islands.size(); ++k) { bool found = !(CPath::lookup(islands[k].Island + ".packed_island", false, false).empty()); if (!found) { nlwarning("ISLAND COLLISION MISSING FOR : %s", islands[k].Island.c_str()); } found = !(CPath::lookup(islands[k].Island + ".island_hm", false, false).empty()); if (!found) { nlwarning("ISLAND HEIGHTMAP MISSING FOR : %s", islands[k].Island.c_str()); } } } // ********************************************************************************************************* // move class CAHEdContextMenu : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { getEditor().displayContextMenu(); } }; REGISTER_ACTION_HANDLER(CAHEdContextMenu, "r2ed_context_menu"); // ********************************************************************************************************* NLMISC::CVector getVector(const CObject *obj) { return getVectorD(obj).asVector(); } // ********************************************************************************************************* NLMISC::CVectorD getVectorD(const CObject *obj) { return CVectorD(getNumber(obj, "x"), getNumber(obj, "y"), getNumber(obj, "z")); } // ********************************************************************************************************* CObject *buildVector(const NLMISC::CVectorD &vector, const std::string &instanceId /*= ""*/) { CObject *table; if (instanceId == "") { table = getEditor().getDMC().newComponent("Position"); table->set("x", vector.x); table->set("y", vector.y); table->set("z", vector.z); } else { table = new CObjectTableClient; table->insert("InstanceId", new CObjectString(instanceId)); table->insert("Class", new CObjectString("Position")); table->insert("x", new CObjectNumber(vector.x)); table->insert("y", new CObjectNumber(vector.y)); table->insert("z", new CObjectNumber(vector.z)); } return table; } // ********************************************************************************************************* const CObject *getObject(const CObject *obj,const std::string &attrName) { if (!obj) return NULL; return getEditor().getDMC().getPropertyAccessor().getPropertyValue(obj, attrName); } // ********************************************************************************************************* std::string getString(const CObject *obj, const std::string &attrName) { obj = getObject(obj, attrName); if (!obj) return ""; return obj->isString() ? obj->toString() : ""; } // ********************************************************************************************************* double getNumber(const CObject *obj, const std::string &attrName) { obj = getObject(obj, attrName); if (!obj) return 0; return obj->isNumber() ? obj->toNumber() : 0; } bool isEditionCurrent() { CEditor &ed = getEditor(); return ClientCfg.R2EDEnabled && ed.getMode() == CEditor::EditionMode; } // ********************************************************************************************************* void CEditor::onContinentChanged() { //H_AUTO(R2_CEditor_onContinentChanged) if (_Mode != EditionMode) return; // refresh all collisions if (_ScenarioInstance) { struct CContinentChangedVisitor : public IInstanceVisitor { virtual void visit(CInstance &inst) { inst.onContinentChanged(); } }; CContinentChangedVisitor continentChangedVisitor; _ScenarioInstance->visit(continentChangedVisitor); } } //----------------------------- bool CEditor::getVisualPropertiesFromObject(CObject* object, SPropVisualA& vA, SPropVisualB& vB, SPropVisualC& vC) { //H_AUTO(R2_CEditor_getVisualPropertiesFromObject) std::string sheetClient = getString(object, "SheetClient"); const CEntitySheet *entitySheet = SheetMngr.get(CSheetId(sheetClient)); if (!entitySheet) { nlwarning("Can't find client sheet %s", sheetClient.c_str()); return false; } CSheetId sheetId(sheetClient); if (sheetId == CSheetId::Unknown) { nlwarning("Can't get sheet"); return false; } //-------------------------random init npc visual properties std::map< std::string, double > visualProps; static const char* keys[] = { "GabaritHeight", "GabaritTorsoWidth", "GabaritArmsWidth", "GabaritLegsWidth", "GabaritBreastSize" , "HairType", "HairColor", "Tattoo", "EyesColor" , "MorphTarget1", "MorphTarget2", "MorphTarget3", "MorphTarget4" , "MorphTarget5", "MorphTarget6", "MorphTarget7", "MorphTarget8" , "JacketModel", "TrouserModel", "FeetModel", "HandsModel" , "ArmModel", "WeaponRightHand", "WeaponLeftHand" , "JacketColor", "ArmColor", "HandsColor" , "TrouserColor", "FeetColor"}; unsigned int first = 0; unsigned int last = sizeof(keys) / sizeof(keys[0]); for (; first != last; ++first) { visualProps[keys[first]] = getNumber(object, keys[first]); } //vA.PropertySubData.Sex = (uint) visualProps["Sex"]; if(entitySheet) { const CCharacterSheet *chSheet = dynamic_cast(entitySheet); if(chSheet) { vA.PropertySubData.Sex = (chSheet->Gender == GSGENDER::female); } } vC.PropertySubData.CharacterHeight = (uint) visualProps["GabaritHeight"]; vC.PropertySubData.ArmsWidth = (uint) visualProps["GabaritArmsWidth"]; vC.PropertySubData.TorsoWidth = (uint) visualProps["GabaritTorsoWidth"]; vC.PropertySubData.LegsWidth = (uint) visualProps["GabaritLegsWidth"]; vC.PropertySubData.BreastSize = (uint) visualProps["GabaritBreastSize"]; int itemNb = (int) visualProps["HairType"]; std::string itemFileName; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.HatModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::HEAD_SLOT); } else { vA.PropertySubData.HatModel = 0; } vA.PropertySubData.HatColor = (uint) visualProps["HairColor"]; vC.PropertySubData.Tattoo = (uint) visualProps["Tattoo"]; vC.PropertySubData.EyesColor = (uint) visualProps["EyesColor"]; vC.PropertySubData.MorphTarget1 = (uint) visualProps["MorphTarget1"]; vC.PropertySubData.MorphTarget2 = (uint) visualProps["MorphTarget2"]; vC.PropertySubData.MorphTarget3 = (uint) visualProps["MorphTarget3"]; vC.PropertySubData.MorphTarget4 = (uint) visualProps["MorphTarget4"]; vC.PropertySubData.MorphTarget5 = (uint) visualProps["MorphTarget5"]; vC.PropertySubData.MorphTarget6 = (uint) visualProps["MorphTarget6"]; vC.PropertySubData.MorphTarget7 = (uint) visualProps["MorphTarget7"]; vC.PropertySubData.MorphTarget8 = (uint) visualProps["MorphTarget8"]; itemNb = (int) visualProps["JacketModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.JacketModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::CHEST_SLOT); } else { vA.PropertySubData.JacketModel = 0; } itemNb = (int) visualProps["TrouserModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.TrouserModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::LEGS_SLOT); } else { vA.PropertySubData.TrouserModel = 0; } itemNb = (int) visualProps["FeetModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vB.PropertySubData.FeetModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::FEET_SLOT); } else { vB.PropertySubData.FeetModel = 0; } itemNb = (int) visualProps["HandsModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vB.PropertySubData.HandsModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::HANDS_SLOT); } else { vB.PropertySubData.HandsModel = 0; } itemNb = (int) visualProps["ArmModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.ArmModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::ARMS_SLOT); } else { vA.PropertySubData.ArmModel = 0; } vA.PropertySubData.JacketColor = (uint) visualProps["JacketColor"]; vA.PropertySubData.TrouserColor = (uint) visualProps["TrouserColor"]; vB.PropertySubData.FeetColor = (uint) visualProps["FeetColor"]; vB.PropertySubData.HandsColor = (uint) visualProps["HandsColor"]; vA.PropertySubData.ArmColor = (uint) visualProps["ArmColor"]; itemNb = (int) visualProps["WeaponRightHand"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.WeaponRightHand = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::RIGHT_HAND_SLOT); } else { vA.PropertySubData.WeaponRightHand = 0; } itemNb = (int) visualProps["WeaponLeftHand"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.WeaponLeftHand = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::LEFT_HAND_SLOT); } else { vA.PropertySubData.WeaponLeftHand = 0; } return true; } // ********************************************************************************************************* bool CEditor::CSortedInstances::contains(CInstance *inst) const { TInstanceToIter::const_iterator it = _InstanceToIter.find(inst); #ifdef NL_DEBUG if (it == _InstanceToIter.end()) { for(TInstanceByName::const_iterator it = _ByName.begin(); it != _ByName.end(); ++it) { nlassert(it->second != inst); // should be empty } } #endif return it != _InstanceToIter.end(); } // ********************************************************************************************************* void CEditor::CSortedInstances::insert(const ucstring &name, CInstance *inst) { nlassert(inst); #ifdef NL_DEBUG static volatile bool doTest = true; if (doTest) { nlassert(!contains(inst)); // inserted twice !! } #endif _InstanceToIter[inst] = _ByName.insert(std::make_pair(name, inst)); } // ********************************************************************************************************* void CEditor::CSortedInstances::remove(CInstance *inst) { nlassert(inst); TInstanceToIter::iterator it = _InstanceToIter.find(inst); nlassert(it != _InstanceToIter.end()); _ByName.erase(it->second); _InstanceToIter.erase(it); #ifdef NL_DEBUG nlassert(!contains(inst)); #endif } // ********************************************************************************************************* bool CEditor::isRegisteredByDispName(CInstance *inst) const { nlassert(inst); for (uint k = 0; k < _InstancesByDispName.size(); ++k) { if (_InstancesByDispName[k].contains(inst)) return true; // registered twice !! } return false; } // ********************************************************************************************************* void CEditor::registerInstanceDispName(const ucstring &displayName, CInstance *inst) { nlassert(inst); sint currClass = inst->getClassIndex(); if (currClass < 0) { nlwarning("Classindex not found for class %s", inst->getClassName().c_str()); return; } #ifdef NL_DEBUG nlassert(!isRegisteredByDispName(inst)); #endif // for each class & subclass of the object, insert in the matching list while (currClass >= 0) { nlassert(currClass < (sint) _InstancesByDispName.size()); _InstancesByDispName[currClass].insert(displayName, inst); currClass = getBaseClass(currClass); } // #ifdef NL_DEBUG nlassert(isRegisteredByDispName(inst)); #endif } // ********************************************************************************************************* void CEditor::unregisterInstanceDispName(CInstance *inst) { nlassert(inst); sint currClass = inst->getClassIndex(); if (currClass < 0) { nlwarning("Classindex not found for class %s", inst->getClassName().c_str()); return; } #ifdef NL_DEBUG nlassert(isRegisteredByDispName(inst)); #endif while (currClass >= 0) { nlassert(currClass < (sint) _InstancesByDispName.size()); _InstancesByDispName[currClass].remove(inst); currClass = getBaseClass(currClass); } #ifdef NL_DEBUG nlassert(!isRegisteredByDispName(inst)); #endif } // ********************************************************************************************************* // // Creation of a new entity in scene // class CAHCreateEntity : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &sParams) { CHECK_EDITOR if (getEditor().getMode() != CEditor::EditionMode) { nlwarning("Can't modify scenario while testing"); return; } CInterfaceManager *im = CInterfaceManager::getInstance(); // Retrieve sheet for entity that is to be created. std::string paletteId = getParam(sParams, "PaletteId"); CObject *paletteNode = getEditor().getDMC().getPaletteElement(paletteId); if (!paletteNode) { nlwarning("Can't retrieve palette node for id %s", paletteId.c_str()); return; } if (!paletteNode->isTable()) { nlwarning("Bad type for palette node %s (should be a table)", paletteId.c_str()); return; } std::string sheetClient = getString(paletteNode, "SheetClient"); const CEntitySheet *entitySheet = SheetMngr.get(CSheetId(sheetClient)); if (!entitySheet) { nlwarning("Can't find client sheet %s", sheetClient.c_str()); return; } const CCharacterSheet *chSheet = dynamic_cast(entitySheet); if(chSheet->R2Npc) { getEditor().getLua().push(sheetClient); if (getEditor().getEnv().callMethodByNameNoThrow("randomNPCSex", 1, 1)) { CLuaObject result(getEditor().getLua()); sheetClient = result.toString(); } } CSheetId sheetId(sheetClient); if (sheetId == CSheetId::Unknown) { nlwarning("Can't get sheet"); return; } getEditor().setCurrentTool(NULL); // remove current to avoid to have ghost removed by that tool if it was a "CToolCreateEntity" too. uint ghostSlot = 1; // TMP TMP CEntityCL * entity = NULL; if (!(entity=CEditor::createEntity(ghostSlot, sheetId, CVector::Null, 0.f))) { return; } //-------------------------random init npc visual properties if(dynamic_cast(entity)) { // push equipment id getEditor().getLua().push(getString(paletteNode, "Equipment")); // push race std::string race; switch(entity->people()) { case EGSPD::CPeople::Fyros: race = "Fyros"; break; case EGSPD::CPeople::Matis: race = "Matis"; break; case EGSPD::CPeople::Tryker: race = "Tryker"; break; case EGSPD::CPeople::Zorai: race = "Zorai"; break; default: nlwarning("CAHCreateEntity::execute unknown people"); } getEditor().getLua().push(race); if (getEditor().getEnv().callMethodByNameNoThrow("randomNPCProperties", 2, 1)) { CLuaObject result(getEditor().getLua()); std::map< std::string, double > visualProps; ENUM_LUA_TABLE(result, it) { visualProps[it.nextKey().toString()] = it.nextValue().toNumber(); } // visual property A depends on the type of the entity SPropVisualA vA; SPropVisualB vB; SPropVisualC vC; sint64 *prop = 0; //vA.PropertySubData.Sex = (uint) visualProps["Sex"]; const CEntitySheet *entitySheet = SheetMngr.get((CSheetId)sheetId.asInt()); if(entitySheet) { const CCharacterSheet *chSheet = dynamic_cast(entitySheet); if(chSheet) { vA.PropertySubData.Sex = (chSheet->Gender == GSGENDER::female); } } vC.PropertySubData.CharacterHeight = (uint) visualProps["GabaritHeight"]; vC.PropertySubData.ArmsWidth = (uint) visualProps["GabaritArmsWidth"]; vC.PropertySubData.TorsoWidth = (uint) visualProps["GabaritTorsoWidth"]; vC.PropertySubData.LegsWidth = (uint) visualProps["GabaritLegsWidth"]; vC.PropertySubData.BreastSize = (uint) visualProps["GabaritBreastSize"]; int itemNb = (int) visualProps["HairType"]; std::string itemFileName; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.HatModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::HEAD_SLOT); } else { vA.PropertySubData.HatModel = 0; } vA.PropertySubData.HatColor = (uint) visualProps["HairColor"]; vC.PropertySubData.Tattoo = (uint) visualProps["Tattoo"]; vC.PropertySubData.EyesColor = (uint) visualProps["EyesColor"]; vC.PropertySubData.MorphTarget1 = (uint) visualProps["MorphTarget1"]; vC.PropertySubData.MorphTarget2 = (uint) visualProps["MorphTarget2"]; vC.PropertySubData.MorphTarget3 = (uint) visualProps["MorphTarget3"]; vC.PropertySubData.MorphTarget4 = (uint) visualProps["MorphTarget4"]; vC.PropertySubData.MorphTarget5 = (uint) visualProps["MorphTarget5"]; vC.PropertySubData.MorphTarget6 = (uint) visualProps["MorphTarget6"]; vC.PropertySubData.MorphTarget7 = (uint) visualProps["MorphTarget7"]; vC.PropertySubData.MorphTarget8 = (uint) visualProps["MorphTarget8"]; itemNb = (int) visualProps["JacketModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.JacketModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::CHEST_SLOT); } else { vA.PropertySubData.JacketModel = 0; } itemNb = (int) visualProps["TrouserModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.TrouserModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::LEGS_SLOT); } else { vA.PropertySubData.TrouserModel = 0; } itemNb = (int) visualProps["FeetModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vB.PropertySubData.FeetModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::FEET_SLOT); } else { vB.PropertySubData.FeetModel = 0; } itemNb = (int) visualProps["HandsModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vB.PropertySubData.HandsModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::HANDS_SLOT); } else { vB.PropertySubData.HandsModel = 0; } itemNb = (int) visualProps["ArmModel"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.ArmModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::ARMS_SLOT); } else { vA.PropertySubData.ArmModel = 0; } vA.PropertySubData.JacketColor = (uint) visualProps["JacketColor"]; vA.PropertySubData.TrouserColor = (uint) visualProps["TrouserColor"]; vB.PropertySubData.FeetColor = (uint) visualProps["FeetColor"]; vB.PropertySubData.HandsColor = (uint) visualProps["HandsColor"]; vA.PropertySubData.ArmColor = (uint) visualProps["ArmColor"]; itemNb = (int) visualProps["WeaponRightHand"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.WeaponRightHand = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::RIGHT_HAND_SLOT); } else { vA.PropertySubData.WeaponRightHand = 0; } itemNb = (int) visualProps["WeaponLeftHand"]; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.WeaponLeftHand = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::LEFT_HAND_SLOT); } else { vA.PropertySubData.WeaponLeftHand = 0; } prop = (sint64 *)&vA; im->getDbProp("SERVER:Entities:E"+toString("%d", entity->slot())+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->setValue64(*prop); prop = (sint64 *)&vB; im->getDbProp("SERVER:Entities:E"+toString("%d", entity->slot())+":P"+toString("%d", CLFECOMMON::PROPERTY_VPB))->setValue64(*prop); prop = (sint64 *)&vC; im->getDbProp("SERVER:Entities:E"+toString("%d", entity->slot())+":P"+toString("%d", CLFECOMMON::PROPERTY_VPC))->setValue64(*prop); EntitiesMngr.updateVisualProperty(0, entity->slot(), CLFECOMMON::PROPERTY_VPA); EntitiesMngr.updateVisualProperty(0, entity->slot(), CLFECOMMON::PROPERTY_VPB); EntitiesMngr.updateVisualProperty(0, entity->slot(), CLFECOMMON::PROPERTY_VPC); } } getEditor().setCurrentTool(new CToolCreateEntity(ghostSlot, paletteId, im->getDbProp("UI:TEMP:R2_DRAW_ARRAY")->getValueBool())); } }; REGISTER_ACTION_HANDLER(CAHCreateEntity, "r2ed_create_entity"); // ********************************************************************************************************* sint CEditor::classToIndex(const std::string &className) const { CHashMap::const_iterator it = _ClassNameToIndex.find(className); if (it == _ClassNameToIndex.end()) return -1; return it->second; } // ********************************************************************************************************* bool CEditor::isKindOf(sint testedClassIndex, sint kindClassIndex) const { if (testedClassIndex < 0 || kindClassIndex < 0) return false; return _KindOfTable(testedClassIndex, kindClassIndex) != 0; } // ********************************************************************************************************* void CEditor::initClassInheritanceTable() { std::vector classes; std::map baseClasses; ENUM_LUA_TABLE(getClasses(), it) { CLuaObject name = it.nextValue()["Name"]; CLuaObject base = it.nextValue()["BaseClass"]; std::string baseName; if (base.isString()) { baseName = base.toString(); } if (name.isString()) { _ClassNameToIndex[name.toString()] = (uint)classes.size(); classes.push_back(name.toString()); baseClasses[name.toString()] = baseName; } } _KindOfTable.init((uint)classes.size(), (uint)classes.size(), 0); _BaseClassIndices.resize(classes.size()); for (uint k = 0; k < classes.size(); ++k) { //nlwarning("Class %d = %s", (int) k, classes[k].c_str()); _BaseClassIndices[k] = classToIndex(baseClasses[classes[k]]); for (uint l = 0; l < classes.size(); ++l) { std::string currClass = classes[k]; while(!currClass.empty()) { if (currClass == classes[l]) { _KindOfTable(k, l) = 1; break; } currClass = baseClasses[currClass]; } } } _InstancesByDispName.clear(); _InstancesByDispName.resize(classes.size()); } // ********************************************************************************************************* sint CEditor::getBaseClass(sint derivedClass) const { if (derivedClass < 0 || derivedClass >= (sint) _BaseClassIndices.size()) return -1; return _BaseClassIndices[derivedClass]; } // ********************************************************************************************************* void CEditor::setMaxVisibleEntityExceededFlag(bool on) { if (on == _MaxVisibleEntityExceededFlag) return; _MaxVisibleEntityExceededFlag = on; // lua ui update CLuaStackChecker lsc(&getLua(), 0); getLua().push(on); callEnvMethod("setMaxVisibleEntityExceededFlag", 1, 0); } // ********************************************************************************************************* class CAHGoTest : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { CHECK_EDITOR ConnectionWanted = true; } }; REGISTER_ACTION_HANDLER(CAHGoTest, "r2ed_go_test"); // ********************************************************************************************************* class CAHStopTest : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { CHECK_EDITOR if (getEditor().getAccessMode() != CEditor::AccessEditor) return; ConnectionWanted = true; } }; REGISTER_ACTION_HANDLER(CAHStopTest, "r2ed_stop_test"); // ********************************************************************************************************* class CAHOpenScenarioControl : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &sParams) { CHECK_EDITOR bool openUI = true; bool showHide = (getParam(sParams, "showHide")=="1"); CInterfaceManager *pIM = CInterfaceManager::getInstance(); CInterfaceGroup* wnd = NULL; if(!R2::getEditor().isInitialized()) wnd = dynamic_cast(pIM->getElementFromId("ui:interface:ring_scenario_loading_window")); else wnd = dynamic_cast(pIM->getElementFromId("ui:interface:r2ed_scenario_control")); if(wnd) { if(showHide) openUI = !wnd->getActive(); wnd->setActive(openUI); } //} } }; REGISTER_ACTION_HANDLER(CAHOpenScenarioControl, "open_scenario_control"); // ********************************************************************************************************* class CAHR2StopLive : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &sParams) { CHECK_EDITOR bool confirmStopLive = getParam(sParams, "confirmStopLive")=="1"; if(!confirmStopLive) { CInterfaceManager *pIM = CInterfaceManager::getInstance(); pIM->validMessageBox(CInterfaceManager::QuestionIconMsg, CI18N::get("uiR2EDconfirmStopLive"), "r2_stop_live", "confirmStopLive=1", "", "", "ui:interface"); } else { CSessionBrowserImpl &sb = CSessionBrowserImpl::getInstance(); sb.closeSession(sb.getCharId(), R2::getEditor().getDMC().getEditionModule().getCurrentAdventureId()); if(sb.waitOneMessage(sb.getMessageName("on_invokeResult"))) { if(R2::getEditor().getDMC().getEditionModule().getEditSessionLink()!=0) { // Now we expect to receive an impulsion FAR_TP to mainland, triggered // by the DSS. We'll disobey it by FarTPing to the Edition session instead! nldebug( "Will return to editing session %u", R2::getEditor().getDMC().getEditionModule().getEditSessionLink().asInt() ); FarTP.hookNextFarTPForEditor(); } // otherwise, let accomplish the FAR_TP to mainland (returnToPreviousSession) // triggered by the DSS. } else { nlwarning("closeSession callback return false"); } } } }; REGISTER_ACTION_HANDLER(CAHR2StopLive, "r2_stop_live"); // ********************************************************************************************************* class CAHInviteCharacter : public IActionHandler { virtual void execute(CCtrlBase *pCaller, const std::string &/* sParams */) { CHECK_EDITOR CInterfaceManager *pIM = CInterfaceManager::getInstance(); pIM->runActionHandler("leave_modal", pCaller, ""); if(pCaller) { CInterfaceGroup *fatherGC = pCaller->getParent(); if (fatherGC) { // Look for the root parent for(;;) { CInterfaceGroup *parent = fatherGC->getParent(); if (!parent || (parent->getId()=="ui:interface")) break; fatherGC = parent; } // Get the modal edit box CGroupEditBox *geb = dynamic_cast(fatherGC->getGroup("add_contact_eb:eb")); if (geb && !geb->getInputString().empty()) { string charName = geb->getInputString().toString(); CSessionBrowserImpl & sessionBrowser = CSessionBrowserImpl::getInstance(); sessionBrowser.inviteCharacterByName(sessionBrowser.getCharId(), charName); if(!sessionBrowser.waitOneMessage(sessionBrowser.getMessageName("on_invokeResult"))) { nlwarning("inviteCharacterByName callback return false"); } if(sessionBrowser._LastInvokeResult == 14) { CViewText* pVT = dynamic_cast(pIM->getElementFromId("ui:interface:warning_free_trial:text")); if (pVT != NULL) pVT->setText(CI18N::get("uiRingWarningInviteFreeTrial")); pIM->runActionHandler("enter_modal", pCaller, "group=ui:interface:warning_free_trial"); } else if(sessionBrowser._LastInvokeResult == 12) { pIM->runActionHandler("enter_modal", pCaller, "group=ui:interface:warning_newcomer"); } geb->setInputString(ucstring("")); } } } } }; REGISTER_ACTION_HANDLER(CAHInviteCharacter, "r2ed_invite_character"); // ********************************************************************************************************* class CAHTryGoTest : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { CHECK_EDITOR getEditor().callEnvMethod("tryGoTest", 0, 0); } }; REGISTER_ACTION_HANDLER(CAHTryGoTest, "r2ed_try_go_test"); // ********************************************************************************************************* class CAHCancelTool : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { if (!ClientCfg.R2EDEnabled || !getEditor().isInitialized()) return; CHECK_EDITOR getEditor().setCurrentTool(NULL); } }; REGISTER_ACTION_HANDLER(CAHCancelTool, "r2ed_cancel_tool"); ////////////////////////////////////////// // SWITCH DM / TESTER AT ANIMATION TIME // ////////////////////////////////////////// // ********************************************************************************************************* class CAHAnimTestMode : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { CHECK_EDITOR if (getEditor().getAccessMode() == CEditor::AccessDM) return; getEditor().setMode(CEditor::TestMode); } }; REGISTER_ACTION_HANDLER(CAHAnimTestMode, "r2ed_anim_test_mode"); // ********************************************************************************************************* class CAHAnimDMMode : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { CHECK_EDITOR getEditor().setMode(CEditor::DMMode); } }; REGISTER_ACTION_HANDLER(CAHAnimDMMode, "r2ed_anim_dm_mode"); // ********************************************************************************************************* class CAHR2ContextCommand : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &sParams) { CHECK_EDITOR // forward the call to lua // first param is the id of the command std::string commandId = getParam(sParams, "commandId"); // if there's a current active tool, then see if it wants to handle it first if (commandId == "delete") { if (getEditor().getCurrentTool() && getEditor().getCurrentTool()->onDeleteCmd()) return; } getEditor().getLua().push(commandId); getEditor().callEnvMethod("execContextCommand", 1, 0); } }; REGISTER_ACTION_HANDLER(CAHR2ContextCommand, "r2ed_context_command"); // ********************************************************************************************************* class CAHR2Teleport : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { if (!ClientCfg.R2EDEnabled) return; if (R2::getEditor().getMode() != CEditor::EditionMode && R2::getEditor().getMode() != CEditor::DMMode && R2::getEditor().getMode() != CEditor::AnimationModeDm) return; CHECK_EDITOR // just forward to lua getEditor().callEnvMethod("activeTeleportTool", 0, 0); } }; REGISTER_ACTION_HANDLER(CAHR2Teleport, "r2ed_teleport"); // ********************************************************************************************************* class CAHR2Undo : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { // if an edit box currently has focus, then try undo on it first CGroupEditBox *eb = dynamic_cast(getEditor().getUI().getCaptureKeyboard()); if (eb && eb->undo()) { return; } CActionHistoric &historic = getEditor().getDMC().getActionHistoric(); if (historic.canUndo()) { getEditor().setCurrentTool(NULL); const ucstring *actionName = historic.getPreviousActionName(); nlassert(actionName); CLuaIHM::push(getEditor().getLua(), *actionName); historic.undo(); getEditor().callEnvMethod("onUndo", 1, 0); } else { getEditor().callEnvMethod("onCantUndo", 0, 0); } } }; REGISTER_ACTION_HANDLER(CAHR2Undo, "r2ed_undo"); // ********************************************************************************************************* class CAHR2Redo : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { // if an edit box currently has focus, then try redo on it first CGroupEditBox *eb = dynamic_cast(getEditor().getUI().getCaptureKeyboard()); if (eb && eb->redo()) { return; } CActionHistoric &historic = getEditor().getDMC().getActionHistoric(); if (historic.canRedo()) { getEditor().setCurrentTool(NULL); const ucstring *actionName = historic.getNextActionName(); nlassert(actionName); CLuaIHM::push(getEditor().getLua(), *actionName); historic.redo(); getEditor().callEnvMethod("onRedo", 1, 0); } else { getEditor().callEnvMethod("onCantRedo", 0, 0); } } }; REGISTER_ACTION_HANDLER(CAHR2Redo, "r2ed_redo"); // ********************************************************************************************************* // signal the server that a dm gift has begun, so that the server can take note of the target entity (the current target) class CAHR2DMGiftBegin : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { CBitMemStream out; if(GenericMsgHeaderMngr.pushNameToStream("DM_GIFT:BEGIN", out)) { NetMngr.push(out); } else nlwarning(" unknown message name 'DM_GIFT:BEGIN"); } }; REGISTER_ACTION_HANDLER(CAHR2DMGiftBegin, "r2ed_dm_gift_begin"); // ********************************************************************************************************* // signal the server that a dm gift has begun, so that the server can take note of the target entity (the current target) class CAHR2DMGiftValidate : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { CInterfaceManager *im = CInterfaceManager::getInstance(); CBitMemStream out; if(GenericMsgHeaderMngr.pushNameToStream("DM_GIFT:VALIDATE", out)) { for(uint k = 0; k < 8; ++k) { uint32 sheetId = 0; uint8 quantity = 0; CCDBNodeLeaf *sheetLeaf = im->getDbProp(toString("LOCAL:R2:DM_GIFT:%d:SHEET", (int) k)); if (sheetLeaf) sheetId = (uint32) sheetLeaf->getValue32(); CCDBNodeLeaf *quantityLeaf = im->getDbProp(toString("LOCAL:R2:DM_GIFT:%d:QUANTITY", (int) k)); if (quantityLeaf) quantity = (uint8) quantityLeaf->getValue8(); out.serial(sheetId); out.serial(quantity); } NetMngr.push(out); } else nlwarning(" unknown message name 'DM_GIFT:VALIDATE"); } }; REGISTER_ACTION_HANDLER(CAHR2DMGiftValidate, "r2ed_dm_gift_validate"); // ********************************************************************************************************* // freeze / unfreeze bot objects class CAHR2FreezeUnfreezeBotObjects : public IActionHandler { virtual void execute(CCtrlBase * /* pCaller */, const std::string &/* sParams */) { CInterfaceManager *im = CInterfaceManager::getInstance(); im->executeLuaScript("r2:freezeUnfreezeBotObjects()"); } }; REGISTER_ACTION_HANDLER(CAHR2FreezeUnfreezeBotObjects, "r2ed_freeze_unfreeze_botobjects"); } // R2 // ********************************************************************************************************* // COMMAND TO REINITIALIZE THE EDITOR : useful to check scripts error NLMISC_COMMAND(resetEditor, "reset R2 editor, reload all scripts (except ui xmls)", "") { if (!args.empty()) return false; R2::ResetWanted = true; R2::ReloadUIFlag = false; return true; } // ********************************************************************************************************* // COMMAND TO REINITIALIZE THE EDITOR : useful to check scripts error NLMISC_COMMAND(resetEditorAndReloadUI, "reset R2 editor, reload all scripts & xml (possibly preserving scenario)", "") { if (!args.empty()) return false; R2::ResetWanted = true; R2::ReloadUIFlag = true; return true; } // ********************************************************************************************************* // write scenario rtdata to a file for debugging or stress testing by simulator_service NLMISC_COMMAND(saveScenarioRtData, "save scenario RtData to file", "") { const R2::CObject *scenario = R2::getEditor().getDMC().getHighLevel(); if( !scenario ) { nlinfo("No current scenario -- can't save rtdata"); return false; } std::string fileName = "outpout"; if( args.empty() ) { // try to name file from title, then area+instance R2::CObject *description = scenario->findAttr("Description"); if( description ) { R2::CObject *name = description->findAttr("Name"); R2::CObject *title = 0; if (name) { title = name; } else { title = description->findAttr("Title"); //obsolete } R2::CObject *locationId = description->findAttr("LocationId"); R2::CObject *instanceId = description->findAttr("InstanceId"); if( title ) { string sTitle = title->toString(); if( !sTitle.empty() ) fileName = sTitle; } else if( locationId && instanceId ) fileName = "area" + locationId->toString() + "_" + instanceId->toString(); } } else // filename given { fileName = args[0]; } nlinfo("translating current scenario"); R2::CObject *pHighLevel = scenario->clone(); nlassert( pHighLevel ); R2::CObject *pRtData = R2::getEditor().getDMC().translateScenario( pHighLevel ); delete pHighLevel; if( !pRtData ) { nlwarning("Failed to translate high-level scenario into rtdata"); return false; } // binary string fullFileName = fileName; fullFileName += ".rt.bin"; nlinfo("writing rtdata to %s", fullFileName.c_str()); COFile output; output.open(fullFileName); R2::CObjectSerializerClient serializer( pRtData ); serializer.serial(output); output.flush(); output.close(); if( true ) // debug { // text fullFileName = fileName; fullFileName += ".rt.txt"; COFile output; //std::stringstream ss; std::string ss; output.open(fullFileName); pRtData->serialize(ss); //std::string str = ss.str(); output.serial(ss); output.flush(); output.close(); } return true; } NLMISC_COMMAND(dumpValidPositions, "dump valid position to create objects from the current camera viewpoint", "") { if (args.size() != 1) return false; try { COFile output(args[0]); CBitmap result; Driver->getBuffer(result); if (result.getWidth() == 0 || result.getHeight() == 0) return false; CMatrix camMatrix = MainCam.getMatrix(); NL3D::CFrustum fru = MainCam.getFrustum(); R2::CTool::CWorldViewRay wvr; wvr.Right = camMatrix.getI().normed(); wvr.Up = camMatrix.getK().normed(); wvr.OnMiniMap = false; wvr.Origin = camMatrix.getPos(); wvr.Valid = true; for (uint x = 0; x < result.getWidth(); ++x) { for (uint y = 0; y < result.getHeight(); ++y) { CVector ray(fru.Left + (fru.Right - fru.Left) * (x / (float) result.getWidth()), fru.Near, fru.Top + (fru.Bottom - fru.Top) * (y / (float) result.getHeight())); ray.normalize(); ray = camMatrix.mulVector(ray); CVector inter; wvr.Dir = ray; R2::CTool::TRayIntersectionType rit = R2::CTool::computeLandscapeRayIntersection(wvr, inter); CRGBA resultCol; switch(rit) { case R2::CTool::NoIntersection: resultCol = CRGBA(0, 0, 0, 0); break; case R2::CTool::ValidPacsPos: resultCol = CRGBA(0, 0, 255, 100); break; default: resultCol = CRGBA(255, 0, 0, 100); break; } CRGBA *dest = ((CRGBA *) &result.getPixels(0)[0]) + x + y * result.getWidth(); dest->blendFromui(*dest, resultCol, resultCol.A); } } result.writeTGA(output, 32, false); } catch(...) { } return true; } /* // ********************************************************************************************************* NLMISC_COMMAND(resetScenario, "reset R2 editor, reload all scripts & xml", "") { if (!args.empty()) return false; R2::ResetScenarioWanted = true; return true; } // ********************************************************************************************************* NLMISC_COMMAND(reloadScenario, "reset R2 editor, reload all scripts & xml (possibly preserving scenario)", "") { if (!args.empty()) return false; // R2::ReloadScenarioWanted = true; return true; } */ bool IsInRingMode() { return R2::getEditor().getMode() != R2::CEditor::NotInitialized; }