diff options
-rw-r--r-- | example/scripts/global_events.lua | 17 | ||||
-rw-r--r-- | src/game-server/character.cpp | 26 | ||||
-rw-r--r-- | src/game-server/character.h | 12 | ||||
-rw-r--r-- | src/scripting/lua.cpp | 26 | ||||
-rw-r--r-- | src/scripting/luascript.cpp | 20 | ||||
-rw-r--r-- | src/scripting/luascript.h | 4 | ||||
-rw-r--r-- | src/scripting/script.cpp | 2 | ||||
-rw-r--r-- | src/scripting/script.h | 33 | ||||
-rw-r--r-- | src/scripting/scriptmanager.cpp | 13 | ||||
-rw-r--r-- | src/scripting/scriptmanager.h | 5 |
10 files changed, 125 insertions, 33 deletions
diff --git a/example/scripts/global_events.lua b/example/scripts/global_events.lua index fe4175be..c320d163 100644 --- a/example/scripts/global_events.lua +++ b/example/scripts/global_events.lua @@ -10,24 +10,29 @@ --]] - --- This function is called when the hit points of a character reach zero. -function on_chr_death(ch) +-- Register the callback that is called when the hit points of a character +-- reach zero. +mana.on_character_death(function(ch) mana.being_say(ch, "Noooooo!!!") -end +end) -- This function is called when the player clicks on the OK button after the -- death message appeared. It should be used to implement the respawn -- mechanic (for example: warp the character to the respawn location and -- bring HP above zero in some way) -function on_chr_death_accept(ch) +mana.on_character_death_accept(function(ch) -- restores to full hp mana.being_heal(ch) -- restores 1 hp (in case you want to be less nice) -- mana.being_heal(ch, 1) -- warp the character to the respawn location mana.chr_warp(ch, 1, 815, 100) -end +end) + + +-- +-- TODO: All of the below functions are not implemented yet! +-- -- This function is called after chr_death_accept. The difference is that -- it is called in the context of the map the character is spawned on after diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index 819da1c1..b5051764 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -52,6 +52,24 @@ const float Character::EXPCURVE_FACTOR = 10.0f; const float Character::LEVEL_SKILL_PRECEDENCE_FACTOR = 0.75f; const float Character::EXP_LEVEL_FLEXIBILITY = 1.0f; +Script::Ref Character::mDeathCallback = Script::NoRef; +Script::Ref Character::mDeathAcceptedCallback = Script::NoRef; + +static bool executeCallback(Script::Ref function, Character *character) +{ + if (function == Script::NoRef) + return false; + + Script *script = ScriptManager::currentState(); + script->setMap(character->getMap()); + script->prepare(function); + script->push(character); + script->execute(); + script->setMap(0); + return true; +} + + Character::Character(MessageIn &msg): Being(OBJECT_CHARACTER), mClient(NULL), @@ -197,7 +215,7 @@ void Character::perform() void Character::died() { Being::died(); - ScriptManager::executeGlobalEventFunction("on_chr_death", this); + executeCallback(mDeathCallback, this); } void Character::respawn() @@ -214,11 +232,11 @@ void Character::respawn() // Reset target mTarget = NULL; - // Execute respawn script - if (ScriptManager::executeGlobalEventFunction("on_chr_death_accept", this)) + // Execute respawn callback when set + if (executeCallback(mDeathAcceptedCallback, this)) return; - // Script-controlled respawning didn't work - fall back to hardcoded logic. + // No script respawn callback set - fall back to hardcoded logic mAttributes[ATTR_HP].setBase(mAttributes[ATTR_MAX_HP].getModifiedAttribute()); updateDerivedAttributes(ATTR_HP); // Warp back to spawn point. diff --git a/src/game-server/character.h b/src/game-server/character.h index 7777217d..76498421 100644 --- a/src/game-server/character.h +++ b/src/game-server/character.h @@ -26,9 +26,10 @@ #include <vector> #include "common/defines.h" -#include "common/manaserv_protocol.h" #include "common/inventorydata.h" +#include "common/manaserv_protocol.h" #include "game-server/being.h" +#include "scripting/script.h" #include "utils/logger.h" class BuySell; @@ -361,6 +362,12 @@ class Character : public Being bool isConnected() const { return mConnected; } + static void setDeathCallback(Script *script) + { script->assignCallback(mDeathCallback); } + + static void setDeathAcceptedCallback(Script *script) + { script->assignCallback(mDeathAcceptedCallback); } + protected: /** * Gets the way the actor blocks pathfinding for other objects @@ -458,6 +465,9 @@ class Character : public Being TransactionType mTransaction; /**< Trade/buy/sell action the character is involved in. */ std::map<int, int> mKillCount; /**< How many monsters the character has slain of each type */ + static Script::Ref mDeathCallback; + static Script::Ref mDeathAcceptedCallback; + // Set as a friend, but still a lot of redundant accessors. FIXME. template< class T > friend void serializeCharacterData(const T &data, MessageOut &msg); diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 9aec6b3f..7a94ce24 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -74,6 +74,30 @@ static Script *getScript(lua_State *s) /** + * mana.on_character_death( function(Character*) ): void + * Sets a listener function to the character death event. + */ +static int on_character_death(lua_State *s) +{ + luaL_checktype(s, 1, LUA_TFUNCTION); + Character::setDeathCallback(getScript(s)); + return 0; +} + +/** + * mana.on_character_death_accept( function(Character*) ): void + * Sets a listener function that is called when the player clicks on the OK + * button after the death message appeared. It should be used to implement the + * respawn mechanic. + */ +static int on_character_death_accept(lua_State *s) +{ + luaL_checktype(s, 1, LUA_TFUNCTION); + Character::setDeathAcceptedCallback(getScript(s)); + return 0; +} + +/** * mana.npc_message(NPC*, Character*, string): void * Callback for sending a NPC_MESSAGE. */ @@ -2564,6 +2588,8 @@ LuaScript::LuaScript(): // Put the callback functions in the scripting environment. static luaL_Reg const callbacks[] = { + { "on_character_death", &on_character_death }, + { "on_character_death_accept", &on_character_death_accept }, { "npc_create", &npc_create }, { "npc_message", &npc_message }, { "npc_choice", &npc_choice }, diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp index c487aa67..605302e6 100644 --- a/src/scripting/luascript.cpp +++ b/src/scripting/luascript.cpp @@ -35,6 +35,15 @@ LuaScript::~LuaScript() lua_close(mState); } +void LuaScript::prepare(Ref function) +{ + assert(nbArgs == -1); + lua_rawgeti(mState, LUA_REGISTRYINDEX, function); + assert(lua_isfunction(mState, -1)); + nbArgs = 0; + mCurFunction = "<callback>"; // We don't know the function name +} + void LuaScript::prepare(const std::string &name) { assert(nbArgs == -1); @@ -108,6 +117,17 @@ int LuaScript::execute() mCurFunction.clear(); } +void LuaScript::assignCallback(Script::Ref &function) +{ + assert(lua_isfunction(mState, -1)); + + // If there is already a callback set, replace it + if (function != NoRef) + luaL_unref(mState, LUA_REGISTRYINDEX, function); + + function = luaL_ref(mState, LUA_REGISTRYINDEX); +} + void LuaScript::load(const char *prog, const char *name) { int res = luaL_loadbuffer(mState, prog, std::strlen(prog), name); diff --git a/src/scripting/luascript.h b/src/scripting/luascript.h index 0d59703c..6f2bceee 100644 --- a/src/scripting/luascript.h +++ b/src/scripting/luascript.h @@ -44,6 +44,8 @@ class LuaScript : public Script void load(const char *prog, const char *name); + void prepare(Ref function); + void prepare(const std::string &); void push(int); @@ -56,6 +58,8 @@ class LuaScript : public Script int execute(); + void assignCallback(Ref &function); + static void getQuestCallback(Character *, const std::string &, const std::string &, void *); diff --git a/src/scripting/script.cpp b/src/scripting/script.cpp index db44bd57..66720842 100644 --- a/src/scripting/script.cpp +++ b/src/scripting/script.cpp @@ -34,6 +34,8 @@ typedef std::map< std::string, Script::Factory > Engines; static Engines *engines = NULL; +Script::Ref Script::NoRef = -1; + Script::Script(): mMap(NULL), mEventListener(&scriptEventDispatch) diff --git a/src/scripting/script.h b/src/scripting/script.h index bd143114..b475a0f0 100644 --- a/src/scripting/script.h +++ b/src/scripting/script.h @@ -21,11 +21,12 @@ #ifndef SCRIPTING_SCRIPT_H #define SCRIPTING_SCRIPT_H -#include <string> - -#include "game-server/character.h" +#include "common/inventorydata.h" #include "game-server/eventlistener.h" +#include <list> +#include <string> + class MapComposite; class Thing; @@ -35,7 +36,9 @@ class Thing; class Script { public: - + /** + * Defines a function that creates a Script instance. + */ typedef Script *(*Factory)(); /** @@ -48,6 +51,15 @@ class Script */ static Script *create(const std::string &engine); + /** + * A reference to a script object. It's just an integer, but the + * typedef makes the purpose of the variable clear. + * + * Variables of this type should be initialized to Script::NoRef. + */ + typedef int Ref; + static Ref NoRef; + Script(); virtual ~Script() {} @@ -81,6 +93,12 @@ class Script virtual void update(); /** + * Prepares a call to the referenced function. + * Only one function can be prepared at once. + */ + virtual void prepare(Ref function) = 0; + + /** * Prepares a call to the given function. * Only one function can be prepared at once. */ @@ -117,6 +135,13 @@ class Script virtual int execute() = 0; /** + * Assigns the current callback to the given \a function. + * + * Where the callback exactly comes from is up to the script engine. + */ + virtual void assignCallback(Ref &function) = 0; + + /** * Sets associated map. */ void setMap(MapComposite *m) diff --git a/src/scripting/scriptmanager.cpp b/src/scripting/scriptmanager.cpp index 52515699..d58379f6 100644 --- a/src/scripting/scriptmanager.cpp +++ b/src/scripting/scriptmanager.cpp @@ -49,19 +49,6 @@ Script *ScriptManager::currentState() // TODO: Have some generic event mechanism rather than calling global functions -bool ScriptManager::executeGlobalEventFunction(const std::string &function, Being* obj) -{ - bool isScriptHandled = false; - _currentState->setMap(obj->getMap()); - _currentState->prepare(function); - _currentState->push(obj); - _currentState->execute(); - _currentState->setMap(NULL); - isScriptHandled = true; // TODO: don't set to true when execution failed - return isScriptHandled; -} - - void ScriptManager::addDataToSpecial(int id, Special *special) { /* currently only gets the recharge cost. diff --git a/src/scripting/scriptmanager.h b/src/scripting/scriptmanager.h index 7560c343..ade76c19 100644 --- a/src/scripting/scriptmanager.h +++ b/src/scripting/scriptmanager.h @@ -56,11 +56,6 @@ bool loadMainScript(const std::string &file); */ Script *currentState(); - -/** - * Runs a global function from the global event script file - */ -bool executeGlobalEventFunction(const std::string &function, Being *obj); void addDataToSpecial(int specialId, Special *special); bool performSpecialAction(int specialId, Being *caster); bool performCraft(Being *crafter, const std::list<InventoryItem> &recipe); |