summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--example/abilities.xml1
-rw-r--r--example/attributes.xml6
-rw-r--r--example/scripts/attributes.lua5
-rw-r--r--scripts/lua/libmana-constants.lua2
-rw-r--r--src/common/manaserv_protocol.h1
-rw-r--r--src/game-server/abilitycomponent.cpp14
-rw-r--r--src/game-server/abilitycomponent.h14
-rw-r--r--src/game-server/abilitymanager.cpp22
-rw-r--r--src/game-server/abilitymanager.h7
-rw-r--r--src/game-server/character.cpp22
-rw-r--r--src/game-server/character.h4
-rw-r--r--src/game-server/commandhandler.cpp2
-rw-r--r--src/scripting/lua.cpp55
-rw-r--r--src/scripting/luautil.cpp12
-rw-r--r--src/scripting/luautil.h28
15 files changed, 154 insertions, 41 deletions
diff --git a/example/abilities.xml b/example/abilities.xml
index 6d1c323..50a9ba0 100644
--- a/example/abilities.xml
+++ b/example/abilities.xml
@@ -7,6 +7,7 @@
rechargeable="true"
needed="100"
rechargeattribute="6"
+ cooldownattribute="20"
target="point"
/>
</ability-category>
diff --git a/example/attributes.xml b/example/attributes.xml
index e0e8829..425dd96 100644
--- a/example/attributes.xml
+++ b/example/attributes.xml
@@ -172,5 +172,11 @@
player-info="max-weight" >
<modifier stacktype="stackable" modtype="additive" tag="cap1" effect="Capacity %+.2f" />
</attribute>
+ <attribute id="20" name="Global ability cooldown"
+ desc="The time in ticks until any ability can be used again after one was used"
+ modifiable="false"
+ scope="being"
+ minimum="0"
+ maximum="100" />
<!-- End of core-attributes definition -->
</attributes>
diff --git a/example/scripts/attributes.lua b/example/scripts/attributes.lua
index 2096da9..37d642d 100644
--- a/example/scripts/attributes.lua
+++ b/example/scripts/attributes.lua
@@ -46,6 +46,9 @@ local function recalculate_base_attribute(being, attribute)
elseif attribute == ATTR_INV_CAPACITY then
-- Provisional
new_base = 2000 + being:modified_attribute(ATTR_STR) * 180
+ elseif attribute == ATTR_ABILITY_COOLDOWN then
+ -- Provisional
+ new_base = 100 - being:modified_attribute(ATTR_WIL)
end
if new_base ~= old_base then
@@ -66,7 +69,7 @@ local function update_derived_attributes(being, attribute)
elseif attribute == ATTR_INT then
-- unimplemented
elseif attribute == ATTR_WIL then
- -- unimplemented
+ recalculate_base_attribute(being, ATTR_ABILITY_COOLDOWN)
end
end
diff --git a/scripts/lua/libmana-constants.lua b/scripts/lua/libmana-constants.lua
index bb1f221..c187797 100644
--- a/scripts/lua/libmana-constants.lua
+++ b/scripts/lua/libmana-constants.lua
@@ -84,6 +84,8 @@ ATTR_MOVE_SPEED_RAW = 17;
ATTR_GP = 18;
ATTR_INV_CAPACITY = 19;
+ATTR_ABILITY_COOLDOWN = 20;
+
-- Emotes - TODO: should be obtainable in a smarter way
EMOTE_DISGUST = 10000;
diff --git a/src/common/manaserv_protocol.h b/src/common/manaserv_protocol.h
index 05e5fd5..498e35f 100644
--- a/src/common/manaserv_protocol.h
+++ b/src/common/manaserv_protocol.h
@@ -149,6 +149,7 @@ enum {
GPMSG_ABILITY_STATUS = 0x0293, // { B abilityID, D current, D max, D recharge }
PGMSG_USE_ABILITY_ON_POINT = 0x0294, // B abilityID, W*2 position
GPMSG_ABILITY_REMOVED = 0x0295, // B abilityID
+ GPMSG_ABILITY_COOLDOWN = 0x0296, // W ticks to wait
PGMSG_SAY = 0x02A0, // S text
GPMSG_SAY = 0x02A1, // W being id, S text
GPMSG_NPC_CHOICE = 0x02B0, // W being id, { S text }*
diff --git a/src/game-server/abilitycomponent.cpp b/src/game-server/abilitycomponent.cpp
index eac5f1c..5b27be1 100644
--- a/src/game-server/abilitycomponent.cpp
+++ b/src/game-server/abilitycomponent.cpp
@@ -77,6 +77,9 @@ bool AbilityComponent::takeAbility(int id)
bool AbilityComponent::abilityUseCheck(AbilityMap::iterator it)
{
+ if (!mCooldown.expired())
+ return false;
+
if (it == mAbilities.end())
{
LOG_INFO("Character uses ability " << it->first
@@ -123,6 +126,7 @@ void AbilityComponent::useAbilityOnBeing(Entity &user, int id, Entity *b)
if (ability.abilityInfo->autoconsume) {
ability.currentPoints = 0;
signal_ability_changed.emit(id);
+ startCooldown(user, ability.abilityInfo);
}
//tell script engine to cast the spell
@@ -151,6 +155,7 @@ void AbilityComponent::useAbilityOnPoint(Entity &user, int id, int x, int y)
if (ability.abilityInfo->autoconsume) {
ability.currentPoints = 0;
signal_ability_changed.emit(id);
+ startCooldown(user, ability.abilityInfo);
}
//tell script engine to cast the spell
@@ -201,6 +206,15 @@ bool AbilityComponent::setAbilityMana(int id, int mana)
return false;
}
+void AbilityComponent::startCooldown(
+ Entity &entity, const AbilityManager::AbilityInfo *abilityInfo)
+{
+ unsigned cooldownAttribute = abilityInfo->cooldownAttribute;
+ auto *bc = entity.getComponent<BeingComponent>();
+ mCooldown.set((int)bc->getModifiedAttribute(cooldownAttribute));
+ signal_cooldown_activated.emit();
+}
+
void AbilityComponent::attributeChanged(Entity *entity, unsigned attr)
{
for (auto &abilityIt : mAbilities)
diff --git a/src/game-server/abilitycomponent.h b/src/game-server/abilitycomponent.h
index ae48e91..c44dcb7 100644
--- a/src/game-server/abilitycomponent.h
+++ b/src/game-server/abilitycomponent.h
@@ -23,6 +23,7 @@
#include "game-server/abilitymanager.h"
#include "game-server/component.h"
+#include "game-server/timeout.h"
#include <sigc++/signal.h>
@@ -65,13 +66,19 @@ public:
bool setAbilityMana(int id, int mana);
+ void startCooldown(Entity &entity,
+ const AbilityManager::AbilityInfo *abilityInfo);
+ int remainingCooldown() const;
+
sigc::signal<void, int> signal_ability_changed;
sigc::signal<void, int> signal_ability_took;
-
+ sigc::signal<void> signal_cooldown_activated;
private:
bool abilityUseCheck(AbilityMap::iterator it);
void attributeChanged(Entity *entity, unsigned attr);
+ Timeout mCooldown;
+
AbilityMap mAbilities;
};
@@ -105,4 +112,9 @@ inline const AbilityMap &AbilityComponent::getAbilities() const
return mAbilities;
}
+inline int AbilityComponent::remainingCooldown() const
+{
+ return mCooldown.remaining();
+}
+
#endif /* ABILITYCOMPONENT_H_ */
diff --git a/src/game-server/abilitymanager.cpp b/src/game-server/abilitymanager.cpp
index eb828e8..0657c2c 100644
--- a/src/game-server/abilitymanager.cpp
+++ b/src/game-server/abilitymanager.cpp
@@ -100,6 +100,8 @@ void AbilityManager::readAbilityNode(xmlNodePtr abilityNode,
int neededMana = XML::getProperty(abilityNode, "needed", 0);
int rechargeAttribute = XML::getProperty(abilityNode,
"rechargeattribute", 0);
+ int cooldownAttribute = XML::getProperty(abilityNode,
+ "cooldownattribute", 0);
bool autoconsume = XML::getBoolProperty(abilityNode, "autoconsume", true);
if (rechargeable && neededMana <= 0)
@@ -118,6 +120,7 @@ void AbilityManager::readAbilityNode(xmlNodePtr abilityNode,
newInfo->rechargeable = rechargeable;
newInfo->neededPoints = neededMana;
newInfo->rechargeAttribute = rechargeAttribute;
+ newInfo->cooldownAttribute = cooldownAttribute;
newInfo->autoconsume = autoconsume;
newInfo->target = getTargetByString(XML::getProperty(abilityNode, "target",
@@ -177,8 +180,25 @@ const std::string AbilityManager::getCategoryName(int id) const
return it != mAbilitiesInfo.end() ? it->second->categoryName : "";
}
-AbilityManager::AbilityInfo *AbilityManager::getAbilityInfo(int id)
+AbilityManager::AbilityInfo *AbilityManager::getAbilityInfo(int id) const
{
AbilitiesInfo::const_iterator it = mAbilitiesInfo.find(id);
return it != mAbilitiesInfo.end() ? it->second : 0;
}
+
+AbilityManager::AbilityInfo *AbilityManager::getAbilityInfo(
+ const std::string &category,
+ const std::string &name) const
+{
+ std::string key = utils::toLower(category) + "_" + utils::toLower(name);
+ return getAbilityInfo(key);
+}
+
+AbilityManager::AbilityInfo *AbilityManager::getAbilityInfo(
+ const std::string &abilityName) const
+{
+ if (mNamedAbilitiesInfo.contains(abilityName))
+ return mNamedAbilitiesInfo.value(abilityName);
+ else
+ return 0;
+}
diff --git a/src/game-server/abilitymanager.h b/src/game-server/abilitymanager.h
index 2d23c48..9e315b7 100644
--- a/src/game-server/abilitymanager.h
+++ b/src/game-server/abilitymanager.h
@@ -44,6 +44,7 @@ public:
id(0),
rechargeable(false),
rechargeAttribute(0),
+ cooldownAttribute(0),
neededPoints(0),
autoconsume(true),
target(TARGET_BEING)
@@ -54,6 +55,7 @@ public:
std::string categoryName;
bool rechargeable;
unsigned rechargeAttribute;
+ unsigned cooldownAttribute;
unsigned neededPoints;
bool autoconsume;
TargetMode target;
@@ -91,7 +93,10 @@ public:
const std::string getAbilityName(int id) const;
const std::string getCategoryName(int id) const;
- AbilityInfo *getAbilityInfo(int id);
+ AbilityInfo *getAbilityInfo(int id) const;
+ AbilityInfo *getAbilityInfo(const std::string &category,
+ const std::string &name) const;
+ AbilityInfo *getAbilityInfo(const std::string &abilityName) const;
void readAbilityCategoryNode(xmlNodePtr node, const std::string &filename);
diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp
index 0fa1e10..3d23e3a 100644
--- a/src/game-server/character.cpp
+++ b/src/game-server/character.cpp
@@ -82,6 +82,7 @@ CharacterComponent::CharacterComponent(Entity &entity, MessageIn &msg):
mLevelProgress(0),
mUpdateLevelProgress(false),
mRecalculateLevel(true),
+ mSendAbilityCooldown(false),
mParty(0),
mTransaction(TRANS_NONE),
mTalkNpcId(0),
@@ -127,10 +128,14 @@ CharacterComponent::CharacterComponent(Entity &entity, MessageIn &msg):
mKnuckleAttackInfo = new AttackInfo(0, knuckleDamage, 7, 3, 0);
combatcomponent->addAttack(mKnuckleAttackInfo);
+
auto *abilityComponent = new AbilityComponent(entity);
entity.addComponent(abilityComponent);
abilityComponent->signal_ability_changed.connect(
sigc::mem_fun(this, &CharacterComponent::abilityStatusChanged));
+ abilityComponent->signal_cooldown_activated.connect(
+ sigc::mem_fun(this,
+ &CharacterComponent::abilityCooldownActivated));
// Get character data.
mDatabaseID = msg.readInt32();
@@ -170,6 +175,9 @@ void CharacterComponent::update(Entity &entity)
if (!mModifiedAbilities.empty())
sendAbilityUpdate(entity);
+
+ if (mSendAbilityCooldown)
+ sendAbilityCooldownUpdate(entity);
}
void CharacterComponent::characterDied(Entity *being)
@@ -214,6 +222,11 @@ void CharacterComponent::abilityStatusChanged(int id)
mModifiedAbilities.insert(id);
}
+void CharacterComponent::abilityCooldownActivated()
+{
+ mSendAbilityCooldown = true;
+}
+
void CharacterComponent::sendAbilityUpdate(Entity &entity)
{
auto *beingComponent = entity.getComponent<BeingComponent>();
@@ -240,6 +253,15 @@ void CharacterComponent::sendAbilityUpdate(Entity &entity)
gameHandler->sendTo(mClient, msg);
}
+void CharacterComponent::sendAbilityCooldownUpdate(Entity &entity)
+{
+ MessageOut msg(GPMSG_ABILITY_COOLDOWN);
+ auto *abilityComponent = entity.getComponent<AbilityComponent>();
+ msg.writeInt16(abilityComponent->remainingCooldown());
+ gameHandler->sendTo(mClient, msg);
+ mSendAbilityCooldown = false;
+}
+
void CharacterComponent::cancelTransaction()
{
TransactionType t = mTransaction;
diff --git a/src/game-server/character.h b/src/game-server/character.h
index a67436c..78d27ec 100644
--- a/src/game-server/character.h
+++ b/src/game-server/character.h
@@ -429,12 +429,15 @@ class CharacterComponent : public Component
void recalculateLevel(Entity &entity);
void abilityStatusChanged(int id);
+ void abilityCooldownActivated();
/**
* Informs the client about his characters abilities charge status
*/
void sendAbilityUpdate(Entity &entity);
+ void sendAbilityCooldownUpdate(Entity &entity);
+
enum TransactionType
{ TRANS_NONE, TRANS_TRADE, TRANS_BUYSELL };
@@ -468,6 +471,7 @@ class CharacterComponent : public Component
int mCorrectionPoints; /**< Unused attribute correction points */
bool mUpdateLevelProgress; /**< Flag raised when percent to next level changed */
bool mRecalculateLevel; /**< Flag raised when the character level might have increased */
+ bool mSendAbilityCooldown;
unsigned char mAccountLevel; /**< Account level of the user. */
int mParty; /**< Party id of the character */
TransactionType mTransaction; /**< Trade/buy/sell action the character is involved in. */
diff --git a/src/game-server/commandhandler.cpp b/src/game-server/commandhandler.cpp
index 3e74835..e54b9ea 100644
--- a/src/game-server/commandhandler.cpp
+++ b/src/game-server/commandhandler.cpp
@@ -1710,7 +1710,7 @@ static void handleRechargeAbility(Entity *player, std::string &args)
else
abilityId = abilityManager->getId(ability);
- AbilityManager::AbilityInfo *info =
+ const AbilityManager::AbilityInfo *info =
abilityManager->getAbilityInfo(abilityId);
if (!info)
diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp
index fd48f9b..3fd1651 100644
--- a/src/scripting/lua.cpp
+++ b/src/scripting/lua.cpp
@@ -1293,9 +1293,10 @@ static int chr_set_quest(lua_State *s)
static int entity_set_ability_mana(lua_State *s)
{
Entity *c = checkCharacter(s, 1);
- const int ability = checkAbility(s, 2);
+ auto *abilityInfo = checkAbility(s, 2);
const int mana = luaL_checkint(s, 3);
- if (!c->getComponent<AbilityComponent>()->setAbilityMana(ability, mana))
+ if (!c->getComponent<AbilityComponent>()->setAbilityMana(abilityInfo->id,
+ mana))
{
luaL_error(s,
"set_ability_mana called with ability "
@@ -1318,14 +1319,35 @@ static int entity_get_ability_mana(lua_State *s)
{
Entity *c = checkCharacter(s, 1);
auto *abilityComponent = c->getComponent<AbilityComponent>();
- const int ability = checkAbility(s, 2);
- AbilityMap::iterator it = abilityComponent->findAbility(ability);
+ auto *abilityInfo = checkAbility(s, 2);
+ AbilityMap::iterator it = abilityComponent->findAbility(abilityInfo->id);
luaL_argcheck(s, it != abilityComponent->getAbilities().end(), 2,
"character does not have ability");
lua_pushinteger(s, it->second.currentPoints);
return 1;
}
+/** LUA entity:cooldown_ability (being)
+ * entity:cooldown_ability(int abilityid)
+ * entity:cooldown_ability(string abilityname)
+ **
+ * Starts the cooldown of the passed ability. No other ability will be useable
+ * in this time.
+ *
+ * You do not need to call this if the attribute is set to ''autoconsume''.
+ *
+ * **Note:** When passing the ''abilityname'' as parameter make sure that it is
+ * formatted in this way: <setname>_<abilityname> (for eg. "Magic_Healingspell").
+ */
+static int entity_cooldown_ability(lua_State *s)
+{
+ Entity *c = checkCharacter(s, 1);
+ auto *abilityComponent = c->getComponent<AbilityComponent>();
+ auto *abilityInfo = checkAbility(s, 2);
+ abilityComponent->startCooldown(*c, abilityInfo);
+ return 0;
+}
+
/** LUA entity:walk (being)
* entity:walk(int pixelX, int pixelY [, int walkSpeed])
**
@@ -2298,10 +2320,11 @@ 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);
- const int ability = checkAbility(s, 2);
+ auto *abilityInfo = checkAbility(s, 2);
const int currentMana = luaL_optint(s, 3, 0);
- c->getComponent<AbilityComponent>()->giveAbility(ability, currentMana);
+ c->getComponent<AbilityComponent>()->giveAbility(abilityInfo->id,
+ currentMana);
return 0;
}
@@ -2864,11 +2887,8 @@ static int get_distance(lua_State *s)
*/
static int get_ability_info(lua_State *s)
{
- const int ability = checkAbility(s, 1);
- AbilityManager::AbilityInfo *info =
- abilityManager->getAbilityInfo(ability);
- luaL_argcheck(s, info, 1, "invalid ability");
- LuaAbilityInfo::push(s, info);
+ auto *abilityInfo = checkAbility(s, 1);
+ LuaAbilityInfo::push(s, abilityInfo);
return 1;
}
@@ -2882,7 +2902,7 @@ static int get_ability_info(lua_State *s)
*/
static int abilityinfo_get_name(lua_State *s)
{
- AbilityManager::AbilityInfo *info = LuaAbilityInfo::check(s, 1);
+ auto *info = LuaAbilityInfo::check(s, 1);
push(s, info->name);
return 1;
}
@@ -2897,7 +2917,7 @@ static int abilityinfo_get_name(lua_State *s)
*/
static int abilityinfo_get_needed_mana(lua_State *s)
{
- AbilityManager::AbilityInfo *info = LuaAbilityInfo::check(s, 1);
+ auto *info = LuaAbilityInfo::check(s, 1);
lua_pushinteger(s, info->neededPoints);
return 1;
}
@@ -2913,7 +2933,7 @@ static int abilityinfo_get_needed_mana(lua_State *s)
*/
static int abilityinfo_is_rechargeable(lua_State *s)
{
- AbilityManager::AbilityInfo *info = LuaAbilityInfo::check(s, 1);
+ auto *info = LuaAbilityInfo::check(s, 1);
lua_pushboolean(s, info->rechargeable);
return 1;
}
@@ -2929,7 +2949,7 @@ static int abilityinfo_is_rechargeable(lua_State *s)
*/
static int abilityinfo_on_use(lua_State *s)
{
- AbilityManager::AbilityInfo *info = LuaAbilityInfo::check(s, 1);
+ auto *info = LuaAbilityInfo::check(s, 1);
Script *script = getScript(s);
luaL_checktype(s, 2, LUA_TFUNCTION);
script->assignCallback(info->useCallback);
@@ -2947,7 +2967,7 @@ static int abilityinfo_on_use(lua_State *s)
*/
static int abilityinfo_on_recharged(lua_State *s)
{
- AbilityManager::AbilityInfo *info = LuaAbilityInfo::check(s, 1);
+ auto *info = LuaAbilityInfo::check(s, 1);
Script *script = getScript(s);
luaL_checktype(s, 2, LUA_TFUNCTION);
script->assignCallback(info->rechargedCallback);
@@ -2965,7 +2985,7 @@ static int abilityinfo_on_recharged(lua_State *s)
*/
static int abilitiyinfo_get_category(lua_State *s)
{
- AbilityManager::AbilityInfo *info = LuaAbilityInfo::check(s, 1);
+ auto *info = LuaAbilityInfo::check(s, 1);
push(s, info->categoryName);
return 1;
}
@@ -3685,6 +3705,7 @@ LuaScript::LuaScript():
{ "unequip_item", entity_unequip_item },
{ "set_ability_mana", entity_set_ability_mana },
{ "ability_mana", entity_get_ability_mana },
+ { "cooldown_ability", entity_cooldown_ability },
{ "walk", entity_walk },
{ "damage", entity_damage },
{ "heal", entity_heal },
diff --git a/src/scripting/luautil.cpp b/src/scripting/luautil.cpp
index f21e489..f889aa6 100644
--- a/src/scripting/luautil.cpp
+++ b/src/scripting/luautil.cpp
@@ -223,14 +223,16 @@ int checkSkill(lua_State *s, int p)
return id;
}
-int checkAbility(lua_State *s, int p)
+AbilityManager::AbilityInfo *checkAbility(lua_State *s, int p)
{
+ AbilityManager::AbilityInfo *abilityInfo;
if (lua_isnumber(s, p))
- return luaL_checkint(s, p);
+ abilityInfo = abilityManager->getAbilityInfo(luaL_checkint(s, p));
+ else
+ abilityInfo = abilityManager->getAbilityInfo(luaL_checkstring(s, p));
- int id = abilityManager->getId(luaL_checkstring(s, p));
- luaL_argcheck(s, id != 0, p, "invalid ability name");
- return id;
+ luaL_argcheck(s, abilityInfo != nullptr, p, "invalid ability");
+ return abilityInfo;
}
diff --git a/src/scripting/luautil.h b/src/scripting/luautil.h
index 2f3c15c..9ea11c0 100644
--- a/src/scripting/luautil.h
+++ b/src/scripting/luautil.h
@@ -168,20 +168,20 @@ typedef LuaUserData<MonsterClass> LuaMonsterClass;
typedef LuaUserData<StatusEffect> LuaStatusEffect;
typedef LuaUserData<AbilityManager::AbilityInfo> LuaAbilityInfo;
-Script * getScript(lua_State *s);
-
-ItemClass * getItemClass(lua_State *s, int p);
-MonsterClass * getMonsterClass(lua_State *s, int p);
-
-Entity * checkActor(lua_State *s, int p);
-Entity * checkBeing(lua_State *s, int p);
-Entity * checkCharacter(lua_State *s, int p);
-ItemClass * checkItemClass(lua_State *s, int p);
-Entity * checkMonster(lua_State *s, int p);
-MonsterClass * checkMonsterClass(lua_State *s, int p);
-Entity * checkNpc(lua_State *s, int p);
-int checkSkill(lua_State *s, int p);
-int checkAbility(lua_State *s, int p);
+Script * getScript(lua_State *s);
+
+ItemClass * getItemClass(lua_State *s, int p);
+MonsterClass * getMonsterClass(lua_State *s, int p);
+
+Entity * checkActor(lua_State *s, int p);
+Entity * checkBeing(lua_State *s, int p);
+Entity * checkCharacter(lua_State *s, int p);
+ItemClass * checkItemClass(lua_State *s, int p);
+Entity * checkMonster(lua_State *s, int p);
+MonsterClass * checkMonsterClass(lua_State *s, int p);
+Entity * checkNpc(lua_State *s, int p);
+int checkSkill(lua_State *s, int p);
+AbilityManager::AbilityInfo *checkAbility(lua_State *s, int p);
MapComposite * checkCurrentMap(lua_State *s, Script *script = 0);
Script::Thread* checkCurrentThread(lua_State *s, Script *script = 0);