summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Schilling <ablu.erikschilling@googlemail.com>2013-02-04 16:19:21 +0100
committerErik Schilling <ablu.erikschilling@googlemail.com>2013-02-04 17:43:24 +0100
commitf7d71e54c0b5fdaa9e8aa1e69e615b9f80d124e4 (patch)
tree24a1d06ef633fe3a1bf4943846666f48aaf0c32a
parent5255be5a5457e287464606478df62300a44d8479 (diff)
downloadmanaserv-f7d71e54c0b5fdaa9e8aa1e69e615b9f80d124e4.tar.gz
manaserv-f7d71e54c0b5fdaa9e8aa1e69e615b9f80d124e4.tar.xz
manaserv-f7d71e54c0b5fdaa9e8aa1e69e615b9f80d124e4.zip
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.
-rw-r--r--example/scripts/attributes.lua74
-rw-r--r--example/scripts/main.lua1
-rw-r--r--scripts/lua/libmana-constants.lua4
-rw-r--r--src/game-server/being.cpp84
-rw-r--r--src/game-server/being.h14
-rw-r--r--src/game-server/character.cpp89
-rw-r--r--src/game-server/character.h10
-rw-r--r--src/game-server/monster.cpp41
-rw-r--r--src/game-server/monster.h8
-rw-r--r--src/scripting/lua.cpp26
10 files changed, 174 insertions, 177 deletions
diff --git a/example/scripts/attributes.lua b/example/scripts/attributes.lua
new file mode 100644
index 0000000..7009a73
--- /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 6d8207e..3418385 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 9638588..a07df4c 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 d7df2df..2c334b3 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 d5a7358..1d1f420 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<void, Being *> 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 a977799..cf5415d 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 d225081..9c9a545 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 9f30840..7d5ec76 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 3313345..93c9f4e 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 880216d..4e2cd28 100644
--- a/src/scripting/lua.cpp
+++ b/src/scripting/lua.cpp
@@ -69,6 +69,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 },