summaryrefslogtreecommitdiff
path: root/src/game-server/being.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game-server/being.cpp')
-rw-r--r--src/game-server/being.cpp241
1 files changed, 141 insertions, 100 deletions
diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp
index fc56dc57..599ca1f1 100644
--- a/src/game-server/being.cpp
+++ b/src/game-server/being.cpp
@@ -24,6 +24,8 @@
#include "defines.h"
#include "common/configuration.hpp"
+#include "game-server/attributemanager.hpp"
+#include "game-server/character.hpp"
#include "game-server/collisiondetection.hpp"
#include "game-server/eventlistener.hpp"
#include "game-server/mapcomposite.hpp"
@@ -36,64 +38,92 @@ Being::Being(ThingType type):
Actor(type),
mAction(STAND),
mTarget(NULL),
- mSpeed(0),
mDirection(0)
{
- Attribute attr = { 0, 0 };
- mAttributes.resize(NB_BEING_ATTRIBUTES + CHAR_ATTR_NB, attr);
+ const AttributeScopes &attr = attributeManager->getAttributeInfoForType(ATTR_BEING);
+ LOG_DEBUG("Being creation: initialisation of " << attr.size() << " attributes.");
+ for (AttributeScopes::const_iterator it1 = attr.begin(),
+ it1_end = attr.end();
+ it1 != it1_end;
+ ++it1)
+ {
+ if (mAttributes.count(it1->first))
+ LOG_WARN("Redefinition of attribute '" << it1->first << "'!");
+ LOG_DEBUG("Attempting to create attribute '" << it1->first << "'.");
+ mAttributes.insert(std::make_pair(it1->first,
+ Attribute(*it1->second)));
+
+ }
+ // TODO: Way to define default base values?
+ // Should this be handled by the virtual modifiedAttribute?
+ // URGENT either way
+#if 0
// Initialize element resistance to 100 (normal damage).
- for (int i = BASE_ELEM_BEGIN; i < BASE_ELEM_END; ++i)
+ for (i = BASE_ELEM_BEGIN; i < BASE_ELEM_END; ++i)
{
- mAttributes[i].base = 100;
+ mAttributes[i] = Attribute(TY_ST);
+ mAttributes[i].setBase(100);
}
+#endif
}
-int Being::damage(Actor *, const Damage &damage)
+int Being::damage(Actor *source, const Damage &damage)
{
if (mAction == DEAD)
return 0;
int HPloss = damage.base;
if (damage.delta)
- {
- HPloss += rand() / (RAND_MAX / (damage.delta + 1));
- }
+ HPloss += rand() * (damage.delta + 1) / RAND_MAX;
- int hitThrow = rand()%(damage.cth + 1);
- int evadeThrow = rand()%(getModifiedAttribute(BASE_ATTR_EVADE) + 1);
- if (evadeThrow > hitThrow)
- {
- HPloss = 0;
- }
-
- /* Elemental modifier at 100 means normal damage. At 0, it means immune.
- And at 200, it means vulnerable (double damage). */
- int mod1 = getModifiedAttribute(BASE_ELEM_BEGIN + damage.element);
- HPloss = HPloss * (mod1 / 100);
- /* Defence is an absolute value which is subtracted from the damage total. */
- int mod2 = 0;
+ // TODO magical attacks and associated elemental modifiers
switch (damage.type)
{
case DAMAGE_PHYSICAL:
- mod2 = getModifiedAttribute(BASE_ATTR_PHY_RES);
- HPloss = HPloss - mod2;
+ if (!damage.trueStrike &&
+ rand()%((int) getModifiedAttribute(ATTR_DODGE) + 1) >
+ rand()%(damage.cth + 1))
+ {
+ HPloss = 0;
+ // TODO Process triggers for a dodged physical attack here.
+ // If there is an attacker included, also process triggers for the attacker (failed physical strike)
+ }
+ else
+ {
+ HPloss = HPloss * (1.0 - (0.0159375f *
+ getModifiedAttribute(ATTR_DEFENSE)) /
+ (1.0 + 0.017 *
+ getModifiedAttribute(ATTR_DEFENSE))) +
+ (rand()%((HPloss >> 4) + 1));
+ // TODO Process triggers for receiving damage here.
+ // If there is an attacker included, also process triggers for the attacker (successful physical strike)
+ }
break;
case DAMAGE_MAGICAL:
- mod2 = getModifiedAttribute(BASE_ATTR_MAG_RES);
- HPloss = HPloss / (mod2 + 1);
+#if 0
+ getModifiedAttribute(BASE_ELEM_BEGIN + damage.element);
+#else
+ LOG_WARN("Attempt to use magical type damage! This has not been"
+ "implemented yet and should not be used!");
+ HPloss = 0;
+#endif
+ case DAMAGE_DIRECT:
break;
default:
+ LOG_WARN("Unknown damage type '" << damage.type << "'!");
break;
}
if (HPloss > 0)
{
mHitsTaken.push_back(HPloss);
- Attribute &HP = mAttributes[BASE_ATTR_HP];
- LOG_DEBUG("Being " << getPublicID() << " suffered "<<HPloss<<" damage. HP: "<<HP.base + HP.mod<<"/"<<HP.base);
- HP.mod -= HPloss;
- updateDerivedAttributes(BASE_ATTR_HP);
- setTimerSoft(T_B_HP_REGEN, Configuration::getValue("hpRegenBreakAfterHit", 0)); // no HP regen after being hit
+ Attribute &HP = mAttributes.at(ATTR_HP);
+ LOG_DEBUG("Being " << getPublicID() << " suffered "<<HPloss<<" damage. HP: "
+ << HP.getModifiedAttribute() << "/"
+ << mAttributes.at(ATTR_MAX_HP).getModifiedAttribute());
+ HP.setBase(HP.getBase() - HPloss);
+ updateDerivedAttributes(ATTR_HP);
+ setTimerSoft(T_B_HP_REGEN, Configuration::getValue("hpRegenBreakAfterHit", 0)); // no HP regen after being hit if this is set.
} else {
HPloss = 0;
}
@@ -103,17 +133,23 @@ int Being::damage(Actor *, const Damage &damage)
void Being::heal()
{
- Attribute &HP = mAttributes[BASE_ATTR_HP];
- HP.mod = HP.base;
- updateDerivedAttributes(BASE_ATTR_HP);
+ Attribute &hp = mAttributes.at(ATTR_HP);
+ Attribute &maxHp = mAttributes.at(ATTR_MAX_HP);
+ if (maxHp.getModifiedAttribute() == hp.getModifiedAttribute()) return; // Full hp, do nothing.
+ hp.clearMods(); // Reset all modifications present in hp
+ hp.setBase(maxHp.getModifiedAttribute());
+ updateDerivedAttributes(ATTR_HP);
}
-void Being::heal(int hp)
+void Being::heal(int gain)
{
- Attribute &HP = mAttributes[BASE_ATTR_HP];
- HP.mod += hp;
- if (HP.mod > HP.base) HP.mod = HP.base;
- updateDerivedAttributes(BASE_ATTR_HP);
+ Attribute &hp = mAttributes.at(ATTR_HP);
+ Attribute &maxHp = mAttributes.at(ATTR_MAX_HP);
+ if (maxHp.getModifiedAttribute() == hp.getModifiedAttribute()) return; // Full hp, do nothing.
+ hp.setBase(hp.getBase() + gain);
+ if (hp.getModifiedAttribute() > maxHp.getModifiedAttribute()) // Cannot go over maximum hitpoints.
+ hp.setBase(maxHp.getModifiedAttribute());
+ updateDerivedAttributes(ATTR_HP);
}
void Being::died()
@@ -154,18 +190,10 @@ Path Being::findPath()
return map->findPath(startX, startY, destX, destY, getWalkMask());
}
-void Being::setSpeed(float s)
-{
- if (s > 0)
- mSpeed = (int)(32000 / (s * (float)DEFAULT_TILE_LENGTH));
- else
- mSpeed = 0;
-}
-
void Being::move()
{
- // Don't deal with not moving beings
- if (mSpeed <= 0 && mSpeed >= 32000)
+ // Immobile beings cannot move.
+ if (!checkAttributeExists(ATTR_MOVE_SPEED_RAW) || !getModifiedAttribute(ATTR_MOVE_SPEED_RAW))
return;
mOld = getPosition();
@@ -233,9 +261,10 @@ void Being::move()
{
Position next = mPath.front();
mPath.pop_front();
- // 362 / 256 is square root of 2, used for walking diagonally
- mActionTime += (prev.x != next.x && prev.y != next.y)
- ? mSpeed * 362 / 256 : mSpeed;
+ // SQRT2 is used for diagonal movement.
+ mActionTime += (prev.x == next.x || prev.y == next.y) ?
+ getModifiedAttribute(ATTR_MOVE_SPEED_RAW) :
+ getModifiedAttribute(ATTR_MOVE_SPEED_RAW) * SQRT2;
if (mPath.empty())
{
// skip last tile center
@@ -264,6 +293,10 @@ int Being::directionToAngle(int direction)
}
}
+int Being::performAttack(Being *target, const Damage &damage) {
+ return performAttack(target, damage.range, damage);
+}
+
int Being::performAttack(Being *target, unsigned range, const Damage &damage)
{
// check target legality
@@ -281,7 +314,7 @@ int Being::performAttack(Being *target, unsigned range, const Damage &damage)
if (maxDist * maxDist < distSquare)
return -1;
- mActionTime += 1000; // set to 10 ticks wait time
+ //mActionTime += 1000; // No tick. Auto-attacks should have their own, built-in delays.
return (mTarget->damage(this, damage));
}
@@ -296,41 +329,60 @@ void Being::setAction(Action action)
}
}
-void Being::applyModifier(int attr, int amount, int duration, int lvl)
+void Being::applyModifier(unsigned int attr, double value, unsigned int layer,
+ unsigned int duration, unsigned int id)
{
- if (duration)
- {
- AttributeModifier mod;
- mod.attr = attr;
- mod.value = amount;
- mod.duration = duration;
- mod.level = lvl;
- mModifiers.push_back(mod);
- }
- mAttributes[attr].mod += amount;
+ mAttributes.at(attr).add(duration, value, layer, id);
+ updateDerivedAttributes(attr);
+}
+
+bool Being::removeModifier(unsigned int attr, double value, unsigned int layer,
+ unsigned int id, bool fullcheck)
+{
+ bool ret = mAttributes.at(attr).remove(value, layer, id, fullcheck);
updateDerivedAttributes(attr);
+ return ret;
}
-void Being::dispellModifiers(int level)
+void Being::setAttribute(unsigned int id, double value, bool calc)
{
- AttributeModifiers::iterator i = mModifiers.begin();
- while (i != mModifiers.end())
+ AttributeMap::iterator ret = mAttributes.find(id);
+ if (ret == mAttributes.end())
{
- if (i->level && i->level <= level)
- {
- mAttributes[i->attr].mod -= i->value;
- updateDerivedAttributes(i->attr);
- i = mModifiers.erase(i);
- continue;
- }
- ++i;
+ /*
+ * The attribute does not yet exist, so we must attempt to create it.
+ */
+ LOG_ERROR("Being: Attempt to access non-existing attribute '" << id << "'!");
+ LOG_WARN("Being: Creation of new attributes dynamically is not "
+ "implemented yet!");
+ }
+ else {
+ ret->second.setBase(value);
+ if (calc)
+ updateDerivedAttributes(id);
}
}
-int Being::getModifiedAttribute(int attr) const
+double Being::getAttribute(unsigned int id) const
+{
+ AttributeMap::const_iterator ret = mAttributes.find(id);
+ if (ret == mAttributes.end()) return 0;
+ return ret->second.getBase();
+}
+
+
+double Being::getModifiedAttribute(unsigned int id) const
+{
+ AttributeMap::const_iterator ret = mAttributes.find(id);
+ if (ret == mAttributes.end()) return 0;
+ return ret->second.getModifiedAttribute();
+}
+
+void Being::setModAttribute(unsigned int id, double value)
{
- int res = mAttributes[attr].base + mAttributes[attr].mod;
- return res <= 0 ? 0 : res;
+ // No-op to satisfy shared structure.
+ // The game-server calculates this manually.
+ return;
}
void Being::applyStatusEffect(int id, int timer)
@@ -389,15 +441,15 @@ void Being::update()
if (i->second > -1) i->second--;
}
- int oldHP = getModifiedAttribute(BASE_ATTR_HP);
+ int oldHP = getModifiedAttribute(ATTR_HP);
int newHP = oldHP;
- int maxHP = getAttribute(BASE_ATTR_HP);
+ int maxHP = getModifiedAttribute(ATTR_MAX_HP);
// Regenerate HP
if (mAction != DEAD && !isTimerRunning(T_B_HP_REGEN))
{
setTimerHard(T_B_HP_REGEN, TICKS_PER_HP_REGENERATION);
- newHP += getModifiedAttribute(BASE_ATTR_HP_REGEN);
+ newHP += getModifiedAttribute(ATTR_HP_REGEN);
}
// Cap HP at maximum
if (newHP > maxHP)
@@ -407,24 +459,16 @@ void Being::update()
// Only update HP when it actually changed to avoid network noise
if (newHP != oldHP)
{
- applyModifier(BASE_ATTR_HP, newHP - oldHP);
+ mAttributes.at(ATTR_HP).setBase(newHP);
raiseUpdateFlags(UPDATEFLAG_HEALTHCHANGE);
}
// Update lifetime of effects.
- AttributeModifiers::iterator i = mModifiers.begin();
- while (i != mModifiers.end())
- {
- --i->duration;
- if (!i->duration)
- {
- mAttributes[i->attr].mod -= i->value;
- updateDerivedAttributes(i->attr);
- i = mModifiers.erase(i);
- continue;
- }
- ++i;
- }
+ for (AttributeMap::iterator it = mAttributes.begin();
+ it != mAttributes.end();
+ ++it)
+ if (it->second.tick())
+ updateDerivedAttributes(it->first);
// Update and run status effects
StatusEffects::iterator it = mStatus.begin();
@@ -432,9 +476,7 @@ void Being::update()
{
it->second.time--;
if (it->second.time > 0 && mAction != DEAD)
- {
it->second.status->tick(this, it->second.time);
- }
if (it->second.time <= 0 || mAction == DEAD)
{
@@ -445,10 +487,8 @@ void Being::update()
}
// Check if being died
- if (getModifiedAttribute(BASE_ATTR_HP) <= 0 && mAction != DEAD)
- {
+ if (getModifiedAttribute(ATTR_HP) <= 0 && mAction != DEAD)
died();
- }
}
void Being::setTimerSoft(TimerID id, int value)
@@ -484,3 +524,4 @@ bool Being::isTimerJustFinished(TimerID id) const
{
return getTimer(id) == 0;
}
+