summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Schilling <ablu.erikschilling@googlemail.com>2013-05-08 00:06:32 +0200
committerErik Schilling <ablu.erikschilling@googlemail.com>2013-05-11 23:31:14 +0200
commitf6765ffda1e0db5006aaebc359b2634837fe70bd (patch)
tree9d79efdcd21bf008d296cfd6b73d03e9870fc0ba
parentcec65058123f710643290c13b7f2b0512b878381 (diff)
downloadmanaserv-f6765ffda1e0db5006aaebc359b2634837fe70bd.tar.gz
manaserv-f6765ffda1e0db5006aaebc359b2634837fe70bd.tar.xz
manaserv-f6765ffda1e0db5006aaebc359b2634837fe70bd.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.lua97
-rw-r--r--src/game-server/abilitycomponent.cpp35
-rw-r--r--src/game-server/abilitycomponent.h6
-rw-r--r--src/game-server/monster.cpp7
-rw-r--r--src/game-server/monster.h19
-rw-r--r--src/game-server/monstermanager.cpp44
-rw-r--r--src/scripting/lua.cpp55
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 0000000..9c35f42
--- /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 60c9b86..961617b 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 def3e00..7d3472e 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 e0feed9..45a99d8 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 4949d4c..9854a7a 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 df8c690..19e196e 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 258b9bc..b3fe93c 100644
--- a/src/scripting/lua.cpp
+++ b/src/scripting/lua.cpp
@@ -2242,18 +2242,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;
}
@@ -2261,40 +2261,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)
*/
@@ -3327,6 +3361,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 },