From cf69b4b007e602576208d1beff9852c8bf1acbe9 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Sun, 10 Jun 2012 15:11:11 +0200 Subject: Added script binds for getting attack information New binds: - Damage: - id - skill - base - delta - cth - element - type - truestrike - range - Attack: - priority - cooldown - warmup - global_cooldown - damage - on_attack - MonsterClass: - attacks - ItemClass: - attacks --- src/scripting/lua.cpp | 153 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 7 deletions(-) (limited to 'src/scripting/lua.cpp') diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 5e0f93c9..6285f382 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -1001,7 +1001,7 @@ static int being_damage(lua_State *s) dmg.delta = luaL_checkint(s, 3); dmg.cth = luaL_checkint(s, 4); dmg.type = (DamageType)luaL_checkint(s, 5); - dmg.element = luaL_checkint(s, 6); + dmg.element = (Element)luaL_checkint(s, 6); Being *source = 0; if (lua_gettop(s) >= 7) { @@ -1269,13 +1269,11 @@ static int monster_class_on_damage(lua_State *s) return 0; } -static int monster_class_on(lua_State *s) +static int monster_class_attacks(lua_State *s) { MonsterClass *monsterClass = LuaMonsterClass::check(s, 1); - const char *event = luaL_checkstring(s, 2); - luaL_checktype(s, 3, LUA_TFUNCTION); - monsterClass->setEventCallback(event, getScript(s)); - return 0; + pushSTLContainer(s, monsterClass->getAttackInfos()); + return 1; } /** @@ -2170,6 +2168,14 @@ static int item_class_on(lua_State *s) return 0; } +static int item_class_attacks(lua_State *s) +{ + ItemClass *itemClass = LuaItemClass::check(s, 1); + std::vector attacks = itemClass->getAttackInfos(); + pushSTLContainer(s, attacks); + return 1; +} + /** * drop_item(int x, int y, int id || string name[, int number]): bool * Creates an item stack on the floor. @@ -2435,6 +2441,113 @@ static int specialinfo_on_use(lua_State *s) return 0; } +static int attack_get_priority(lua_State *s) +{ + AttackInfo *attack = LuaAttackInfo::check(s, 1); + lua_pushinteger(s, attack->getPriority()); + return 1; +} + +static int attack_get_cooldowntime(lua_State *s) +{ + AttackInfo *attack = LuaAttackInfo::check(s, 1); + lua_pushinteger(s, attack->getCooldownTime()); + return 1; +} + +static int attack_get_warmuptime(lua_State *s) +{ + AttackInfo *attack = LuaAttackInfo::check(s, 1); + lua_pushinteger(s, attack->getWarmupTime()); + return 1; +} + +static int attack_get_reusetime(lua_State *s) +{ + AttackInfo *attack = LuaAttackInfo::check(s, 1); + lua_pushinteger(s, attack->getReuseTime()); + return 1; +} + +static int attack_get_damage(lua_State *s) +{ + AttackInfo *attack = LuaAttackInfo::check(s, 1); + LuaDamage::push(s, &attack->getDamage()); + return 1; +} + +static int attack_on_attack(lua_State *s) +{ + AttackInfo *attack = LuaAttackInfo::check(s, 1); + luaL_checktype(s, 2, LUA_TFUNCTION); + attack->setCallback(getScript(s)); + return 0; +} + +static int damage_get_id(lua_State *s) +{ + Damage *damage = LuaDamage::check(s, 1); + lua_pushinteger(s, damage->id); + return 1; +} + + +static int damage_get_skill(lua_State *s) +{ + Damage *damage = LuaDamage::check(s, 1); + lua_pushinteger(s, damage->skill); + return 1; +} + +static int damage_get_base(lua_State *s) +{ + Damage *damage = LuaDamage::check(s, 1); + lua_pushinteger(s, damage->base); + return 1; +} + +static int damage_get_delta(lua_State *s) +{ + Damage *damage = LuaDamage::check(s, 1); + lua_pushinteger(s, damage->delta); + return 1; +} + +static int damage_get_cth(lua_State *s) +{ + Damage *damage = LuaDamage::check(s, 1); + lua_pushinteger(s, damage->cth); + return 1; +} + +static int damage_get_element(lua_State *s) +{ + Damage *damage = LuaDamage::check(s, 1); + lua_pushinteger(s, damage->element); + return 1; +} + +static int damage_get_type(lua_State *s) +{ + Damage *damage = LuaDamage::check(s, 1); + lua_pushinteger(s, damage->type); + return 1; +} + +static int damage_is_truestrike(lua_State *s) +{ + Damage *damage = LuaDamage::check(s, 1); + lua_pushboolean(s, damage->trueStrike); + return 1; +} + +static int damage_get_range(lua_State *s) +{ + Damage *damage = LuaDamage::check(s, 1); + lua_pushinteger(s, damage->range); + return 1; +} + static int require_loader(lua_State *s) { // Add .lua extension (maybe only do this when it doesn't have it already) @@ -2590,8 +2703,32 @@ LuaScript::LuaScript(): luaL_register(mRootState, NULL, callbacks); lua_pop(mRootState, 1); // pop the globals table + static luaL_Reg const members_AttackInfo[] = { + { "priority", &attack_get_priority }, + { "cooldowntime", &attack_get_cooldowntime }, + { "warmuptime", &attack_get_warmuptime }, + { "reusetime", &attack_get_reusetime }, + { "damage", &attack_get_damage }, + { "on_attack", &attack_on_attack }, + { NULL, NULL } + }; + + static luaL_Reg const members_Damage[] = { + { "id", &damage_get_id }, + { "skill", &damage_get_skill }, + { "base", &damage_get_base }, + { "delta", &damage_get_delta }, + { "cth", &damage_get_cth }, + { "element", &damage_get_element }, + { "type", &damage_get_type }, + { "is_truestrike", &damage_is_truestrike }, + { "range", &damage_get_range }, + { NULL, NULL } + }; + static luaL_Reg const members_ItemClass[] = { { "on", &item_class_on }, + { "attacks", &item_class_attacks }, { NULL, NULL } }; @@ -2606,7 +2743,7 @@ LuaScript::LuaScript(): static luaL_Reg const members_MonsterClass[] = { { "on_update", &monster_class_on_update }, { "on_damage", &monster_class_on_damage }, - { "on", &monster_class_on }, + { "attacks", &monster_class_attacks }, { NULL, NULL } }; @@ -2625,6 +2762,8 @@ LuaScript::LuaScript(): { NULL, NULL} }; + LuaAttackInfo::registerType(mRootState, "Attack", members_AttackInfo); + LuaDamage::registerType(mRootState, "Damage", members_Damage); LuaItemClass::registerType(mRootState, "ItemClass", members_ItemClass); LuaMapObject::registerType(mRootState, "MapObject", members_MapObject); LuaMonsterClass::registerType(mRootState, "MonsterClass", members_MonsterClass); -- cgit v1.2.3-70-g09d2 From 2594084de6e163f15a1516d64cf413de5b0d899f Mon Sep 17 00:00:00 2001 From: Thorbjørn Lindeijer Date: Tue, 8 Jan 2013 18:03:02 +0100 Subject: Added support for Lua 5.2 Should still work against Lua 5.1 as well. --- src/scripting/lua.cpp | 9 +++++++++ src/scripting/luascript.cpp | 4 ++++ src/scripting/luautil.h | 11 ++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) (limited to 'src/scripting/lua.cpp') diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 6285f382..c0aedfdd 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -2575,7 +2575,11 @@ LuaScript::LuaScript(): // Register package loader that goes through the resource manager // table.insert(package.loaders, 2, require_loader) lua_getglobal(mRootState, "package"); +#if LUA_VERSION_NUM < 502 lua_getfield(mRootState, -1, "loaders"); +#else + lua_getfield(mRootState, -1, "searchers"); +#endif lua_pushcfunction(mRootState, require_loader); lua_rawseti(mRootState, -2, 2); lua_pop(mRootState, 2); @@ -2699,8 +2703,13 @@ LuaScript::LuaScript(): { "get_special_info", &get_special_info }, { NULL, NULL } }; +#if LUA_VERSION_NUM < 502 lua_pushvalue(mRootState, LUA_GLOBALSINDEX); luaL_register(mRootState, NULL, callbacks); +#else + lua_pushglobaltable(mRootState); + luaL_setfuncs(mRootState, callbacks, 0); +#endif lua_pop(mRootState, 1); // pop the globals table static luaL_Reg const members_AttackInfo[] = { diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp index 97b03e1d..a3dcb1d4 100644 --- a/src/scripting/luascript.cpp +++ b/src/scripting/luascript.cpp @@ -151,7 +151,11 @@ bool LuaScript::resume() setMap(mCurrentThread->mMap); const int tmpNbArgs = nbArgs; nbArgs = -1; +#if LUA_VERSION_NUM < 502 int result = lua_resume(mCurrentState, tmpNbArgs); +#else + int result = lua_resume(mCurrentState, NULL, tmpNbArgs); +#endif setMap(0); if (result == 0) // Thread is done diff --git a/src/scripting/luautil.h b/src/scripting/luautil.h index 1ff2ab8d..0d2a981d 100644 --- a/src/scripting/luautil.h +++ b/src/scripting/luautil.h @@ -103,7 +103,12 @@ public: luaL_newmetatable(s, mTypeName); // metatable lua_pushstring(s, "__index"); // metatable, "__index" - luaL_register(s, typeName, members); // metatable, "__index", {} + lua_createtable(s, 0, 0); // metatable, "__index", {} +#if LUA_VERSION_NUM < 502 + luaL_register(s, NULL, members); +#else + luaL_setfuncs(s, members, 0); +#endif lua_rawset(s, -3); // metatable lua_pop(s, 1); // -empty- } @@ -125,8 +130,12 @@ public: void *userData = lua_newuserdata(s, sizeof(T*)); * static_cast(userData) = object; +#if LUA_VERSION_NUM < 502 luaL_newmetatable(s, mTypeName); lua_setmetatable(s, -2); +#else + luaL_setmetatable(s, mTypeName); +#endif UserDataCache::insert(s, object); } -- cgit v1.2.3-70-g09d2 From 562b403a66a6a96d883620b27455564d50e3d49b Mon Sep 17 00:00:00 2001 From: Thorbjørn Lindeijer Date: Wed, 9 Jan 2013 11:47:16 +0100 Subject: Micro-optimizations for pushing strings to Lua Use lua_pushliteral and lua_pushlstring instead of lua_pushstring, which avoids Lua having to determine the length of the string. --- src/scripting/lua.cpp | 56 +++++++++++++++++++-------------------------- src/scripting/luascript.cpp | 2 +- src/scripting/luautil.cpp | 4 ++-- src/scripting/luautil.h | 4 ++-- 4 files changed, 29 insertions(+), 37 deletions(-) (limited to 'src/scripting/lua.cpp') diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index c0aedfdd..545e365e 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -458,8 +458,6 @@ static int chr_get_inventory(lua_State *s) int firstTableStackPosition = lua_gettop(s); int tableIndex = 1; - std::string itemName = ""; - for (InventoryData::const_iterator it = invData.begin(), it_end = invData.end(); it != it_end; ++it) { @@ -470,20 +468,19 @@ static int chr_get_inventory(lua_State *s) lua_createtable(s, 0, 4); int subTableStackPosition = lua_gettop(s); // Stores the item info in it. - lua_pushstring(s, "slot"); + lua_pushliteral(s, "slot"); lua_pushinteger(s, it->first); // The slot id lua_settable(s, subTableStackPosition); - lua_pushstring(s, "id"); + lua_pushliteral(s, "id"); lua_pushinteger(s, it->second.itemId); lua_settable(s, subTableStackPosition); - lua_pushstring(s, "name"); - itemName = itemManager->getItem(it->second.itemId)->getName(); - lua_pushstring(s, itemName.c_str()); + lua_pushliteral(s, "name"); + push(s, itemManager->getItem(it->second.itemId)->getName()); lua_settable(s, subTableStackPosition); - lua_pushstring(s, "amount"); + lua_pushliteral(s, "amount"); lua_pushinteger(s, it->second.amount); lua_settable(s, subTableStackPosition); @@ -517,7 +514,6 @@ static int chr_get_equipment(lua_State *s) int firstTableStackPosition = lua_gettop(s); int tableIndex = 1; - std::string itemName; std::set itemInstances; for (EquipData::const_iterator it = equipData.begin(), @@ -534,17 +530,16 @@ static int chr_get_equipment(lua_State *s) lua_createtable(s, 0, 3); int subTableStackPosition = lua_gettop(s); // Stores the item info in it. - lua_pushstring(s, "slot"); + lua_pushliteral(s, "slot"); lua_pushinteger(s, it->first); // The slot id lua_settable(s, subTableStackPosition); - lua_pushstring(s, "id"); + lua_pushliteral(s, "id"); lua_pushinteger(s, it->second.itemId); lua_settable(s, subTableStackPosition); - lua_pushstring(s, "name"); - itemName = itemManager->getItem(it->second.itemId)->getName(); - lua_pushstring(s, itemName.c_str()); + lua_pushliteral(s, "name"); + push(s, itemManager->getItem(it->second.itemId)->getName()); lua_settable(s, subTableStackPosition); // Add the sub-table as value of the main one. @@ -1127,7 +1122,7 @@ static int being_remove_attribute_modifier(lua_State *s) static int being_get_name(lua_State *s) { Being *being = checkBeing(s, 1); - lua_pushstring(s, being->getName().c_str()); + push(s, being->getName()); return 1; } @@ -1309,7 +1304,7 @@ static int monster_get_name(lua_State *s) luaL_error(s, "monster_get_name called with unknown monster id."); return 0; } - lua_pushstring(s, spec->getName().c_str()); + push(s, spec->getName()); return 1; } @@ -1386,7 +1381,7 @@ static int chr_get_quest(lua_State *s) bool res = getQuestVar(q, name, value); if (res) { - lua_pushstring(s, value.c_str()); + push(s, value); return 1; } QuestCallback *f = new QuestThreadCallback(&LuaScript::getQuestCallback, @@ -1453,7 +1448,7 @@ static int chr_try_get_quest(lua_State *s) std::string value; bool res = getQuestVar(q, name, value); if (res) - lua_pushstring(s, value.c_str()); + push(s, value); else lua_pushnil(s); return 1; @@ -1469,9 +1464,8 @@ static int getvar_map(lua_State *s) luaL_argcheck(s, name[0] != 0, 1, "empty variable name"); MapComposite *map = checkCurrentMap(s); - std::string value = map->getVariable(name); - lua_pushstring(s, value.c_str()); + push(s, map->getVariable(name)); return 1; } @@ -1500,8 +1494,7 @@ static int getvar_world(lua_State *s) const char *name = luaL_checkstring(s, 1); luaL_argcheck(s, name[0] != 0, 1, "empty variable name"); - std::string value = GameState::getVariable(name); - lua_pushstring(s, value.c_str()); + push(s, GameState::getVariable(name)); return 1; } @@ -2128,8 +2121,7 @@ static int get_map_property(lua_State *s) const char *property = luaL_checkstring(s, 1); Map *map = checkCurrentMap(s)->getMap(); - std::string value = map->getProperty(property); - lua_pushstring(s, value.c_str()); + push(s, map->getProperty(property)); return 1; } @@ -2210,7 +2202,7 @@ static int item_get_name(lua_State *s) luaL_error(s, "item_get_name called with unknown item id."); return 0; } - lua_pushstring(s, it->getName().c_str()); + push(s, it->getName()); return 1; } @@ -2314,7 +2306,7 @@ static int map_object_get_property(lua_State *s) std::string property = obj->getProperty(key); if (!property.empty()) { - lua_pushstring(s, property.c_str()); + push(s, property); return 1; } else @@ -2346,7 +2338,7 @@ static int map_object_get_bounds(lua_State *s) static int map_object_get_name(lua_State *s) { MapObject *obj = LuaMapObject::check(s, 1); - lua_pushstring(s, obj->getName().c_str()); + push(s, obj->getName()); return 1; } @@ -2357,7 +2349,7 @@ static int map_object_get_name(lua_State *s) static int map_object_get_type(lua_State *s) { MapObject *obj = LuaMapObject::check(s, 1); - lua_pushstring(s, obj->getType().c_str()); + push(s, obj->getType()); return 1; } @@ -2398,7 +2390,7 @@ static int get_special_info(lua_State *s) static int specialinfo_get_name(lua_State *s) { SpecialManager::SpecialInfo *info = LuaSpecialInfo::check(s, 1); - lua_pushstring(s, info->name.c_str()); + push(s, info->name); return 1; } @@ -2419,7 +2411,7 @@ static int specialinfo_is_rechargeable(lua_State *s) static int specialinfo_get_category(lua_State *s) { SpecialManager::SpecialInfo *info = LuaSpecialInfo::check(s, 1); - lua_pushstring(s, info->setName.c_str()); + push(s, info->setName); return 1; } @@ -2559,7 +2551,7 @@ static int require_loader(lua_State *s) if (!path.empty()) luaL_loadfile(s, path.c_str()); else - lua_pushstring(s, "File not found"); + lua_pushliteral(s, "File not found"); return 1; } @@ -2573,7 +2565,7 @@ LuaScript::LuaScript(): luaL_openlibs(mRootState); // Register package loader that goes through the resource manager - // table.insert(package.loaders, 2, require_loader) + // package.loaders[2] = require_loader lua_getglobal(mRootState, "package"); #if LUA_VERSION_NUM < 502 lua_getfield(mRootState, -1, "loaders"); diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp index a3dcb1d4..4104bc89 100644 --- a/src/scripting/luascript.cpp +++ b/src/scripting/luascript.cpp @@ -82,7 +82,7 @@ void LuaScript::push(int v) void LuaScript::push(const std::string &v) { assert(nbArgs >= 0); - lua_pushstring(mCurrentState, v.c_str()); + lua_pushlstring(mCurrentState, v.c_str(), v.length()); ++nbArgs; } diff --git a/src/scripting/luautil.cpp b/src/scripting/luautil.cpp index 3de23c77..f7b39e29 100644 --- a/src/scripting/luautil.cpp +++ b/src/scripting/luautil.cpp @@ -86,8 +86,8 @@ void UserDataCache::insert(lua_State *s, void *object) // The metatable that makes the values in the table above weak lua_newtable(s); // UD, Cache, {} - lua_pushstring(s, "__mode"); - lua_pushstring(s, "v"); + lua_pushliteral(s, "__mode"); + lua_pushliteral(s, "v"); lua_rawset(s, -3); // UD, Cache, { __mode = "v" } lua_setmetatable(s, -2); // UD, Cache diff --git a/src/scripting/luautil.h b/src/scripting/luautil.h index 0d2a981d..8e380d4e 100644 --- a/src/scripting/luautil.h +++ b/src/scripting/luautil.h @@ -102,7 +102,7 @@ public: mTypeName = typeName; luaL_newmetatable(s, mTypeName); // metatable - lua_pushstring(s, "__index"); // metatable, "__index" + lua_pushliteral(s, "__index"); // metatable, "__index" lua_createtable(s, 0, 0); // metatable, "__index", {} #if LUA_VERSION_NUM < 502 luaL_register(s, NULL, members); @@ -197,7 +197,7 @@ inline void push(lua_State *s, int val) inline void push(lua_State *s, const std::string &val) { - lua_pushstring(s, val.c_str()); + lua_pushlstring(s, val.c_str(), val.length()); } inline void push(lua_State *s, Entity *val) -- cgit v1.2.3-70-g09d2 From 0b339e547b77f80d6e81313bfb38249ce8995553 Mon Sep 17 00:00:00 2001 From: Thorbjørn Lindeijer Date: Sun, 27 May 2012 23:26:19 +0200 Subject: Replaced EventListener with signals based on libsigc++ This replaces the rather hard to understand event dispatcher with a probably even harder to understand templated library, but fortunately we can rely on the available documentation. Hopefully it will also help with the readability of our code and with adding additional signals to other classes. Added libsigc++ to README and Travis CI configuration. Reviewed-by: Erik Schilling --- .travis.yml | 2 +- CMake/Modules/FindSigC++.cmake | 34 +++++++++++++ CMake/Modules/LibFindMacros.cmake | 99 ++++++++++++++++++++++++++++++++++++ README | 13 ++--- src/CMakeLists.txt | 5 +- src/game-server/being.cpp | 27 ++++------ src/game-server/being.h | 10 ++-- src/game-server/character.cpp | 10 +--- src/game-server/character.h | 2 + src/game-server/entity.cpp | 47 ------------------ src/game-server/entity.h | 37 ++++---------- src/game-server/eventlistener.h | 102 -------------------------------------- src/game-server/monster.cpp | 83 ++++++++++++++++--------------- src/game-server/monster.h | 23 ++++++--- src/game-server/quest.cpp | 78 +++++++++++++++-------------- src/game-server/spawnarea.cpp | 20 ++------ src/game-server/spawnarea.h | 2 - src/game-server/state.cpp | 6 +-- src/scripting/lua.cpp | 6 ++- src/scripting/luascript.cpp | 2 - src/scripting/luascript.h | 2 + src/scripting/script.cpp | 3 +- src/scripting/script.h | 22 ++------ 23 files changed, 291 insertions(+), 344 deletions(-) create mode 100644 CMake/Modules/FindSigC++.cmake create mode 100644 CMake/Modules/LibFindMacros.cmake delete mode 100644 src/game-server/eventlistener.h (limited to 'src/scripting/lua.cpp') diff --git a/.travis.yml b/.travis.yml index bbebefd2..b56d187b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,5 @@ compiler: - clang before_install: - sudo apt-get update -qq - - sudo apt-get install -qq sqlite3 cmake make gcc libxml2-dev liblua5.1-0-dev libphysfs-dev libsqlite3-dev libsdl-gfx1.2-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-net1.2-dev libsdl-pango-dev libsdl-ttf2.0-dev libsdl1.2-dev libguichan-dev libphysfs-dev libenet1a libcurl4-openssl-dev libcurl3 zlib1g-dev libmysqlclient-dev + - sudo apt-get install -qq sqlite3 cmake make gcc libxml2-dev liblua5.1-0-dev libphysfs-dev libsqlite3-dev libsdl-gfx1.2-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-net1.2-dev libsdl-pango-dev libsdl-ttf2.0-dev libsdl1.2-dev libguichan-dev libphysfs-dev libenet1a libcurl4-openssl-dev libcurl3 zlib1g-dev libmysqlclient-dev libsigc++-2.0-dev script: cmake . -DWITH_MYSQL=ON && make diff --git a/CMake/Modules/FindSigC++.cmake b/CMake/Modules/FindSigC++.cmake new file mode 100644 index 00000000..35687d5b --- /dev/null +++ b/CMake/Modules/FindSigC++.cmake @@ -0,0 +1,34 @@ +# - Try to find SigC++-2.0 +# Once done, this will define +# +# SigC++_FOUND - system has SigC++ +# SigC++_INCLUDE_DIRS - the SigC++ include directories +# SigC++_LIBRARIES - link these to use SigC++ + +include(LibFindMacros) + +# Use pkg-config to get hints about paths +libfind_pkg_check_modules(SigC++_PKGCONF sigc++-2.0) + +# Main include dir +find_path(SigC++_INCLUDE_DIR + NAMES sigc++/sigc++.h + PATHS ${SigC++_PKGCONF_INCLUDE_DIRS} + PATH_SUFFIXES sigc++-2.0 +) + +# Glib-related libraries also use a separate config header, which is in lib dir +find_path(SigC++Config_INCLUDE_DIR + NAMES sigc++config.h + PATHS ${SigC++_PKGCONF_INCLUDE_DIRS} /usr + PATH_SUFFIXES lib/sigc++-2.0/include +) + +libfind_library(SigC++ sigc 2.0) + +# Set the include dir variables and the libraries and let libfind_process do +# the rest. NOTE: Singular variables for this library, plural for libraries +# this this lib depends on. +set(SigC++_PROCESS_INCLUDES SigC++_INCLUDE_DIR SigC++Config_INCLUDE_DIR) +set(SigC++_PROCESS_LIBS SigC++_LIBRARY) +libfind_process(SigC++) diff --git a/CMake/Modules/LibFindMacros.cmake b/CMake/Modules/LibFindMacros.cmake new file mode 100644 index 00000000..69975c51 --- /dev/null +++ b/CMake/Modules/LibFindMacros.cmake @@ -0,0 +1,99 @@ +# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments +# used for the current package. For this to work, the first parameter must be the +# prefix of the current package, then the prefix of the new package etc, which are +# passed to find_package. +macro (libfind_package PREFIX) + set (LIBFIND_PACKAGE_ARGS ${ARGN}) + if (${PREFIX}_FIND_QUIETLY) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) + endif (${PREFIX}_FIND_QUIETLY) + if (${PREFIX}_FIND_REQUIRED) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) + endif (${PREFIX}_FIND_REQUIRED) + find_package(${LIBFIND_PACKAGE_ARGS}) +endmacro (libfind_package) + +# CMake developers made the UsePkgConfig system deprecated in the same release (2.6) +# where they added pkg_check_modules. Consequently I need to support both in my scripts +# to avoid those deprecated warnings. Here's a helper that does just that. +# Works identically to pkg_check_modules, except that no checks are needed prior to use. +macro (libfind_pkg_check_modules PREFIX PKGNAME) + if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + include(UsePkgConfig) + pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) + else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(${PREFIX} ${PKGNAME}) + endif (PKG_CONFIG_FOUND) + endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) +endmacro (libfind_pkg_check_modules) + +# Do the final processing once the paths have been detected. +# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain +# all the variables, each of which contain one include directory. +# Ditto for ${PREFIX}_PROCESS_LIBS and library files. +# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. +# Also handles errors in case library detection was required, etc. +macro (libfind_process PREFIX) + # Skip processing if already processed during this run + if (NOT ${PREFIX}_FOUND) + # Start with the assumption that the library was found + set (${PREFIX}_FOUND TRUE) + + # Process all includes and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_INCLUDES}) + if (${i}) + set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Process all libraries and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_LIBS}) + if (${i}) + set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Print message and/or exit on fatal error + if (${PREFIX}_FOUND) + if (NOT ${PREFIX}_FIND_QUIETLY) + message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") + endif (NOT ${PREFIX}_FIND_QUIETLY) + else (${PREFIX}_FOUND) + if (${PREFIX}_FIND_REQUIRED) + foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) + message("${i}=${${i}}") + endforeach (i) + message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") + endif (${PREFIX}_FIND_REQUIRED) + endif (${PREFIX}_FOUND) + endif (NOT ${PREFIX}_FOUND) +endmacro (libfind_process) + +macro(libfind_library PREFIX basename) + set(TMP "") + if(MSVC80) + set(TMP -vc80) + endif(MSVC80) + if(MSVC90) + set(TMP -vc90) + endif(MSVC90) + set(${PREFIX}_LIBNAMES ${basename}${TMP}) + if(${ARGC} GREATER 2) + set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) + string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) + set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) + endif(${ARGC} GREATER 2) + find_library(${PREFIX}_LIBRARY + NAMES ${${PREFIX}_LIBNAMES} + PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} + ) +endmacro(libfind_library) + diff --git a/README b/README index 06e56ce6..34faafcf 100644 --- a/README +++ b/README @@ -6,15 +6,16 @@ COMPILATION Before trying to compile, make sure all the dependencies are installed. For each dependency the Ubuntu package name is listed as well as the website. - * libxml2 (libxml2-dev) - http://xmlsoft.org/ - * Lua (liblua5.1-0-dev) - http://lua.org/ - * PhysFS (libphysfs-dev) - http://icculus.org/physfs/ - * SQLite 3 (libsqlite3-dev) - http://sqlite.org/ - * zlib (zlib1g-dev) - http://zlib.net/ + * libsigc++ 2.0 (libsigc++-2.0-dev) - http://libsigc.sourceforge.net/ + * libxml2 (libxml2-dev) - http://xmlsoft.org/ + * Lua (liblua5.1-0-dev) - http://lua.org/ + * PhysFS (libphysfs-dev) - http://icculus.org/physfs/ + * SQLite 3 (libsqlite3-dev) - http://sqlite.org/ + * zlib (zlib1g-dev) - http://zlib.net/ Optional dependencies: - * MySQL (libmysqlclient-dev) - http://dev.mysql.com/ + * MySQL (libmysqlclient-dev) - http://dev.mysql.com/ (replaces the SQLite 3 depency) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a4b38fc..73f969f7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ FIND_PACKAGE(LibXml2 REQUIRED) FIND_PACKAGE(PhysFS REQUIRED) FIND_PACKAGE(ZLIB REQUIRED) +FIND_PACKAGE(SigC++ REQUIRED) IF (CMAKE_COMPILER_IS_GNUCXX) # Help getting compilation warnings @@ -93,6 +94,8 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ${PHYSFS_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR} + ${SigC++_INCLUDE_DIR} + ${SigC++Config_INCLUDE_DIR} ) # Fix some stuff that gets not hidden by mainline modules @@ -214,7 +217,6 @@ SET(SRCS_MANASERVGAME game-server/effect.cpp game-server/entity.h game-server/entity.cpp - game-server/eventlistener.h game-server/gamehandler.h game-server/gamehandler.cpp game-server/inventory.h @@ -315,6 +317,7 @@ FOREACH(program ${PROGRAMS}) ${PHYSFS_LIBRARY} ${LIBXML2_LIBRARIES} ${ZLIB_LIBRARIES} + ${SigC++_LIBRARIES} ${OPTIONAL_LIBRARIES} ${EXTRA_LIBRARIES}) INSTALL(TARGETS ${program} RUNTIME DESTINATION ${PKG_BINDIR}) diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp index ea7540ca..9ce19d76 100644 --- a/src/game-server/being.cpp +++ b/src/game-server/being.cpp @@ -27,7 +27,6 @@ #include "game-server/attributemanager.h" #include "game-server/character.h" #include "game-server/collisiondetection.h" -#include "game-server/eventlistener.h" #include "game-server/mapcomposite.h" #include "game-server/effect.h" #include "game-server/skillmanager.h" @@ -58,6 +57,9 @@ Being::Being(EntityType type): Attribute(*it1->second))); } + + signal_inserted.connect(sigc::mem_fun(this, &Being::inserted)); + // TODO: Way to define default base values? // Should this be handled by the virtual modifiedAttribute? // URGENT either way @@ -177,14 +179,7 @@ void Being::died() // reset target mTarget = NULL; - for (Listeners::iterator i = mListeners.begin(), - i_end = mListeners.end(); i != i_end;) - { - const EventListener &l = **i; - ++i; // In case the listener removes itself from the list on the fly. - if (l.dispatch->died) - l.dispatch->died(&l, this); - } + signal_died.emit(this); } void Being::processAttacks() @@ -512,6 +507,11 @@ bool Being::removeModifier(unsigned attr, double value, unsigned layer, return ret; } +void Being::setGender(BeingGender gender) +{ + mGender = gender; +} + void Being::setAttribute(unsigned id, double value) { AttributeMap::iterator ret = mAttributes.find(id); @@ -743,20 +743,13 @@ void Being::update() processAttacks(); } -void Being::inserted() +void Being::inserted(Entity *) { - Actor::inserted(); - // Reset the old position, since after insertion it is important that it is // in sync with the zone that we're currently present in. mOld = getPosition(); } -void Being::setGender(BeingGender gender) -{ - mGender = gender; -} - void Being::processAttack(Attack &attack) { performAttack(mTarget, attack.getAttackInfo()->getDamage()); diff --git a/src/game-server/being.h b/src/game-server/being.h index 0083be1f..a08df018 100644 --- a/src/game-server/being.h +++ b/src/game-server/being.h @@ -297,10 +297,7 @@ class Being : public Actor void setTarget(Being *target) { mTarget = target; } - /** - * Overridden in order to reset the old position upon insertion. - */ - virtual void inserted(); + sigc::signal signal_died; protected: /** @@ -332,6 +329,11 @@ class Being : public Actor Being(const Being &rhs); Being &operator=(const Being &rhs); + /** + * Connected to signal_inserted to reset the old position. + */ + void inserted(Entity *); + Path mPath; BeingDirection mDirection; /**< Facing direction. */ diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index 15ba940a..a9777991 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -24,7 +24,6 @@ #include "game-server/accountconnection.h" #include "game-server/attributemanager.h" #include "game-server/buysell.h" -#include "game-server/eventlistener.h" #include "game-server/inventory.h" #include "game-server/item.h" #include "game-server/itemmanager.h" @@ -792,14 +791,7 @@ void Character::disconnected() else GameState::remove(this); - for (Listeners::iterator i = mListeners.begin(), - i_end = mListeners.end(); i != i_end;) - { - const EventListener &l = **i; - ++i; // In case the listener removes itself from the list on the fly. - if (l.dispatch->disconnected) - l.dispatch->disconnected(&l, this); - } + signal_disconnected.emit(this); } bool Character::takeSpecial(int id) diff --git a/src/game-server/character.h b/src/game-server/character.h index 5f3d3a64..d225081b 100644 --- a/src/game-server/character.h +++ b/src/game-server/character.h @@ -428,6 +428,8 @@ class Character : public Being virtual void removeAttack(AttackInfo *attackInfo); + sigc::signal signal_disconnected; + protected: /** * Gets the way the actor blocks pathfinding for other objects diff --git a/src/game-server/entity.cpp b/src/game-server/entity.cpp index 671ef5e6..6cb61e58 100644 --- a/src/game-server/entity.cpp +++ b/src/game-server/entity.cpp @@ -18,51 +18,4 @@ * along with The Mana Server. If not, see . */ -#include - #include "game-server/entity.h" - -#include "game-server/eventlistener.h" - -Entity::~Entity() -{ - /* As another object will stop listening and call removeListener when it is - deleted, the following assertion ensures that all the calls to - removeListener have been performed will this object was still alive. It - is not strictly necessary, as there are cases where no removal is - performed (e.g. ~SpawnArea). But this is rather exceptional, so keep the - assertion to catch all the other forgotten calls to removeListener. */ - assert(mListeners.empty()); -} - -void Entity::addListener(const EventListener *l) -{ - mListeners.insert(l); -} - -void Entity::removeListener(const EventListener *l) -{ - mListeners.erase(l); -} - -void Entity::inserted() -{ - for (Listeners::iterator i = mListeners.begin(), - i_end = mListeners.end(); i != i_end;) - { - const EventListener &l = **i; - ++i; // In case the listener removes itself from the list on the fly. - if (l.dispatch->inserted) l.dispatch->inserted(&l, this); - } -} - -void Entity::removed() -{ - for (Listeners::iterator i = mListeners.begin(), - i_end = mListeners.end(); i != i_end;) - { - const EventListener &l = **i; - ++i; // In case the listener removes itself from the list on the fly. - if (l.dispatch->removed) l.dispatch->removed(&l, this); - } -} diff --git a/src/game-server/entity.h b/src/game-server/entity.h index 652db7c7..91f13699 100644 --- a/src/game-server/entity.h +++ b/src/game-server/entity.h @@ -23,18 +23,20 @@ #include "common/manaserv_protocol.h" -using namespace ManaServ; - #include -class EventListener; +#include +#include + +using namespace ManaServ; + class MapComposite; /** * Base class for in-game objects. Knows only its type and the map it resides * on. Provides listeners. */ -class Entity +class Entity : public sigc::trackable { public: Entity(EntityType type, MapComposite *map = 0) @@ -42,7 +44,7 @@ class Entity mType(type) {} - virtual ~Entity(); + virtual ~Entity() {} /** * Gets type of this entity. @@ -88,29 +90,8 @@ class Entity virtual void setMap(MapComposite *map) { mMap = map; } - /** - * Adds a new listener. - */ - void addListener(const EventListener *); - - /** - * Removes an existing listener. - */ - void removeListener(const EventListener *); - - /** - * Calls all the "inserted" listeners. - */ - virtual void inserted(); - - /** - * Calls all the "removed" listeners. - */ - virtual void removed(); - - protected: - typedef std::set< const EventListener * > Listeners; - Listeners mListeners; /**< List of event listeners. */ + sigc::signal signal_inserted; + sigc::signal signal_removed; private: MapComposite *mMap; /**< Map the entity is on */ diff --git a/src/game-server/eventlistener.h b/src/game-server/eventlistener.h deleted file mode 100644 index 53d92077..00000000 --- a/src/game-server/eventlistener.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * The Mana Server - * Copyright (C) 2007-2010 The Mana World Development Team - * - * This file is part of The Mana Server. - * - * The Mana Server is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * The Mana Server 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Mana Server. If not, see . - */ - -#ifndef GAMESERVER_EVENTLISTENER_H -#define GAMESERVER_EVENTLISTENER_H - -class Entity; -class Being; -class Character; - -struct EventDispatch; - -/** - * Pointer to a dispatch table. - */ -struct EventListener -{ - const EventDispatch *dispatch; - EventListener(const EventDispatch *d): dispatch(d) {} -}; - -/** - * Dispatch table for event notification. - */ -struct EventDispatch -{ - /** - * Called just after something is inserted in a map. - */ - void (*inserted)(const EventListener *, Entity *); - - /** - * Called just before something is removed from a map. - */ - void (*removed)(const EventListener *, Entity *); - - /** - * Called just after a being has died. - */ - void (*died)(const EventListener *, Being *); - - /** - * Called just before a character is deleted. - */ - void (*disconnected)(const EventListener *, Character *); - - /** - * Initializes dispatch methods as missing. - */ - EventDispatch(): - inserted(0), removed(0), died(0), disconnected(0) - {} -}; - -/** - * Helper for using member functions as dispatch methods. The 3-level structure - * is due to default template parameter not being allowed on functions yet. - * Conceptually, this helper takes two parameters: the name of the member - * variable pointing to the dispatch table and the name of the member function - * to call on dispatch. With these two parameters, it creates a dispatch - * method. When called, this free function forwards the call to the member - * function. - * Pseudo-syntax for getting a dispatch method: - * &EventListenerFactory< _, DispatchPointerName >::create< _, MemberFunctionName >::function - * See the start of the spawnarea.cpp file for a complete example. - */ -template< class T, EventListener T::*D > -struct EventListenerFactory -{ - template< class U, void (T::*F)(U *), class V = U > - struct create - { - static void function(const EventListener *d, V *u) - { - /* Get the address of the T object by substracting the offset of D - from the pointer d. */ - T *t = (T *)((char *)d - - ((char *)&(((T *)42)->*D) - (char *)&(*(T *)42))); - // Then call the method F of this T object. - (t->*F)(u); - } - }; -}; - -#endif diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp index cdfe063a..330e3f4f 100644 --- a/src/game-server/monster.cpp +++ b/src/game-server/monster.cpp @@ -35,18 +35,6 @@ #include -struct MonsterTargetEventDispatch: EventDispatch -{ - MonsterTargetEventDispatch() - { - typedef EventListenerFactory Factory; - removed = &Factory::create< Entity, &Monster::forgetTarget >::function; - died = &Factory::create::function; - } -}; - -static MonsterTargetEventDispatch monsterTargetEventDispatch; - MonsterClass::~MonsterClass() { for (std::vector::iterator it = mAttacks.begin(), @@ -67,7 +55,6 @@ double MonsterClass::getVulnerability(Element element) const Monster::Monster(MonsterClass *specy): Being(OBJECT_MONSTER), mSpecy(specy), - mTargetListener(&monsterTargetEventDispatch), mOwner(NULL) { LOG_DEBUG("Monster spawned! (id: " << mSpecy->getId() << ")."); @@ -137,12 +124,6 @@ Monster::Monster(MonsterClass *specy): Monster::~Monster() { - // Remove death listeners. - for (std::map::iterator i = mAnger.begin(), - i_end = mAnger.end(); i != i_end; ++i) - { - i->first->removeListener(&mTargetListener); - } } void Monster::update() @@ -229,11 +210,11 @@ void Monster::refreshTarget() // Determine how much we hate the target int targetPriority = 0; - std::map >::iterator angerIterator; - angerIterator = mAnger.find(target); + std::map::iterator angerIterator = mAnger.find(target); if (angerIterator != mAnger.end()) { - targetPriority = angerIterator->second; + const AggressionInfo &aggressionInfo = angerIterator->second; + targetPriority = aggressionInfo.anger; } else if (mSpecy->isAggressive()) { @@ -359,11 +340,15 @@ int Monster::calculatePositionPriority(Point position, int targetPriority) } } -void Monster::forgetTarget(Entity *t) +void Monster::forgetTarget(Entity *entity) { - Being *b = static_cast< Being * >(t); + Being *b = static_cast< Being * >(entity); + { + AggressionInfo &aggressionInfo = mAnger[b]; + aggressionInfo.removedConnection.disconnect(); + aggressionInfo.diedConnection.disconnect(); + } mAnger.erase(b); - b->removeListener(&mTargetListener); if (b->getType() == OBJECT_CHARACTER) { @@ -375,20 +360,42 @@ void Monster::forgetTarget(Entity *t) void Monster::changeAnger(Actor *target, int amount) { - if (target && (target->getType() == OBJECT_MONSTER - || target->getType() == OBJECT_CHARACTER)) + const EntityType type = target->getType(); + if (type != OBJECT_MONSTER && type != OBJECT_CHARACTER) + return; + + Being *being = static_cast< Being * >(target); + + if (mAnger.find(being) != mAnger.end()) { - Being *t = static_cast< Being * >(target); - if (mAnger.find(t) != mAnger.end()) - { - mAnger[t] += amount; - } - else - { - mAnger[t] = amount; - t->addListener(&mTargetListener); - } + mAnger[being].anger += amount; + } + else + { + AggressionInfo &aggressionInfo = mAnger[being]; + aggressionInfo.anger = amount; + + // Forget target either when it's removed or died, whichever + // happens first. + aggressionInfo.removedConnection = + being->signal_removed.connect(sigc::mem_fun(this, &Monster::forgetTarget)); + aggressionInfo.diedConnection = + being->signal_died.connect(sigc::mem_fun(this, &Monster::forgetTarget)); + } +} + +std::map Monster::getAngerList() const +{ + std::map result; + std::map::const_iterator i, i_end; + + for (i = mAnger.begin(), i_end = mAnger.end(); i != i_end; ++i) + { + const AggressionInfo &aggressionInfo = i->second; + result.insert(std::make_pair(i->first, aggressionInfo.anger)); } + + return result; } int Monster::damage(Actor *source, const Damage &damage) @@ -399,9 +406,7 @@ int Monster::damage(Actor *source, const Damage &damage) newDamage.delta = newDamage.delta * factor; int HPLoss = Being::damage(source, newDamage); if (source) - { changeAnger(source, HPLoss); - } if (HPLoss && source && source->getType() == OBJECT_CHARACTER) { diff --git a/src/game-server/monster.h b/src/game-server/monster.h index 73fec6aa..3313345c 100644 --- a/src/game-server/monster.h +++ b/src/game-server/monster.h @@ -22,7 +22,6 @@ #define MONSTER_H #include "game-server/being.h" -#include "game-server/eventlistener.h" #include "common/defines.h" #include "scripting/script.h" #include "utils/string.h" @@ -31,6 +30,9 @@ #include #include +#include + +class Character; class ItemClass; class Script; @@ -315,8 +317,7 @@ class Monster : public Being */ void changeAnger(Actor *target, int amount); - const std::map &getAngerList() const - { return mAnger; } + std::map getAngerList() const; /** * Calls the damage function in Being and updates the aggro list @@ -326,7 +327,7 @@ class Monster : public Being /** * Removes a being from the anger list. */ - void forgetTarget(Entity *being); + void forgetTarget(Entity *entity); /** * Called when an attribute modifier is changed. @@ -351,10 +352,16 @@ class Monster : public Being MonsterClass *mSpecy; /** Aggression towards other beings. */ - std::map mAnger; - - /** Listener for updating the anger list. */ - EventListener mTargetListener; + struct AggressionInfo { + AggressionInfo() + : anger(0) + {} + + int anger; + sigc::connection removedConnection; + sigc::connection diedConnection; + }; + std::map mAnger; /** * Character who currently owns this monster (killsteal protection). diff --git a/src/game-server/quest.cpp b/src/game-server/quest.cpp index 4d659227..c28a4213 100644 --- a/src/game-server/quest.cpp +++ b/src/game-server/quest.cpp @@ -18,24 +18,27 @@ * along with The Mana Server. If not, see . */ -#include -#include -#include -#include - #include "game-server/quest.h" #include "game-server/accountconnection.h" #include "game-server/character.h" -#include "game-server/eventlistener.h" #include "utils/logger.h" +#include +#include +#include +#include + +#include + typedef std::list< QuestCallback * > QuestCallbacks; typedef std::map< std::string, QuestCallbacks > PendingVariables; struct PendingQuest { Character *character; + sigc::connection removedConnection; + sigc::connection disconnectedConnection; PendingVariables variables; }; @@ -72,22 +75,6 @@ void setQuestVar(Character *ch, const std::string &name, accountHandler->updateCharacterVar(ch, name, value); } -/** - * Listener for deleting related quests when a character disappears. - */ -struct QuestDeathListener: EventDispatch -{ - static void partialRemove(const EventListener *, Entity *); - - static void fullRemove(const EventListener *, Character *); - - QuestDeathListener() - { - removed = &partialRemove; - disconnected = &fullRemove; - } -}; - void QuestRefCallback::triggerCallback(Character *ch, const std::string &value) const { @@ -103,10 +90,7 @@ void QuestRefCallback::triggerCallback(Character *ch, s->execute(); } -static QuestDeathListener questDeathDummy; -static EventListener questDeathListener(&questDeathDummy); - -void QuestDeathListener::partialRemove(const EventListener *, Entity *t) +static void partialRemove(Entity *t) { int id = static_cast< Character * >(t)->getDatabaseID(); PendingVariables &variables = pendingQuests[id].variables; @@ -119,11 +103,18 @@ void QuestDeathListener::partialRemove(const EventListener *, Entity *t) // The listener is kept in case a fullRemove is needed later. } -void QuestDeathListener::fullRemove(const EventListener *, Character *ch) +static void fullRemove(Character *ch) { - ch->removeListener(&questDeathListener); + int id = ch->getDatabaseID(); + + { + PendingQuest &pendingQuest = pendingQuests[id]; + pendingQuest.removedConnection.disconnect(); + pendingQuest.disconnectedConnection.disconnect(); + } + // Remove anything related to this character. - pendingQuests.erase(ch->getDatabaseID()); + pendingQuests.erase(id); } void recoverQuestVar(Character *ch, const std::string &name, @@ -134,11 +125,19 @@ void recoverQuestVar(Character *ch, const std::string &name, PendingQuests::iterator i = pendingQuests.lower_bound(id); if (i == pendingQuests.end() || i->first != id) { - i = pendingQuests.insert(i, std::make_pair(id, PendingQuest())); - i->second.character = ch; - /* Register a listener, because we cannot afford to get invalid - pointers, when we finally recover the variable. */ - ch->addListener(&questDeathListener); + PendingQuest pendingQuest; + pendingQuest.character = ch; + + /* Connect to removed and disconnected signals, because we cannot + * afford to get invalid pointers, when we finally recover the + * variable. + */ + pendingQuest.removedConnection = + ch->signal_removed.connect(sigc::ptr_fun(partialRemove)); + pendingQuest.disconnectedConnection = + ch->signal_disconnected.connect(sigc::ptr_fun(fullRemove)); + + i = pendingQuests.insert(i, std::make_pair(id, pendingQuest)); } i->second.variables[name].push_back(f); accountHandler->requestCharacterVar(ch, name); @@ -149,12 +148,14 @@ void recoveredQuestVar(int id, const std::string &value) { PendingQuests::iterator i = pendingQuests.find(id); - if (i == pendingQuests.end()) return; + if (i == pendingQuests.end()) + return; - Character *ch = i->second.character; - ch->removeListener(&questDeathListener); + PendingQuest &pendingQuest = i->second; + pendingQuest.removedConnection.disconnect(); + pendingQuest.disconnectedConnection.disconnect(); - PendingVariables &variables = i->second.variables; + PendingVariables &variables = pendingQuest.variables; PendingVariables::iterator j = variables.find(name); if (j == variables.end()) { @@ -162,6 +163,7 @@ void recoveredQuestVar(int id, return; } + Character *ch = pendingQuest.character; ch->questCache[name] = value; // Call the registered callbacks. diff --git a/src/game-server/spawnarea.cpp b/src/game-server/spawnarea.cpp index 155d4c0d..73dca6b2 100644 --- a/src/game-server/spawnarea.cpp +++ b/src/game-server/spawnarea.cpp @@ -25,18 +25,6 @@ #include "game-server/state.h" #include "utils/logger.h" -struct SpawnAreaEventDispatch : EventDispatch -{ - SpawnAreaEventDispatch() - { - typedef EventListenerFactory< SpawnArea, &SpawnArea::mSpawnedListener > - Factory; - removed = &Factory::create< Entity, &SpawnArea::decrease >::function; - } -}; - -static SpawnAreaEventDispatch spawnAreaEventDispatch; - SpawnArea::SpawnArea(MapComposite *map, MonsterClass *specy, const Rectangle &zone, @@ -44,7 +32,6 @@ SpawnArea::SpawnArea(MapComposite *map, int spawnRate): Entity(OBJECT_OTHER, map), mSpecy(specy), - mSpawnedListener(&spawnAreaEventDispatch), mZone(zone), mMaxBeings(maxBeings), mSpawnRate(spawnRate), @@ -102,7 +89,9 @@ void SpawnArea::update() if (c) { - being->addListener(&mSpawnedListener); + being->signal_removed.connect( + sigc::mem_fun(this, &SpawnArea::decrease)); + being->setMap(map); being->setPosition(position); being->clearDestination(); @@ -125,8 +114,7 @@ void SpawnArea::update() } } -void SpawnArea::decrease(Entity *t) +void SpawnArea::decrease(Entity *) { --mNumBeings; - t->removeListener(&mSpawnedListener); } diff --git a/src/game-server/spawnarea.h b/src/game-server/spawnarea.h index cc0642f1..628c072e 100644 --- a/src/game-server/spawnarea.h +++ b/src/game-server/spawnarea.h @@ -21,7 +21,6 @@ #ifndef SPAWNAREA_H #define SPAWNAREA_H -#include "game-server/eventlistener.h" #include "game-server/entity.h" #include "utils/point.h" @@ -47,7 +46,6 @@ class SpawnArea : public Entity private: MonsterClass *mSpecy; /**< Specy of monster that spawns in this area. */ - EventListener mSpawnedListener; /**< Tracking of spawned monsters. */ Rectangle mZone; int mMaxBeings; /**< Maximum population of this area. */ int mSpawnRate; /**< Number of beings spawning per minute. */ diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp index ede94f86..a688caca 100644 --- a/src/game-server/state.cpp +++ b/src/game-server/state.cpp @@ -498,7 +498,7 @@ bool GameState::insert(Entity *ptr) if (!ptr->isVisible()) { map->insert(ptr); - ptr->inserted(); + ptr->signal_inserted.emit(ptr); return true; } @@ -523,7 +523,7 @@ bool GameState::insert(Entity *ptr) return false; } - obj->inserted(); + obj->signal_inserted.emit(obj); // DEBUG INFO switch (obj->getType()) @@ -596,7 +596,7 @@ void GameState::remove(Entity *ptr) MapComposite *map = ptr->getMap(); int visualRange = Configuration::getValue("game_visualRange", 448); - ptr->removed(); + ptr->signal_removed.emit(ptr); // DEBUG INFO switch (ptr->getType()) diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 545e365e..880216d2 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -1709,7 +1709,11 @@ static int chr_get_post(lua_State *s) static int being_register(lua_State *s) { Being *being = checkBeing(s, 1); - being->addListener(getScript(s)->getScriptListener()); + Script *script = getScript(s); + + being->signal_died.connect(sigc::mem_fun(script, &Script::processDeathEvent)); + being->signal_removed.connect(sigc::mem_fun(script, &Script::processRemoveEvent)); + return 0; } diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp index 4104bc89..f4ea39ac 100644 --- a/src/scripting/luascript.cpp +++ b/src/scripting/luascript.cpp @@ -265,8 +265,6 @@ void LuaScript::processRemoveEvent(Entity *entity) // being. This might be very interesting for scripting quests. execute(); } - - entity->removeListener(getScriptListener()); } /** diff --git a/src/scripting/luascript.h b/src/scripting/luascript.h index 7631d982..9515bf0e 100644 --- a/src/scripting/luascript.h +++ b/src/scripting/luascript.h @@ -28,6 +28,8 @@ extern "C" { #include "scripting/script.h" +class Character; + /** * Implementation of the Script class for Lua. */ diff --git a/src/scripting/script.cpp b/src/scripting/script.cpp index 3e299461..63ab7ff4 100644 --- a/src/scripting/script.cpp +++ b/src/scripting/script.cpp @@ -40,8 +40,7 @@ Script::Ref Script::mUpdateCallback; Script::Script(): mCurrentThread(0), - mMap(0), - mEventListener(&scriptEventDispatch) + mMap(0) {} Script::~Script() diff --git a/src/scripting/script.h b/src/scripting/script.h index 238bc34c..8dee23a9 100644 --- a/src/scripting/script.h +++ b/src/scripting/script.h @@ -23,19 +23,21 @@ #include "common/inventorydata.h" #include "common/manaserv_protocol.h" -#include "game-server/eventlistener.h" #include #include #include +#include + +class Being; class MapComposite; class Entity; /** * Abstract interface for calling functions written in an external language. */ -class Script +class Script : public sigc::trackable { public: /** @@ -216,9 +218,6 @@ class Script MapComposite *getMap() const { return mMap; } - EventListener *getScriptListener() - { return &mEventListener; } - virtual void processDeathEvent(Being *entity) = 0; virtual void processRemoveEvent(Entity *entity) = 0; @@ -235,7 +234,6 @@ class Script private: MapComposite *mMap; - EventListener mEventListener; /**< Tracking of being deaths. */ std::vector mThreads; static Ref mCreateNpcDelayedCallback; @@ -245,16 +243,4 @@ class Script friend class Thread; }; -struct ScriptEventDispatch: EventDispatch -{ - ScriptEventDispatch() - { - typedef EventListenerFactory< Script, &Script::mEventListener > Factory; - died = &Factory::create< Being, &Script::processDeathEvent >::function; - removed = &Factory::create< Entity, &Script::processRemoveEvent >::function; - } -}; - -static ScriptEventDispatch scriptEventDispatch; - #endif // SCRIPTING_SCRIPT_H -- cgit v1.2.3-70-g09d2 From f7d71e54c0b5fdaa9e8aa1e69e615b9f80d124e4 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Mon, 4 Feb 2013 16:19:21 +0100 Subject: Moved attribute (re)calculation to the scripts This introduces two callbacks: - on_update_derived_attribute -> Called to recalculate other derived attributes. - on_recalculate_base_attribute -> Called to recalculate a base attribute (only called for characters. However the function passed as callback can be useful for recalculating the derived attributes as well) Monsters no longer block recalculation of attributes except HP and Speed. I saw no sense to keep this. Fixed constant value in libmana-constants.lua Dropped bool type of the recalculation functions. It would be difficult to keep it while pushing all to the script engine and it was unused anyway. All in all this adds a LOT more flexibillity to projects since they can now adapt all attributes in the way they want. --- example/scripts/attributes.lua | 74 ++++++++++++++++++++++++++++++++ example/scripts/main.lua | 1 + scripts/lua/libmana-constants.lua | 4 +- src/game-server/being.cpp | 84 +++++++++++++++++------------------- src/game-server/being.h | 14 +++++- src/game-server/character.cpp | 89 ++++++--------------------------------- src/game-server/character.h | 10 ++--- src/game-server/monster.cpp | 41 ------------------ src/game-server/monster.h | 8 ---- src/scripting/lua.cpp | 26 ++++++++++++ 10 files changed, 174 insertions(+), 177 deletions(-) create mode 100644 example/scripts/attributes.lua (limited to 'src/scripting/lua.cpp') diff --git a/example/scripts/attributes.lua b/example/scripts/attributes.lua new file mode 100644 index 00000000..7009a730 --- /dev/null +++ b/example/scripts/attributes.lua @@ -0,0 +1,74 @@ +--[[ + + This file demonstrates how attributes are getting calculated and how they can + be linked to each other. + + See http://doc.manasource.org/attributes.xml for more info. + +--]] + +local function recalculate_base_attribute(being, attribute) + local old_base = being_get_base_attribute(being, attribute) + local new_base = old_base + if attribute == ATTR_ACCURACY then + -- Provisional + new_base = being_get_modified_attribute(being, ATTR_DEX) + elseif attribute == ATTR_DEFENSE then + new_base = 0.3 * being_get_modified_attribute(being, ATTR_VIT) + elseif attribute == ATTR_DODGE then + -- Provisional + new_base = being_get_modified_attribute(being, ATTR_AGI) + elseif attribute == ATTR_MAGIC_DODGE then + -- TODO + new_base = 1 + elseif attribute == ATTR_MAGIC_DEFENSE then + -- TODO + new_base = 0 + elseif attribute == ATTR_BONUS_ASPD then + -- TODO + new_base = 0 + elseif attribute == ATTR_HP_REGEN then + local hp_per_sec = being_get_modified_attribute(being, ATTR_VIT) * 0.05 + new_base = hp_per_sec * TICKS_PER_HP_REGENERATION / 10 + elseif attribute == ATTR_HP then + local hp = being_get_modified_attribute(being, ATTR_HP) + local max_hp = being_get_modified_attribute(being, ATTR_MAX_HP) + + if hp > max_hp then + new_base = new_base - hp - max_hp + end + elseif attribute == ATTR_MAX_HP then + local vit = being_get_modified_attribute(being, ATTR_VIT) + new_base = (vit + 3) * (vit + 20) * 0.125 + elseif attribute == ATTR_MOVE_SPEED_TPS then + -- Provisional + new_base = 3.0 + being_get_modified_attribute(being, ATTR_AGI) * 0.08 + elseif attribute == ATTR_INV_CAPACITY then + -- Provisional + new_base = 2000 + being_get_modified_attribute(being, ATTR_STR) * 180 + end + + if new_base ~= old_base then + being_set_base_attribute(being, attribute, new_base) + end + +end + +local function update_derived_attributes(being, attribute) + if attribute == ATTR_STR then + recalculate_base_attribute(ATTR_INV_CAPACITY, being) + elseif attribute == ATTR_AGI then + recalculate_base_attribute(ATTR_DODGE, being) + elseif attribute == ATTR_VIT then + recalculate_base_attribute(ATTR_MAX_HP, being) + recalculate_base_attribute(ATTR_HP_REGEN, being) + recalculate_base_attribute(ATTR_DEFENSE, being) + elseif attribute == ATTR_INT then + -- unimplemented + elseif attribute == ATTR_WIL then + -- unimplemented + end +end + +on_recalculate_base_attribute(recalculate_base_attribute) +on_update_derived_attribute(update_derived_attributes) diff --git a/example/scripts/main.lua b/example/scripts/main.lua index 6d8207e7..34183850 100644 --- a/example/scripts/main.lua +++ b/example/scripts/main.lua @@ -9,6 +9,7 @@ require "scripts/global_events" require "scripts/special_actions" require "scripts/crafting" +require "scripts/attributes" require "scripts/items/candy" require "scripts/monster/testmonster" diff --git a/scripts/lua/libmana-constants.lua b/scripts/lua/libmana-constants.lua index 9638588f..a07df4c5 100644 --- a/scripts/lua/libmana-constants.lua +++ b/scripts/lua/libmana-constants.lua @@ -55,6 +55,8 @@ ELEMENT_METAL = 6; ELEMENT_WOOD = 7; ELEMENT_ICE = 8; +TICKS_PER_HP_REGENERATION = 100 + -- Core attributes Id ATTR_STR = 1; ATTR_AGI = 2; @@ -64,7 +66,7 @@ ATTR_DEX = 5; ATTR_WIL = 6; -- Derived attributes Id -ATTR_ACCURACY = 6; +ATTR_ACCURACY = 7; ATTR_DEFENSE = 8; ATTR_DODGE = 9; diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp index d7df2df9..2c334b37 100644 --- a/src/game-server/being.cpp +++ b/src/game-server/being.cpp @@ -34,6 +34,11 @@ #include "game-server/statusmanager.h" #include "utils/logger.h" #include "utils/speedconv.h" +#include "scripting/scriptmanager.h" + + +Script::Ref Being::mRecalculateDerivedAttributesCallback; +Script::Ref Being::mRecalculateBaseAttributeCallback; Being::Being(EntityType type): Actor(type), @@ -574,75 +579,64 @@ void Being::setModAttribute(unsigned, double) return; } -bool Being::recalculateBaseAttribute(unsigned attr) +void Being::recalculateBaseAttribute(unsigned attr) { LOG_DEBUG("Being: Received update attribute recalculation request for " << attr << "."); if (!mAttributes.count(attr)) { LOG_DEBUG("Being::recalculateBaseAttribute: " << attr << " not found!"); - return false; + return; } - double newBase = getAttribute(attr); - switch (attr) + // Handle speed conversion inside the engine + if (attr == ATTR_MOVE_SPEED_RAW) { - case ATTR_HP_REGEN: - { - double hpPerSec = getModifiedAttribute(ATTR_VIT) * 0.05; - newBase = (hpPerSec * TICKS_PER_HP_REGENERATION / 10); - } - break; - case ATTR_HP: - double diff; - if ((diff = getModifiedAttribute(ATTR_HP) - - getModifiedAttribute(ATTR_MAX_HP)) > 0) - newBase -= diff; - break; - case ATTR_MAX_HP: - newBase = ((getModifiedAttribute(ATTR_VIT) + 3) - * (getModifiedAttribute(ATTR_VIT) + 20)) * 0.125; - break; - case ATTR_MOVE_SPEED_TPS: - newBase = 3.0 + getModifiedAttribute(ATTR_AGI) * 0.08; // Provisional. - break; - case ATTR_MOVE_SPEED_RAW: - newBase = utils::tpsToRawSpeed( - getModifiedAttribute(ATTR_MOVE_SPEED_TPS)); - break; - case ATTR_INV_CAPACITY: - // Provisional - newBase = 2000.0 + getModifiedAttribute(ATTR_STR) * 180.0; - break; - } - if (newBase != getAttribute(attr)) - { - setAttribute(attr, newBase); - return true; + double newBase = utils::tpsToRawSpeed( + getModifiedAttribute(ATTR_MOVE_SPEED_TPS)); + if (newBase != getAttribute(attr)) + setAttribute(attr, newBase); + return; } - LOG_DEBUG("Being: No changes to sync for attribute '" << attr << "'."); - return false; + + if (!mRecalculateBaseAttributeCallback.isValid()) + return; + + Script *script = ScriptManager::currentState(); + script->setMap(getMap()); + script->prepare(mRecalculateBaseAttributeCallback); + script->push(this); + script->push(attr); + script->execute(); } void Being::updateDerivedAttributes(unsigned attr) { LOG_DEBUG("Being: Updating derived attribute(s) of: " << attr); + + // Handle default actions before handing over to the script engine switch (attr) { case ATTR_MAX_HP: - updateDerivedAttributes(ATTR_HP); case ATTR_HP: raiseUpdateFlags(UPDATEFLAG_HEALTHCHANGE); break; case ATTR_MOVE_SPEED_TPS: - if (getAttribute(attr) > 0.0f) - setAttribute(ATTR_MOVE_SPEED_RAW, utils::tpsToRawSpeed( - getModifiedAttribute(ATTR_MOVE_SPEED_TPS))); - break; - default: - // Do nothing + // Does not make a lot of sense to have in the scripts. + // So handle it here: + recalculateBaseAttribute(ATTR_MOVE_SPEED_RAW); break; } + + if (!mRecalculateDerivedAttributesCallback.isValid()) + return; + + Script *script = ScriptManager::currentState(); + script->setMap(getMap()); + script->prepare(mRecalculateDerivedAttributesCallback); + script->push(this); + script->push(attr); + script->execute(); } void Being::applyStatusEffect(int id, int timer) diff --git a/src/game-server/being.h b/src/game-server/being.h index d5a73588..1d1f4204 100644 --- a/src/game-server/being.h +++ b/src/game-server/being.h @@ -238,7 +238,7 @@ class Being : public Actor * attributes if it has changed. * @returns Whether it was changed. */ - virtual bool recalculateBaseAttribute(unsigned); + virtual void recalculateBaseAttribute(unsigned); /** * Attribute has changed, recalculate base value of dependant @@ -297,6 +297,12 @@ class Being : public Actor void setTarget(Being *target) { mTarget = target; } + static void setUpdateDerivedAttributesCallback(Script *script) + { script->assignCallback(mRecalculateDerivedAttributesCallback); } + + static void setRecalculateBaseAttributeCallback(Script *script) + { script->assignCallback(mRecalculateBaseAttributeCallback); } + sigc::signal signal_died; /** @@ -356,6 +362,12 @@ class Being : public Actor /** The last being emote Id. Used when triggering a being emoticon. */ int mEmoteId; + + /** Called when derived attributes need to get calculated */ + static Script::Ref mRecalculateDerivedAttributesCallback; + + /** Called when a base attribute needs to get calculated */ + static Script::Ref mRecalculateBaseAttributeCallback; }; #endif // BEING_H diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index a9777991..cf5415df 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -464,65 +464,26 @@ void Character::modifiedAllAttribute() } } -bool Character::recalculateBaseAttribute(unsigned attr) +void Character::recalculateBaseAttribute(unsigned attr) { - /* - * `attr' may or may not have changed. Recalculate the base value. - */ + // `attr' may or may not have changed. Recalculate the base value. LOG_DEBUG("Received update attribute recalculation request at Character " "for " << attr << "."); if (!mAttributes.count(attr)) - return false; - double newBase = getAttribute(attr); - - /* - * Calculate new base. - */ - switch (attr) - { - case ATTR_ACCURACY: - newBase = getModifiedAttribute(ATTR_DEX); // Provisional - break; - case ATTR_DEFENSE: - newBase = 0.3 * getModifiedAttribute(ATTR_VIT); - break; - case ATTR_DODGE: - newBase = getModifiedAttribute(ATTR_AGI); // Provisional - break; - case ATTR_MAGIC_DODGE: - newBase = 1.0; - // TODO - break; - case ATTR_MAGIC_DEFENSE: - newBase = 0.0; - // TODO - break; - case ATTR_BONUS_ASPD: - newBase = 0.0; - // TODO - break; - case ATTR_STR: - if (mKnuckleAttackInfo) - { - Damage &knuckleDamage = mKnuckleAttackInfo->getDamage(); - knuckleDamage.base = getModifiedAttribute(ATTR_STR); - knuckleDamage.delta = knuckleDamage.base / 2; - } - break; - default: - return Being::recalculateBaseAttribute(attr); - } + return; - if (newBase != getAttribute(attr)) + if (attr == ATTR_STR && mKnuckleAttackInfo) { - setAttribute(attr, newBase); - updateDerivedAttributes(attr); - return true; + // TODO: dehardcode this + Damage &knuckleDamage = mKnuckleAttackInfo->getDamage(); + knuckleDamage.base = getModifiedAttribute(ATTR_STR); + knuckleDamage.delta = knuckleDamage.base / 2; } - LOG_DEBUG("No changes to sync for attribute '" << attr << "'."); - return false; + Being::recalculateBaseAttribute(attr); + } + void Character::updateDerivedAttributes(unsigned attr) { /* @@ -530,32 +491,8 @@ void Character::updateDerivedAttributes(unsigned attr) */ flagAttribute(attr); - switch(attr) - { - case ATTR_STR: - recalculateBaseAttribute(ATTR_INV_CAPACITY); - break; - case ATTR_AGI: - recalculateBaseAttribute(ATTR_DODGE); - recalculateBaseAttribute(ATTR_MOVE_SPEED_TPS); - break; - case ATTR_VIT: - recalculateBaseAttribute(ATTR_MAX_HP); - recalculateBaseAttribute(ATTR_HP_REGEN); - recalculateBaseAttribute(ATTR_DEFENSE); - break; - case ATTR_INT: - // TODO - break; - case ATTR_DEX: - recalculateBaseAttribute(ATTR_ACCURACY); - break; - case ATTR_WIL: - // TODO - break; - default: - Being::updateDerivedAttributes(attr); - } + + Being::updateDerivedAttributes(attr); } void Character::flagAttribute(int attr) diff --git a/src/game-server/character.h b/src/game-server/character.h index d225081b..9c9a5459 100644 --- a/src/game-server/character.h +++ b/src/game-server/character.h @@ -251,11 +251,11 @@ class Character : public Being void modifiedAllAttribute(); /** - * Recalculate the base value of an attribute and update derived - * attributes if it has changed. - * @returns Whether it was changed. - */ - bool recalculateBaseAttribute(unsigned); + * Recalculate the base value of an attribute and update derived + * attributes if it has changed. + */ + void recalculateBaseAttribute(unsigned); + /** * Attribute has changed, recalculate base value of dependant diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp index 9f308401..7d5ec764 100644 --- a/src/game-server/monster.cpp +++ b/src/game-server/monster.cpp @@ -488,44 +488,3 @@ void Monster::died() } } } - -bool Monster::recalculateBaseAttribute(unsigned attr) -{ - LOG_DEBUG("Monster: Received update attribute recalculation request for " - << attr << "."); - if (!mAttributes.count(attr)) - { - LOG_DEBUG("Monster::recalculateBaseAttribute: " - << attr << " not found!"); - return false; - } - double newBase = getAttribute(attr); - - switch (attr) - { - // Those a set only at load time. - case ATTR_MAX_HP: - case ATTR_DODGE: - case ATTR_MAGIC_DODGE: - case ATTR_ACCURACY: - case ATTR_DEFENSE: - case ATTR_MAGIC_DEFENSE: - case ATTR_HP_REGEN: - case ATTR_MOVE_SPEED_TPS: - case ATTR_INV_CAPACITY: - // nothing to do. - break; - - // Only HP and Speed Raw updated for monsters - default: - Being::recalculateBaseAttribute(attr); - break; - } - if (newBase != getAttribute(attr)) - { - setAttribute(attr, newBase); - return true; - } - LOG_DEBUG("Monster: No changes to sync for attribute '" << attr << "'."); - return false; -} diff --git a/src/game-server/monster.h b/src/game-server/monster.h index 3313345c..93c9f4e2 100644 --- a/src/game-server/monster.h +++ b/src/game-server/monster.h @@ -329,14 +329,6 @@ class Monster : public Being */ void forgetTarget(Entity *entity); - /** - * Called when an attribute modifier is changed. - * Recalculate the base value of an attribute and update derived - * attributes if it has changed. - * @returns Whether it was changed. - */ - virtual bool recalculateBaseAttribute(unsigned); - protected: /** * Returns the way the actor blocks pathfinding for other objects. diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 880216d2..4e2cd283 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -68,6 +68,30 @@ extern "C" { */ +/** + * on_update_derived_attribute( function(Being*) ): void + * Sets a listener function to handle + * recalculation of derived attributes event. + */ +static int on_update_derived_attribute(lua_State *s) +{ + luaL_checktype(s, 1, LUA_TFUNCTION); + Being::setUpdateDerivedAttributesCallback(getScript(s)); + return 0; +} + + +/** + * on_recalculateBaseAttributeCallback( function(Being*) ): void + * Sets a listener function to the attribute recalculation event. + */ +static int on_recalculate_base_attribute(lua_State *s) +{ + luaL_checktype(s, 1, LUA_TFUNCTION); + Being::setRecalculateBaseAttributeCallback(getScript(s)); + return 0; +} + /** * on_character_death( function(Character*) ): void * Sets a listener function to the character death event. @@ -2582,6 +2606,8 @@ LuaScript::LuaScript(): // Put the callback functions in the scripting environment. static luaL_Reg const callbacks[] = { + { "on_update_derived_attribute", &on_update_derived_attribute }, + { "on_recalculate_base_attribute", &on_recalculate_base_attribute }, { "on_character_death", &on_character_death }, { "on_character_death_accept", &on_character_death_accept }, { "on_character_login", &on_character_login }, -- cgit v1.2.3-70-g09d2