diff options
author | Erik Schilling <ablu.erikschilling@googlemail.com> | 2013-05-08 00:06:32 +0200 |
---|---|---|
committer | Erik Schilling <ablu.erikschilling@googlemail.com> | 2013-08-26 22:56:46 +0200 |
commit | 68481094c0d02ba127bcab6c692801b01a27f21b (patch) | |
tree | aad235cc104f2a2445bab9f8e0d274ad89969cf4 | |
parent | f31277b327df701361391b1d4b8bd6f89f4e3109 (diff) | |
download | manaserv-68481094c0d02ba127bcab6c692801b01a27f21b.tar.gz manaserv-68481094c0d02ba127bcab6c692801b01a27f21b.tar.bz2 manaserv-68481094c0d02ba127bcab6c692801b01a27f21b.tar.xz manaserv-68481094c0d02ba127bcab6c692801b01a27f21b.zip |
[Abilities] Added abilities to monsters
Monsters can now either receive abilities at lifetime via scripts, or
via the <ability> node in the monsters node.
-rw-r--r-- | example/scripts/monster/basic_ai.lua | 97 | ||||
-rw-r--r-- | src/game-server/abilitycomponent.cpp | 35 | ||||
-rw-r--r-- | src/game-server/abilitycomponent.h | 6 | ||||
-rw-r--r-- | src/game-server/monster.cpp | 7 | ||||
-rw-r--r-- | src/game-server/monster.h | 19 | ||||
-rw-r--r-- | src/game-server/monstermanager.cpp | 44 | ||||
-rw-r--r-- | src/scripting/lua.cpp | 55 |
7 files changed, 237 insertions, 26 deletions
diff --git a/example/scripts/monster/basic_ai.lua b/example/scripts/monster/basic_ai.lua new file mode 100644 index 00000000..9c35f42c --- /dev/null +++ b/example/scripts/monster/basic_ai.lua @@ -0,0 +1,97 @@ +--[[ + + Basic stroll ai + +--]] + +local STROLL_TIMEOUT = 20 +local STROLL_TIMEOUT_RANDOMNESS = 10 + +-- Wrapping the monster update callback in order to do the stroll ai here +local old_on_update = MonsterClass.on_update +local update_functions = {} +function MonsterClass:on_update(callback) + update_functions[self] = callback +end + +local stroll_timer = {} +local angerlist = {} + +local mob_config = require "scripts/monster/settings" + +local function find_target(mob, config) + local target + local target_priority + local attack_x, attack_y + + for _, being in ipairs(get_beings_in_circle(mob, config.trackrange)) do + if being:type() == OBJECT_CHARACTER + and being:action() ~= ACTION_DEAD + then + local anger = angerlist[being] or 0 + if anger == 0 and config.aggressive then + anger = 1 + end + + local possible_attack_positions = { + { + x = being:x() - config.attack_distance or TILESIZE, + y = being:y() + }, + { + + x = being:x() + y = being:y() - config.attack_distance or TILESIZE, + }, + { + + x = being:x() + config.attack_distance or TILESIZE, + y = being:y(), + }, + { + + x = being:x() + y = being:y() + config.attack_distance or TILESIZE, + }, + } + for _, point in ipairs(possible_attack_positions) do + local priority = calculate_position_priority(mob:position(), point.x, point.y) + end + + + + end + end +end + +local function stroll_update(mob, tick) + local stroll_tick = stroll_timer[mob] + local mobconfig = mob_config[mob:name()] + + + local trackrange = mobconfig and mobconfig.trackrange or nil + + + local strollrange = mobconfig and mobconfig.strollrange or nil + if (not stroll_tick or stroll_tick <= tick) and strollrange then + local x, y = mob:position() + local destination_x = math.random(x - strollrange, x + strollrange) + local destination_y = math.random(y - strollrange, y + strollrange) + if is_walkable(destination_x, destination_y) then + mob:walk(destination_x, destination_y) + end + stroll_timer[mob] = tick + STROLL_TIMEOUT + + math.random(STROLL_TIMEOUT_RANDOMNESS) + end + + local monsterclass = get_monster_class(mob:monster_id()) + local update_function = update_functions[monsterclass] + if update_function then + return update_function(mob, tick) + end +end + +-- Register all update functions for strolling +for _, monsterclass in ipairs(get_monster_classes()) do + old_on_update(monsterclass, stroll_update) +end diff --git a/src/game-server/abilitycomponent.cpp b/src/game-server/abilitycomponent.cpp index 60c9b86f..961617b1 100644 --- a/src/game-server/abilitycomponent.cpp +++ b/src/game-server/abilitycomponent.cpp @@ -84,7 +84,7 @@ bool AbilityComponent::abilityUseCheck(AbilityMap::iterator it) if (it == mAbilities.end()) { - LOG_INFO("Character uses ability " << it->first + LOG_INFO("Entity uses ability " << it->first << " without authorization."); return false; } @@ -115,15 +115,15 @@ bool AbilityComponent::abilityUseCheck(AbilityMap::iterator it) * makes the character perform a ability on a being * when it is allowed to do so */ -void AbilityComponent::useAbilityOnBeing(Entity &user, int id, Entity *b) +bool AbilityComponent::useAbilityOnBeing(Entity &user, int id, Entity *b) { AbilityMap::iterator it = mAbilities.find(id); if (!abilityUseCheck(it)) - return; + return false; AbilityValue &ability = it->second; if (ability.abilityInfo->target != AbilityManager::TARGET_BEING) - return; + return false; if (ability.abilityInfo->autoconsume) { ability.currentPoints = 0; @@ -146,21 +146,22 @@ void AbilityComponent::useAbilityOnBeing(Entity &user, int id, Entity *b) mLastTargetBeingId = 0; user.getComponent<ActorComponent>()->raiseUpdateFlags( UPDATEFLAG_ABILITY_ON_BEING); + return true; } /** * makes the character perform a ability on a map point * when it is allowed to do so */ -void AbilityComponent::useAbilityOnPoint(Entity &user, int id, int x, int y) +bool AbilityComponent::useAbilityOnPoint(Entity &user, int id, int x, int y) { AbilityMap::iterator it = mAbilities.find(id); if (!abilityUseCheck(it)) - return; + return false; AbilityValue &ability = it->second; if (ability.abilityInfo->target != AbilityManager::TARGET_POINT) - return; + return false; if (ability.abilityInfo->autoconsume) { ability.currentPoints = 0; @@ -181,6 +182,7 @@ void AbilityComponent::useAbilityOnPoint(Entity &user, int id, int x, int y) mLastTargetPoint = Point(x, y); user.getComponent<ActorComponent>()->raiseUpdateFlags( UPDATEFLAG_ABILITY_ON_POINT); + return true; } /** @@ -190,22 +192,27 @@ bool AbilityComponent::giveAbility(int id, int currentPoints) { if (mAbilities.find(id) == mAbilities.end()) { - const AbilityManager::AbilityInfo *abilityInfo = - abilityManager->getAbilityInfo(id); + auto *abilityInfo = abilityManager->getAbilityInfo(id); if (!abilityInfo) { LOG_ERROR("Tried to give not existing ability id " << id << "."); return false; } - mAbilities.insert(std::pair<int, AbilityValue>( - id, AbilityValue(currentPoints, abilityInfo))); - - signal_ability_changed.emit(id); - return true; + return giveAbility(abilityInfo, currentPoints); } return false; } +bool AbilityComponent::giveAbility(const AbilityManager::AbilityInfo *info, + int currentPoints) +{ + bool added = mAbilities.insert(std::pair<int, AbilityValue>(info->id, + AbilityValue(currentPoints, info))).second; + + signal_ability_changed.emit(info->id); + return added; +} + /** * Sets new current mana + makes sure that the client will get informed. */ diff --git a/src/game-server/abilitycomponent.h b/src/game-server/abilitycomponent.h index def3e00b..7d3472e0 100644 --- a/src/game-server/abilitycomponent.h +++ b/src/game-server/abilitycomponent.h @@ -56,10 +56,12 @@ public: void update(Entity &entity); - void useAbilityOnBeing(Entity &user, int id, Entity *b); - void useAbilityOnPoint(Entity &user, int id, int x, int y); + bool useAbilityOnBeing(Entity &user, int id, Entity *b); + bool useAbilityOnPoint(Entity &user, int id, int x, int y); bool giveAbility(int id, int currentMana = 0); + bool giveAbility(const AbilityManager::AbilityInfo *info, + int currentMana = 0); bool hasAbility(int id) const; bool takeAbility(int id); AbilityMap::iterator findAbility(int id); diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp index e0feed9e..45a99d82 100644 --- a/src/game-server/monster.cpp +++ b/src/game-server/monster.cpp @@ -86,6 +86,13 @@ MonsterComponent::MonsterComponent(Entity &entity, MonsterClass *specy): beingComponent->setGender(specy->getGender()); + AbilityComponent *abilityComponent = new AbilityComponent(entity); + entity.addComponent(abilityComponent); + for (auto *abilitiyInfo : specy->getAbilities()) + { + abilityComponent->giveAbility(abilitiyInfo); + } + beingComponent->signal_died.connect(sigc::mem_fun(this, &MonsterComponent::monsterDied)); } diff --git a/src/game-server/monster.h b/src/game-server/monster.h index 4949d4c7..9854a7a2 100644 --- a/src/game-server/monster.h +++ b/src/game-server/monster.h @@ -21,9 +21,13 @@ #ifndef MONSTER_H #define MONSTER_H +#include "game-server/abilitymanager.h" #include "game-server/being.h" + #include "common/defines.h" + #include "scripting/script.h" + #include "utils/string.h" #include <map> @@ -182,6 +186,9 @@ class MonsterClass double getVulnerability(Element element) const; + void addAbility(AbilityManager::AbilityInfo *info); + const std::set<AbilityManager::AbilityInfo *> &getAbilities() const; + void setUpdateCallback(Script *script) { script->assignCallback(mUpdateCallback); } @@ -201,6 +208,7 @@ class MonsterClass MonsterDrops mDrops; std::map<int, double> mAttributes; /**< Base attributes of the monster. */ + std::set<AbilityManager::AbilityInfo *> mAbilities; float mSpeed; /**< The monster class speed in tiles per second */ int mSize; int mExp; @@ -308,4 +316,15 @@ class MonsterComponent : public Component Timeout mDecayTimeout; }; +inline void MonsterClass::addAbility(AbilityManager::AbilityInfo *info) +{ + mAbilities.insert(info); +} + +inline const std::set<AbilityManager::AbilityInfo *> +&MonsterClass::getAbilities() const +{ + return mAbilities; +} + #endif // MONSTER_H diff --git a/src/game-server/monstermanager.cpp b/src/game-server/monstermanager.cpp index df8c6906..19e196e2 100644 --- a/src/game-server/monstermanager.cpp +++ b/src/game-server/monstermanager.cpp @@ -216,6 +216,50 @@ void MonsterManager::readMonsterNode(xmlNodePtr node, const std::string &filenam } } + else if (xmlStrEqual(subnode->name, BAD_CAST "attribute")) + { + const int id = XML::getProperty(subnode, "id", 0); + auto *attributeInfo = attributeManager->getAttributeInfo(id); + + if (!attributeInfo) + { + LOG_WARN(filename + << ": Invalid attribute id " << id + << " for monster Id: " << id + << ". Skipping!"); + continue; + } + + const double value = XML::getFloatProperty(subnode, "value", 0.0); + + monster->setAttribute(id, value); + } + else if (xmlStrEqual(subnode->name, BAD_CAST "ability")) + { + const std::string idText = XML::getProperty(subnode, "id", + std::string()); + AbilityManager::AbilityInfo *info = 0; + if (utils::isNumeric(idText)) + { + const int id = utils::stringToInt(idText); + info = abilityManager->getAbilityInfo(id); + } + else + { + info = abilityManager->getAbilityInfo(idText); + } + + if (!info) + { + LOG_WARN(filename + << ": Invalid ability id " << idText + << " for monster id: " << id + << " Skipping!"); + continue; + } + + monster->addAbility(info); + } else if (xmlStrEqual(subnode->name, BAD_CAST "exp")) { xmlChar *exp = subnode->xmlChildrenNode->content; diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 87568087..0c27788c 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -2241,18 +2241,18 @@ static int entity_show_text_particle(lua_State *s) /** LUA entity:give_ability (being) * entity:give_ability(int ability) ** - * Valid only for character entities. + * Valid only for character and monster entities. * * Enables a ability for a character. */ static int entity_give_ability(lua_State *s) { // cost_type is ignored until we have more than one cost type - Entity *c = checkCharacter(s, 1); + Entity *b = checkBeing(s, 1); auto *abilityInfo = checkAbility(s, 2); const int currentMana = luaL_optint(s, 3, 0); - c->getComponent<AbilityComponent>()->giveAbility(abilityInfo->id, + b->getComponent<AbilityComponent>()->giveAbility(abilityInfo->id, currentMana); return 0; } @@ -2260,40 +2260,74 @@ static int entity_give_ability(lua_State *s) /** LUA entity:has_ability (being) * entity:has_ability(int ability) ** - * Valid only for character entities. + * Valid only for character and monster entities. * * **Return value:** True if the character has the ability, false otherwise. */ static int entity_has_ability(lua_State *s) { - Entity *c = checkCharacter(s, 1); + Entity *b = checkBeing(s, 1); const int ability = luaL_checkint(s, 2); - lua_pushboolean(s, c->getComponent<AbilityComponent>()->hasAbility(ability)); + lua_pushboolean(s, b->getComponent<AbilityComponent>()->hasAbility(ability)); return 1; } /** LUA entity:take_ability (being) * entity:take_ability(int ability) ** - * Valid only for character entities. + * Valid only for character and monster entities. * - * Removes a ability from a character. + * Removes a ability from a entity. * * **Return value:** True if removal was successful, false otherwise (in case * the character did not have the ability). */ static int entity_take_ability(lua_State *s) { - Entity *c = checkCharacter(s, 1); + Entity *b = checkBeing(s, 1); const int ability = luaL_checkint(s, 2); - auto *abilityComponent = c->getComponent<AbilityComponent>(); + auto *abilityComponent = b->getComponent<AbilityComponent>(); lua_pushboolean(s, abilityComponent->hasAbility(ability)); abilityComponent->takeAbility(ability); return 1; } +/** LUA entity:use_ability (being) + * entity:use_ability(int ability) + ** + * Valid only for character and monster entities. + * + * Makes the entity using the given ability if it is available and recharged. + * + * **Return value:** True if the ability was used successfully. False otherwise + * (if the ability is not available for the entity or was not recharged). + */ +static int entity_use_ability(lua_State *s) +{ + Entity *b = checkBeing(s, 1); + const int ability = luaL_checkint(s, 2); + bool targetIsBeing = lua_gettop(s) == 3; + + auto *abilityComponent = b->getComponent<AbilityComponent>(); + if (targetIsBeing) + { + Entity *target = checkBeing(s, 3); + lua_pushboolean(s, abilityComponent->useAbilityOnBeing(*b, ability, + target)); + } + else + { + const int x = luaL_checkint(s, 3); + const int y = luaL_checkint(s, 4); + lua_pushboolean(s, abilityComponent->useAbilityOnPoint(*b, ability, + x, y)); + } + + return 1; +} + /** LUA_CATEGORY Monster (monster) */ @@ -3332,6 +3366,7 @@ LuaScript::LuaScript(): { "give_ability", entity_give_ability }, { "has_ability", entity_has_ability }, { "take_ability", entity_take_ability }, + { "use_ability", entity_use_ability }, { "monster_id", entity_get_monster_id }, { "apply_status", entity_apply_status }, { "remove_status", entity_remove_status }, |