summaryrefslogtreecommitdiffstats
path: root/src/game-server
diff options
context:
space:
mode:
authorErik Schilling <ablu.erikschilling@googlemail.com>2013-03-30 09:29:08 +0100
committerErik Schilling <ablu.erikschilling@googlemail.com>2013-04-02 13:19:11 +0200
commit585a33e221a7ee392791f4fd5a5ec9214b8fe868 (patch)
tree3aa60a57ff7cdd8f57761d620352fd2ad4d0dd6f /src/game-server
parent4dfc82415691fe298f21bb2f81fed5c168ee14e5 (diff)
downloadmanaserv-585a33e221a7ee392791f4fd5a5ec9214b8fe868.tar.gz
manaserv-585a33e221a7ee392791f4fd5a5ec9214b8fe868.tar.xz
manaserv-585a33e221a7ee392791f4fd5a5ec9214b8fe868.zip
Moved fighting code into a component
All damage dealing is now handeled via CombatComponent. Monsters use a derived MonsterCombatComponent since they can have a damage mutation and have a seperate script callback. The wirering with Being is still not optional since most of the stuff does not exist as components. Things done: - Seperated the fighting code from Being and only let Characters and Monsters add the Component (less overhead for npcs) - Added a getter for Attribute values to prevent searching it all the time in non Being members - Fixed the type if the damage mutation to double (no idea why it was int) I did not want to copy it over incorrectly - Removed the addAttack/removeAttack overrides in Character and made the knuckleAttack being added based on newly added signals Future TODOS: - Remove depedency on Being as soon all needed dependencies are available as components of Entity - Move the monster script callback into the general combatcomponent and make it usuable for characters too
Diffstat (limited to 'src/game-server')
-rw-r--r--src/game-server/attack.cpp2
-rw-r--r--src/game-server/attack.h8
-rw-r--r--src/game-server/being.cpp171
-rw-r--r--src/game-server/being.h86
-rw-r--r--src/game-server/buysell.cpp6
-rw-r--r--src/game-server/character.cpp55
-rw-r--r--src/game-server/character.h4
-rw-r--r--src/game-server/combatcomponent.cpp213
-rw-r--r--src/game-server/combatcomponent.h144
-rw-r--r--src/game-server/commandhandler.cpp2
-rw-r--r--src/game-server/component.h1
-rw-r--r--src/game-server/gamehandler.cpp3
-rw-r--r--src/game-server/item.cpp5
-rw-r--r--src/game-server/mapcomposite.cpp5
-rw-r--r--src/game-server/monster.cpp117
-rw-r--r--src/game-server/monster.h15
-rw-r--r--src/game-server/monstercombatcomponent.cpp99
-rw-r--r--src/game-server/monstercombatcomponent.h54
-rw-r--r--src/game-server/state.cpp14
-rw-r--r--src/game-server/trade.cpp8
20 files changed, 640 insertions, 372 deletions
diff --git a/src/game-server/attack.cpp b/src/game-server/attack.cpp
index 1825b86..a1300e4 100644
--- a/src/game-server/attack.cpp
+++ b/src/game-server/attack.cpp
@@ -75,6 +75,7 @@ AttackInfo *AttackInfo::readAttackNode(xmlNodePtr node)
void Attacks::add(AttackInfo *attackInfo)
{
mAttacks.push_back(Attack(attackInfo));
+ attack_added.emit(*mAttacks.rbegin());
}
void Attacks::remove(AttackInfo *attackInfo)
@@ -86,6 +87,7 @@ void Attacks::remove(AttackInfo *attackInfo)
{
if (mCurrentAttack && mCurrentAttack->getAttackInfo() == attackInfo)
mCurrentAttack = 0;
+ attack_removed.emit(*it);
mAttacks.erase(it);
return;
}
diff --git a/src/game-server/attack.h b/src/game-server/attack.h
index 0bcc361..cecb4fd 100644
--- a/src/game-server/attack.h
+++ b/src/game-server/attack.h
@@ -24,6 +24,9 @@
#include <cstddef>
#include <list>
+#include <sigc++/signal.h>
+#include <sigc++/trackable.h>
+
#include "common/defines.h"
#include "scripting/script.h"
@@ -168,7 +171,7 @@ class Attack
/**
* Helper class for storing multiple auto-attacks.
*/
-class Attacks
+class Attacks : public sigc::trackable
{
public:
Attacks():
@@ -188,6 +191,9 @@ class Attacks
unsigned getNumber()
{ return mAttacks.size(); }
+ sigc::signal<void, Attack &> attack_added;
+ sigc::signal<void, Attack &> attack_removed;
+
private:
std::vector<Attack> mAttacks;
diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp
index 9112a1b..42d3404 100644
--- a/src/game-server/being.cpp
+++ b/src/game-server/being.cpp
@@ -27,6 +27,7 @@
#include "game-server/attributemanager.h"
#include "game-server/character.h"
#include "game-server/collisiondetection.h"
+#include "game-server/combatcomponent.h"
#include "game-server/mapcomposite.h"
#include "game-server/effect.h"
#include "game-server/skillmanager.h"
@@ -43,9 +44,7 @@ Script::Ref Being::mRecalculateBaseAttributeCallback;
Being::Being(EntityType type):
Actor(type),
mAction(STAND),
- mTarget(NULL),
mGender(GENDER_UNSPECIFIED),
- mCurrentAttack(0),
mDirection(DOWN),
mEmoteId(0)
{
@@ -87,74 +86,7 @@ void Being::triggerEmote(int id)
raiseUpdateFlags(UPDATEFLAG_EMOTE);
}
-int Being::damage(Actor * /* source */, const Damage &damage)
-{
- if (mAction == DEAD)
- return 0;
-
- int HPloss = damage.base;
- if (damage.delta)
- HPloss += rand() * (damage.delta + 1) / RAND_MAX;
-
- // TODO magical attacks and associated elemental modifiers
- switch (damage.type)
- {
- case DAMAGE_PHYSICAL:
- 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:
-#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
- break;
- case DAMAGE_DIRECT:
- break;
- default:
- LOG_WARN("Unknown damage type '" << damage.type << "'!");
- break;
- }
-
- if (HPloss > 0)
- {
- mHitsTaken.push_back(HPloss);
- Attribute &HP = mAttributes.at(ATTR_HP);
- LOG_DEBUG("Being " << getPublicID() << " suffered " << HPloss
- << " damage. HP: "
- << HP.getModifiedAttribute() << "/"
- << mAttributes.at(ATTR_MAX_HP).getModifiedAttribute());
- setAttribute(ATTR_HP, HP.getBase() - HPloss);
- // No HP regen after being hit if this is set.
- mHealthRegenerationTimeout.setSoft(
- Configuration::getValue("game_hpRegenBreakAfterHit", 0));
- }
- else
- {
- HPloss = 0;
- }
- return HPloss;
-}
void Being::heal()
{
@@ -191,71 +123,9 @@ void Being::died()
// dead beings stay where they are
clearDestination();
- // reset target
- mTarget = NULL;
-
signal_died.emit(this);
}
-void Being::processAttacks()
-{
- if (mAction != ATTACK || !mTarget)
- return;
-
- // Ticks attacks even when not attacking to permit cooldowns and warmups.
- std::vector<Attack *> attacksReady;
- mAttacks.getUsuableAttacks(&attacksReady);
-
- if (Attack *triggerableAttack = mAttacks.getTriggerableAttack())
- {
- processAttack(*triggerableAttack);
- mAttacks.markAttackAsTriggered();
- }
-
- // Deal with the ATTACK action.
- if (attacksReady.empty())
- return;
-
- Attack *highestPriorityAttack = 0;
- // Performs all ready attacks.
- for (std::vector<Attack *>::const_iterator it = attacksReady.begin(),
- it_end = attacksReady.end(); it != it_end; ++it)
- {
- // check if target is in range using the pythagorean theorem
- int distx = this->getPosition().x - mTarget->getPosition().x;
- int disty = this->getPosition().y - mTarget->getPosition().y;
- int distSquare = (distx * distx + disty * disty);
- AttackInfo *info = (*it)->getAttackInfo();
- int maxDist = info->getDamage().range + getSize();
-
- if (distSquare <= maxDist * maxDist &&
- (!highestPriorityAttack ||
- highestPriorityAttack->getAttackInfo()->getPriority()
- < info->getPriority()))
- {
- highestPriorityAttack = *it;
- }
- }
- if (highestPriorityAttack)
- {
- mAttacks.startAttack(highestPriorityAttack);
- mCurrentAttack = highestPriorityAttack;
- setDestination(getPosition());
- // TODO: Turn into direction of enemy
- raiseUpdateFlags(UPDATEFLAG_ATTACK);
- }
-}
-
-void Being::addAttack(AttackInfo *attackInfo)
-{
- mAttacks.add(attackInfo);
-}
-
-void Being::removeAttack(AttackInfo *attackInfo)
-{
- mAttacks.remove(attackInfo);
-}
-
void Being::setDestination(const Point &dst)
{
mDst = dst;
@@ -480,23 +350,6 @@ int Being::directionToAngle(int direction)
}
}
-int Being::performAttack(Being *target, const Damage &dmg)
-{
- // check target legality
- if (!target
- || target == this
- || target->getAction() == DEAD
- || !target->canFight())
- return -1;
-
- if (getMap()->getPvP() == PVP_NONE
- && target->getType() == OBJECT_CHARACTER
- && getType() == OBJECT_CHARACTER)
- return -1;
-
- return target->damage(this, dmg);
-}
-
void Being::setAction(BeingAction action)
{
mAction = action;
@@ -547,7 +400,7 @@ void Being::setAttribute(unsigned id, double value)
}
}
-double Being::getAttribute(unsigned id) const
+const Attribute *Being::getAttribute(unsigned id) const
{
AttributeMap::const_iterator ret = mAttributes.find(id);
if (ret == mAttributes.end())
@@ -556,6 +409,18 @@ double Being::getAttribute(unsigned id) const
<< id << " not found! Returning 0.");
return 0;
}
+ return &ret->second;
+}
+
+double Being::getAttributeBase(unsigned id) const
+{
+ AttributeMap::const_iterator ret = mAttributes.find(id);
+ if (ret == mAttributes.end())
+ {
+ LOG_DEBUG("Being::getAttributeBase: Attribute "
+ << id << " not found! Returning 0.");
+ return 0;
+ }
return ret->second.getBase();
}
@@ -594,7 +459,7 @@ void Being::recalculateBaseAttribute(unsigned attr)
{
double newBase = utils::tpsToRawSpeed(
getModifiedAttribute(ATTR_MOVE_SPEED_TPS));
- if (newBase != getAttribute(attr))
+ if (newBase != getAttributeBase(attr))
setAttribute(attr, newBase);
return;
}
@@ -742,8 +607,6 @@ void Being::update()
if (getModifiedAttribute(ATTR_HP) <= 0 && mAction != DEAD)
died();
- processAttacks();
-
Actor::update();
}
@@ -754,7 +617,3 @@ void Being::inserted(Entity *)
mOld = getPosition();
}
-void Being::processAttack(Attack &attack)
-{
- performAttack(mTarget, attack.getAttackInfo()->getDamage());
-}
diff --git a/src/game-server/being.h b/src/game-server/being.h
index 1d1f420..ce45d59 100644
--- a/src/game-server/being.h
+++ b/src/game-server/being.h
@@ -47,11 +47,6 @@ struct Status
typedef std::map< int, Status > StatusEffects;
/**
- * Type definition for a list of hits
- */
-typedef std::vector<unsigned> Hits;
-
-/**
* Generic being (living actor). Keeps direction, destination and a few other
* relevant properties. Used for characters & monsters (all animated objects).
*/
@@ -68,13 +63,6 @@ class Being : public Actor
*/
virtual void update();
- /**
- * Takes a damage structure, computes the real damage based on the
- * stats, deducts the result from the hitpoints and adds the result to
- * the HitsTaken list.
- */
- virtual int damage(Actor *source, const Damage &damage);
-
/** Restores all hit points of the being */
void heal();
@@ -87,21 +75,6 @@ class Being : public Actor
virtual void died();
/**
- * Process all available attacks
- */
- void processAttacks();
-
- /**
- * Adds an attack to the available attacks
- */
- void addAttack(AttackInfo *attack);
-
- /**
- * Removes an attack from the available attacks
- */
- void removeAttack(AttackInfo *attackInfo);
-
- /**
* Gets the destination coordinates of the being.
*/
const Point &getDestination() const
@@ -133,25 +106,6 @@ class Being : public Actor
BeingDirection getDirection() const
{ return mDirection; }
-
- /**
- * Gets the damage list.
- */
- const Hits &getHitsTaken() const
- { return mHitsTaken; }
-
- /**
- * Clears the damage list.
- */
- void clearHitsTaken()
- { mHitsTaken.clear(); }
-
- /**
- * Performs an attack.
- * Return Value: damage inflicted or -1 when illegal target
- */
- int performAttack(Being *target, const Damage &dmg);
-
/**
* Sets the current action.
*/
@@ -164,15 +118,6 @@ class Being : public Actor
{ return mAction; }
/**
- * Gets the attack id the being is currently performing.
- * For being, this is defaulted to the first one (1).
- */
- virtual int getAttackId() const
- { return mCurrentAttack ?
- mCurrentAttack->getAttackInfo()->getDamage().id : 0;
- }
-
- /**
* Moves the being toward its destination.
*/
void move();
@@ -195,9 +140,14 @@ class Being : public Actor
void setAttribute(unsigned id, double value);
/**
- * Gets an attribute.
+ * Gets an attribute or 0 if not existing.
+ */
+ const Attribute *getAttribute(unsigned id) const;
+
+ /**
+ * Gets an attribute base.
*/
- double getAttribute(unsigned id) const;
+ double getAttributeBase(unsigned id) const;
/**
* Gets an attribute after applying modifiers.
@@ -285,18 +235,6 @@ class Being : public Actor
*/
static int directionToAngle(int direction);
- /**
- * Get Target
- */
- Being *getTarget() const
- { return mTarget; }
-
- /**
- * Set Target
- */
- void setTarget(Being *target)
- { mTarget = target; }
-
static void setUpdateDerivedAttributesCallback(Script *script)
{ script->assignCallback(mRecalculateDerivedAttributesCallback); }
@@ -318,11 +256,6 @@ class Being : public Actor
protected:
/**
- * Performs an attack
- */
- virtual void processAttack(Attack &attack);
-
- /**
* Update the being direction when moving so avoid directions desyncs
* with other clients.
*/
@@ -333,14 +266,10 @@ class Being : public Actor
BeingAction mAction;
AttributeMap mAttributes;
- Attacks mAttacks;
StatusEffects mStatus;
- Being *mTarget;
Point mOld; /**< Old coordinates. */
Point mDst; /**< Target coordinates. */
BeingGender mGender; /**< Gender of the being. */
- Attack *mCurrentAttack; /**< Last used attack. */
-
private:
Being(const Being &rhs);
@@ -355,7 +284,6 @@ class Being : public Actor
BeingDirection mDirection; /**< Facing direction. */
std::string mName;
- Hits mHitsTaken; /**< List of punches taken since last update. */
/** Time until hp is regenerated again */
Timeout mHealthRegenerationTimeout;
diff --git a/src/game-server/buysell.cpp b/src/game-server/buysell.cpp
index 1a0d8d9..ed6d92e 100644
--- a/src/game-server/buysell.cpp
+++ b/src/game-server/buysell.cpp
@@ -154,15 +154,15 @@ void BuySell::perform(int id, int amount)
{
amount -= inv.remove(id, amount);
mChar->setAttribute(mCurrencyId,
- mChar->getAttribute(mCurrencyId) +
+ mChar->getAttributeBase(mCurrencyId) +
amount * i->cost);
}
else
{
- amount = std::min(amount, ((int) mChar->getAttribute(mCurrencyId)) / i->cost);
+ amount = std::min(amount, ((int) mChar->getAttributeBase(mCurrencyId)) / i->cost);
amount -= inv.insert(id, amount);
mChar->setAttribute(mCurrencyId,
- mChar->getAttribute(mCurrencyId) -
+ mChar->getAttributeBase(mCurrencyId) -
amount * i->cost);
}
if (i->amount)
diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp
index 8cc0e65..994e331 100644
--- a/src/game-server/character.cpp
+++ b/src/game-server/character.cpp
@@ -22,8 +22,10 @@
#include "common/configuration.h"
#include "game-server/accountconnection.h"
+#include "game-server/attack.h"
#include "game-server/attributemanager.h"
#include "game-server/buysell.h"
+#include "game-server/combatcomponent.h"
#include "game-server/inventory.h"
#include "game-server/item.h"
#include "game-server/itemmanager.h"
@@ -99,14 +101,13 @@ Character::Character(MessageIn &msg):
setWalkMask(Map::BLOCKMASK_WALL);
setBlockType(BLOCKTYPE_CHARACTER);
- // Get character data.
- mDatabaseID = msg.readInt32();
- setName(msg.readString());
- deserializeCharacterData(*this, msg);
- mOld = getPosition();
- Inventory(this).initialize();
- modifiedAllAttribute();
- setSize(16);
+
+ CombatComponent *combatcomponent = new CombatComponent(*this);
+ addComponent(combatcomponent);
+ combatcomponent->getAttacks().attack_added.connect(
+ sigc::mem_fun(this, &Character::attackAdded));
+ combatcomponent->getAttacks().attack_removed.connect(
+ sigc::mem_fun(this, &Character::attackRemoved));
// Default knuckle attack
int damageBase = this->getModifiedAttribute(ATTR_STR);
@@ -121,7 +122,16 @@ Character::Character(MessageIn &msg):
knuckleDamage.range = DEFAULT_TILE_LENGTH;
mKnuckleAttackInfo = new AttackInfo(0, knuckleDamage, 7, 3, 0);
- addAttack(mKnuckleAttackInfo);
+ combatcomponent->addAttack(mKnuckleAttackInfo);
+
+ // Get character data.
+ mDatabaseID = msg.readInt32();
+ setName(msg.readString());
+ deserializeCharacterData(*this, msg);
+ mOld = getPosition();
+ Inventory(this).initialize();
+ modifiedAllAttribute();
+ setSize(16);
}
Character::~Character()
@@ -199,7 +209,7 @@ void Character::respawn()
// Make it alive again
setAction(STAND);
// Reset target
- mTarget = NULL;
+ getComponent<CombatComponent>()->clearTarget();
// Execute respawn callback when set
if (executeCallback(mDeathAcceptedCallback, this))
@@ -422,7 +432,7 @@ void Character::sendStatus()
{
int attr = *i;
attribMsg.writeInt16(attr);
- attribMsg.writeInt32(getAttribute(attr) * 256);
+ attribMsg.writeInt32(getAttributeBase(attr) * 256);
attribMsg.writeInt32(getModifiedAttribute(attr) * 256);
}
if (attribMsg.getLength() > 2) gameHandler->sendTo(this, attribMsg);
@@ -497,7 +507,7 @@ void Character::flagAttribute(int attr)
{
// Inform the client of this attribute modification.
accountHandler->updateAttributes(getDatabaseID(), attr,
- getAttribute(attr),
+ getAttributeBase(attr),
getModifiedAttribute(attr));
mModifiedAttributes.insert(attr);
}
@@ -655,7 +665,7 @@ AttribmodResponseCode Character::useCharacterPoint(size_t attribute)
return ATTRIBMOD_NO_POINTS_LEFT;
--mCharacterPoints;
- setAttribute(attribute, getAttribute(attribute) + 1);
+ setAttribute(attribute, getAttributeBase(attribute) + 1);
updateDerivedAttributes(attribute);
return ATTRIBMOD_OK;
}
@@ -666,12 +676,12 @@ AttribmodResponseCode Character::useCorrectionPoint(size_t attribute)
return ATTRIBMOD_INVALID_ATTRIBUTE;
if (!mCorrectionPoints)
return ATTRIBMOD_NO_POINTS_LEFT;
- if (getAttribute(attribute) <= 1)
+ if (getAttributeBase(attribute) <= 1)
return ATTRIBMOD_DENIED;
--mCorrectionPoints;
++mCharacterPoints;
- setAttribute(attribute, getAttribute(attribute) - 1);
+ setAttribute(attribute, getAttributeBase(attribute) - 1);
updateDerivedAttributes(attribute);
return ATTRIBMOD_OK;
}
@@ -704,19 +714,20 @@ void Character::resumeNpcThread()
}
}
-void Character::addAttack(AttackInfo *attackInfo)
+void Character::attackAdded(Attack &attack)
{
// Remove knuckle attack
- Being::addAttack(attackInfo);
- Being::removeAttack(mKnuckleAttackInfo);
+ if (attack.getAttackInfo() != mKnuckleAttackInfo)
+ getComponent<CombatComponent>()->removeAttack(mKnuckleAttackInfo);
}
-void Character::removeAttack(AttackInfo *attackInfo)
+void Character::attackRemoved(Attack &attack)
{
- Being::removeAttack(attackInfo);
// Add knuckle attack
- if (mAttacks.getNumber() == 0)
- Being::addAttack(mKnuckleAttackInfo);
+ CombatComponent *combatComponent = getComponent<CombatComponent>();
+ // 1 since the attack is not really removed yet.
+ if (combatComponent->getAttacks().getNumber() == 1)
+ combatComponent->addAttack(mKnuckleAttackInfo);
}
void Character::disconnected()
diff --git a/src/game-server/character.h b/src/game-server/character.h
index dc703c8..8b015f9 100644
--- a/src/game-server/character.h
+++ b/src/game-server/character.h
@@ -425,9 +425,9 @@ class Character : public Being
void triggerLoginCallback();
- virtual void addAttack(AttackInfo *attackInfo);
+ void attackAdded(Attack &attackInfo);
- virtual void removeAttack(AttackInfo *attackInfo);
+ void attackRemoved(Attack &attackInfo);
sigc::signal<void, Character *> signal_disconnected;
diff --git a/src/game-server/combatcomponent.cpp b/src/game-server/combatcomponent.cpp
new file mode 100644
index 0000000..199126f
--- /dev/null
+++ b/src/game-server/combatcomponent.cpp
@@ -0,0 +1,213 @@
+/*
+ * The Mana Server
+ * Copyright (C) 2013 The Mana Developers
+ *
+ * This file is part of The Mana Server.
+ *
+ * The Mana Server is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "combatcomponent.h"
+
+#include "game-server/being.h"
+#include "game-server/mapcomposite.h"
+
+#include "utils/logger.h"
+
+CombatComponent::CombatComponent(Being &being):
+ mTarget(0),
+ mCurrentAttack(0)
+{
+ being.signal_died.connect(sigc::mem_fun(this,
+ &CombatComponent::diedOrRemoved));
+ being.signal_removed.connect(sigc::mem_fun(this,
+ &CombatComponent::diedOrRemoved));
+}
+
+CombatComponent::~CombatComponent()
+{
+}
+
+void CombatComponent::update(Entity &entity)
+{
+ // Temponary for as long as Being is not split into Components
+ // Prevents to implement all at once
+ // TODO: remove this as soon as possible
+ Being &being = static_cast<Being&>(entity);
+
+
+ if (being.getAction() != ATTACK || !mTarget)
+ return;
+
+ std::vector<Attack *> attacksReady;
+ mAttacks.getUsuableAttacks(&attacksReady);
+
+ if (Attack *triggerableAttack = mAttacks.getTriggerableAttack())
+ {
+ processAttack(being, *triggerableAttack);
+ mAttacks.markAttackAsTriggered();
+ }
+
+ // Deal with the ATTACK action.
+ if (attacksReady.empty())
+ return;
+
+ Attack *highestPriorityAttack = 0;
+ // Performs all ready attacks.
+ for (std::vector<Attack *>::const_iterator it = attacksReady.begin(),
+ it_end = attacksReady.end(); it != it_end; ++it)
+ {
+ // check if target is in range using the pythagorean theorem
+ int distx = being.getPosition().x - mTarget->getPosition().x;
+ int disty = being.getPosition().y - mTarget->getPosition().y;
+ int distSquare = (distx * distx + disty * disty);
+ AttackInfo *info = (*it)->getAttackInfo();
+ int maxDist = info->getDamage().range + being.getSize();
+
+ if (distSquare <= maxDist * maxDist &&
+ (!highestPriorityAttack ||
+ highestPriorityAttack->getAttackInfo()->getPriority()
+ < info->getPriority()))
+ {
+ highestPriorityAttack = *it;
+ }
+ }
+ if (highestPriorityAttack)
+ {
+ mAttacks.startAttack(highestPriorityAttack);
+ mCurrentAttack = highestPriorityAttack;
+ being.setDestination(being.getPosition());
+ // TODO: Turn into direction of enemy
+ being.raiseUpdateFlags(UPDATEFLAG_ATTACK);
+ }
+}
+
+/**
+ * Takes a damage structure, computes the real damage based on the
+ * stats, deducts the result from the hitpoints and adds the result to
+ * the HitsTaken list.
+ */
+int CombatComponent::damage(Being &target, Being *source, const Damage &damage)
+{
+ int HPloss = damage.base;
+ if (damage.delta)
+ HPloss += rand() * (damage.delta + 1) / RAND_MAX;
+
+ // TODO magical attacks and associated elemental modifiers
+ switch (damage.type)
+ {
+ case DAMAGE_PHYSICAL:
+ if (!damage.trueStrike &&
+ rand()%((int) target.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 *
+ target.getModifiedAttribute(ATTR_DEFENSE)) /
+ (1.0 + 0.017 *
+ target.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:
+#if 0
+ target.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
+ break;
+ case DAMAGE_DIRECT:
+ break;
+ default:
+ LOG_WARN("Unknown damage type '" << damage.type << "'!");
+ break;
+ }
+
+ if (HPloss > 0)
+ {
+ mHitsTaken.push_back(HPloss);
+ const Attribute *HP = target.getAttribute(ATTR_HP);
+ LOG_DEBUG("Being " << target.getPublicID() << " suffered " << HPloss
+ << " damage. HP: "
+ << HP->getModifiedAttribute() << "/"
+ << target.getModifiedAttribute(ATTR_MAX_HP));
+ target.setAttribute(ATTR_HP, HP->getBase() - HPloss);
+ // No HP regen after being hit if this is set.
+ // TODO: Reenable this once the attributes are available as a component
+ // A bit too fuzzy to implement at the moment
+ //mHealthRegenerationTimeout.setSoft(
+ // Configuration::getValue("game_hpRegenBreakAfterHit", 0));
+ }
+ else
+ {
+ HPloss = 0;
+ }
+
+ signal_damaged.emit(source, damage, HPloss);
+ return HPloss;
+}
+
+/**
+ * Performs an attack
+ */
+void CombatComponent::processAttack(Being &source, Attack &attack)
+{
+ performAttack(source, attack.getAttackInfo()->getDamage());
+}
+
+/**
+ * Adds an attack to the available attacks
+ */
+void CombatComponent::addAttack(AttackInfo *attackInfo)
+{
+ mAttacks.add(attackInfo);
+}
+
+/**
+ * Removes an attack from the available attacks
+ */
+void CombatComponent::removeAttack(AttackInfo *attackInfo)
+{
+ mAttacks.remove(attackInfo);
+}
+
+/**
+ * Performs an attack.
+ */
+int CombatComponent::performAttack(Being &source, const Damage &dmg)
+{
+ // check target legality
+ if (!mTarget
+ || mTarget == &source
+ || mTarget->getAction() == DEAD
+ || !mTarget->canFight())
+ return -1;
+
+ if (source.getMap()->getPvP() == PVP_NONE
+ && mTarget->getType() == OBJECT_CHARACTER
+ && source.getType() == OBJECT_CHARACTER)
+ return -1;
+
+ return mTarget->getComponent<CombatComponent>()->damage(*mTarget,
+ &source, dmg);
+}
diff --git a/src/game-server/combatcomponent.h b/src/game-server/combatcomponent.h
new file mode 100644
index 0000000..033b543
--- /dev/null
+++ b/src/game-server/combatcomponent.h
@@ -0,0 +1,144 @@
+/*
+ * The Mana Server
+ * Copyright (C) 2013 The Mana Developers
+ *
+ * This file is part of The Mana Server.
+ *
+ * The Mana Server is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef COMBATCOMPONENT_H
+#define COMBATCOMPONENT_H
+
+#include "component.h"
+
+#include <vector>
+
+#include <sigc++/trackable.h>
+
+#include "game-server/attack.h"
+
+class Actor;
+class Being;
+
+/**
+ * Type definition for a list of hits
+ */
+typedef std::vector<unsigned> Hits;
+
+class CombatComponent: public Component, public sigc::trackable
+{
+public:
+ static const ComponentType type = CT_Fighting;
+
+ CombatComponent(Being &being);
+ virtual ~CombatComponent();
+
+ void update(Entity &entity);
+
+ void addAttack(AttackInfo *attack);
+ void removeAttack(AttackInfo *attackInfo);
+ Attacks &getAttacks();
+
+ const Hits &getHitsTaken() const;
+ void clearHitsTaken();
+
+ int performAttack(Being &source, const Damage &dmg);
+ virtual int damage(Being &target, Being *source, const Damage &damage);
+
+ int getAttackId() const;
+
+ Being *getTarget() const;
+ void setTarget(Being *target);
+ void clearTarget();
+
+ void diedOrRemoved(Entity *entity);
+
+ sigc::signal<void, Being *, const Damage &, int> signal_damaged;
+
+protected:
+ virtual void processAttack(Being &source, Attack &attack);
+
+ Being *mTarget;
+ Attacks mAttacks;
+ Attack *mCurrentAttack; // Last used attack
+ Hits mHitsTaken; //List of punches taken since last update.
+
+};
+
+inline Attacks &CombatComponent::getAttacks()
+{
+ return mAttacks;
+}
+
+/**
+ * Gets the damage list.
+ */
+inline const Hits &CombatComponent::getHitsTaken() const
+{
+ return mHitsTaken;
+}
+
+/**
+ * Clears the damage list.
+ */
+inline void CombatComponent::clearHitsTaken()
+{
+ mHitsTaken.clear();
+}
+
+/**
+ * Gets the attack id the being is currently performing.
+ * For being, this is defaulted to the first one (1).
+ */
+inline int CombatComponent::getAttackId() const
+{
+ return mCurrentAttack ?
+ mCurrentAttack->getAttackInfo()->getDamage().id : 0;
+}
+
+/**
+ * Get Target
+ */
+inline Being *CombatComponent::getTarget() const
+{
+ return mTarget;
+}
+
+/**
+ * Set Target
+ */
+inline void CombatComponent::setTarget(Being *target)
+{
+ mTarget = target;
+}
+
+/**
+ * Clears the target
+ */
+inline void CombatComponent::clearTarget()
+{
+ mTarget = 0;
+}
+
+/**
+ * Handler for the died and removed event of the targeting being
+ * @param entity The removed/died being (not used here)
+ */
+inline void CombatComponent::diedOrRemoved(Entity *entity)
+{
+ clearTarget();
+}
+
+#endif // COMBATCOMPONENT_H
diff --git a/src/game-server/commandhandler.cpp b/src/game-server/commandhandler.cpp
index 001f65f..353c3ef 100644
--- a/src/game-server/commandhandler.cpp
+++ b/src/game-server/commandhandler.cpp
@@ -670,7 +670,7 @@ static void handleMoney(Character *player, std::string &args)
value = utils::stringToInt(valuestr);
// change how much money the player has
- other->setAttribute(ATTR_GP , other->getAttribute(ATTR_GP) + value);
+ other->setAttribute(ATTR_GP , other->getAttributeBase(ATTR_GP) + value);
// log transaction
std::string msg = "User created " + valuestr + " money";
diff --git a/src/game-server/component.h b/src/game-server/component.h
index 740668a..47e9dd0 100644
--- a/src/game-server/component.h
+++ b/src/game-server/component.h
@@ -26,6 +26,7 @@ class Entity;
enum ComponentType
{
CT_Effect,
+ CT_Fighting,
CT_Item,
CT_Npc,
CT_SpawnArea,
diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp
index 51b64a2..9cec772 100644
--- a/src/game-server/gamehandler.cpp
+++ b/src/game-server/gamehandler.cpp
@@ -27,6 +27,7 @@
#include "common/transaction.h"
#include "game-server/accountconnection.h"
#include "game-server/buysell.h"
+#include "game-server/combatcomponent.h"
#include "game-server/commandhandler.h"
#include "game-server/emotemanager.h"
#include "game-server/inventory.h"
@@ -649,7 +650,7 @@ void GameHandler::handleAttack(GameClient &client, MessageIn &message)
Being *being = findBeingNear(client.character, id);
if (being && being->getType() != OBJECT_NPC)
{
- client.character->setTarget(being);
+ client.character->getComponent<CombatComponent>()->setTarget(being);
client.character->setAction(ATTACK);
}
}
diff --git a/src/game-server/item.cpp b/src/game-server/item.cpp
index b7b0d78..7295b95 100644
--- a/src/game-server/item.cpp
+++ b/src/game-server/item.cpp
@@ -25,6 +25,7 @@
#include "game-server/attack.h"
#include "game-server/attributemanager.h"
#include "game-server/being.h"
+#include "game-server/combatcomponent.h"
#include "game-server/state.h"
#include "scripting/script.h"
#include "scripting/scriptmanager.h"
@@ -49,13 +50,13 @@ void ItemEffectAttrMod::dispell(Being *itemUser)
bool ItemEffectAttack::apply(Being *itemUser)
{
- itemUser->addAttack(mAttackInfo);
+ itemUser->getComponent<CombatComponent>()->addAttack(mAttackInfo);
return false;
}
void ItemEffectAttack::dispell(Being *itemUser)
{
- itemUser->removeAttack(mAttackInfo);
+ itemUser->getComponent<CombatComponent>()->removeAttack(mAttackInfo);
}
ItemEffectScript::~ItemEffectScript()
diff --git a/src/game-server/mapcomposite.cpp b/src/game-server/mapcomposite.cpp
index a1a3f98..f1b545e 100644
--- a/src/game-server/mapcomposite.cpp
+++ b/src/game-server/mapcomposite.cpp
@@ -26,6 +26,7 @@
#include "common/configuration.h"
#include "common/resourcemanager.h"
#include "game-server/character.h"
+#include "game-server/combatcomponent.h"
#include "game-server/mapcomposite.h"
#include "game-server/map.h"
#include "game-server/mapmanager.h"
@@ -591,9 +592,9 @@ void MapComposite::remove(Entity *ptr)
if ((*i)->canFight())
{
Being *being = static_cast<Being*>(*i);
- if (being->getTarget() == ptr)
+ if (being->getComponent<CombatComponent>()->getTarget() == ptr)
{
- being->setTarget(NULL);
+ being->getComponent<CombatComponent>()->clearTarget();
}
}
if (*i == ptr)
diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp
index ceae82c..25abb68 100644
--- a/src/game-server/monster.cpp
+++ b/src/game-server/monster.cpp
@@ -28,6 +28,7 @@
#include "game-server/item.h"
#include "game-server/map.h"
#include "game-server/mapcomposite.h"
+#include "game-server/monstercombatcomponent.h"
#include "game-server/state.h"
#include "scripting/scriptmanager.h"
#include "utils/logger.h"
@@ -98,9 +99,6 @@ Monster::Monster(MonsterClass *specy):
}
}
- mDamageMutation = mutation ?
- (100 + (rand() % (mutation * 2)) - mutation) / 100.0 : 1;
-
setSize(specy->getSize());
setGender(specy->getGender());
@@ -111,13 +109,16 @@ Monster::Monster(MonsterClass *specy):
mAttackPositions.push_back(AttackPosition(0, -dist, DOWN));
mAttackPositions.push_back(AttackPosition(0, dist, UP));
- // Take attacks from specy
- std::vector<AttackInfo *> &attacks = specy->getAttackInfos();
- for (std::vector<AttackInfo *>::iterator it = attacks.begin(),
- it_end = attacks.end(); it != it_end; ++it)
- {
- addAttack(*it);
- }
+ MonsterCombatComponent *combatComponent =
+ new MonsterCombatComponent(*this);
+ addComponent(combatComponent);
+
+ double damageMutation = mutation ?
+ (100.0 + (rand() % (mutation * 2)) - mutation) / 100.0 : 1.0;
+ combatComponent->setDamageMutation(damageMutation);
+
+ combatComponent->signal_damaged.connect(
+ sigc::mem_fun(this, &Monster::receivedDamage));
}
Monster::~Monster()
@@ -151,7 +152,7 @@ void Monster::update()
refreshTarget();
// Cancel the rest when we have a target
- if (mTarget)
+ if (getComponent<CombatComponent>()->getTarget())
return;
// We have no target - let's wander around
@@ -188,7 +189,7 @@ void Monster::refreshTarget()
Point bestAttackPosition;
// reset Target. We will find a new one if possible
- mTarget = 0;
+ getComponent<CombatComponent>()->clearTarget();
// Iterate through objects nearby
int aroundArea = Configuration::getValue("game_visualRange", 448);
@@ -242,11 +243,11 @@ void Monster::refreshTarget()
}
if (bestTarget)
{
- mTarget = bestTarget;
+ getComponent<CombatComponent>()->setTarget(bestTarget);
if (bestAttackPosition == getPosition())
{
mAction = ATTACK;
- updateDirection(getPosition(), mTarget->getPosition());
+ updateDirection(getPosition(), bestTarget->getPosition());
}
else
{
@@ -255,35 +256,6 @@ void Monster::refreshTarget()
}
}
-void Monster::processAttack(Attack &attack)
-{
- if (!mTarget)
- {
- setAction(STAND);
- return;
- }
-
- Damage dmg = attack.getAttackInfo()->getDamage();
- dmg.skill = 0;
- dmg.base *= mDamageMutation;
- dmg.delta *= mDamageMutation;
-
- int hit = performAttack(mTarget, attack.getAttackInfo()->getDamage());
-
- const Script::Ref &scriptCallback =
- attack.getAttackInfo()->getScriptCallback();
-
- if (scriptCallback.isValid() && hit > -1)
- {
- Script *script = ScriptManager::currentState();
- script->prepare(scriptCallback);
- script->push(this);
- script->push(mTarget);
- script->push(hit);
- script->execute(getMap());
- }
-}
-
int Monster::calculatePositionPriority(Point position, int targetPriority)
{
Point thisPos = getPosition();
@@ -375,44 +347,6 @@ std::map<Being *, int> Monster::getAngerList() const
return result;
}
-int Monster::damage(Actor *source, const Damage &damage)
-{
- Damage newDamage = damage;
- float factor = mSpecy->getVulnerability(newDamage.element);
- newDamage.base = newDamage.base * factor;
- newDamage.delta = newDamage.delta * factor;
- int HPLoss = Being::damage(source, newDamage);
- if (source)
- changeAnger(source, HPLoss);
-
- if (HPLoss && source && source->getType() == OBJECT_CHARACTER)
- {
- Character *s = static_cast< Character * >(source);
-
- mExpReceivers[s].insert(damage.skill);
- if (mKillStealProtectedTimeout.expired() || mOwner == s
- || mOwner->getParty() == s->getParty())
- {
- mOwner = s;
- mLegalExpReceivers.insert(s);
- mKillStealProtectedTimeout.set(KILLSTEAL_PROTECTION_TIME);
- }
- }
-
- if (mSpecy->getDamageCallback().isValid())
- {
- Script *script = ScriptManager::currentState();
- script->prepare(mSpecy->getDamageCallback());
- script->push(this);
- script->push(source);
- script->push(HPLoss);
- // TODO: add exact damage parameters as well
- script->execute(getMap());
- }
-
- return HPLoss;
-}
-
void Monster::died()
{
if (mAction == DEAD)
@@ -467,3 +401,24 @@ void Monster::died()
}
}
}
+
+
+void Monster::receivedDamage(Being *source, const Damage &damage, int hpLoss)
+{
+ if (source)
+ changeAnger(source, hpLoss);
+
+ if (hpLoss && source && source->getType() == OBJECT_CHARACTER)
+ {
+ Character *s = static_cast< Character * >(source);
+
+ mExpReceivers[s].insert(damage.skill);
+ if (mKillStealProtectedTimeout.expired() || mOwner == s
+ || mOwner->getParty() == s->getParty())
+ {
+ mOwner = s;
+ mLegalExpReceivers.insert(s);
+ mKillStealProtectedTimeout.set(KILLSTEAL_PROTECTION_TIME);
+ }
+ }
+}
diff --git a/src/game-server/monster.h b/src/game-server/monster.h
index e87180d..3bd34a9 100644
--- a/src/game-server/monster.h
+++ b/src/game-server/monster.h
@@ -292,15 +292,12 @@ class Monster : public Being
void refreshTarget();
/**
- * Performs an attack
- */
- virtual void processAttack(Attack &attack);
-
- /**
* Kills the being.
*/
void died();
+ void receivedDamage(Being *attacker, const Damage &damage, int hpLoss);
+
/**
* Alters hate for the monster
*/
@@ -309,11 +306,6 @@ class Monster : public Being
std::map<Being *, int> getAngerList() const;
/**
- * Calls the damage function in Being and updates the aggro list
- */
- virtual int damage(Actor *source, const Damage &damage);
-
- /**
* Removes a being from the anger list.
*/
void forgetTarget(Entity *entity);
@@ -342,9 +334,6 @@ class Monster : public Being
*/
Character *mOwner;
- /** Factor for damage mutation */
- unsigned mDamageMutation;
-
/** List of characters and their skills that attacked this monster. */
std::map<Character *, std::set <size_t> > mExpReceivers;
diff --git a/src/game-server/monstercombatcomponent.cpp b/src/game-server/monstercombatcomponent.cpp
new file mode 100644
index 0000000..dc66955
--- /dev/null
+++ b/src/game-server/monstercombatcomponent.cpp
@@ -0,0 +1,99 @@
+/*
+ * The Mana Server
+ * Copyright (C) 2013 The Mana Developers
+ *
+ * This file is part of The Mana Server.
+ *
+ * The Mana Server is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "game-server/monstercombatcomponent.h"
+
+#include "game-server/monster.h"
+#include "scripting/scriptmanager.h"
+
+MonsterCombatComponent::MonsterCombatComponent(Monster &monster):
+ CombatComponent(monster),
+ mDamageMutation(0)
+{
+ // Take attacks from specy
+ std::vector<AttackInfo *> &attacks = monster.getSpecy()->getAttackInfos();
+ for (std::vector<AttackInfo *>::iterator it = attacks.begin(),
+ it_end = attacks.end(); it != it_end; ++it)
+ {
+ addAttack(*it);
+ }
+}
+
+/**
+ * Performs an attack
+ */
+void MonsterCombatComponent::processAttack(Being *source, Attack &attack)
+{
+ if (!mTarget)
+ {
+ source->setAction(STAND);
+ return;
+ }
+
+ Damage dmg = attack.getAttackInfo()->getDamage();
+ dmg.skill = 0;
+ dmg.base *= mDamageMutation;
+ dmg.delta *= mDamageMutation;
+
+ int hit = performAttack(*mTarget, attack.getAttackInfo()->getDamage());
+
+ const Script::Ref &scriptCallback =
+ attack.getAttackInfo()->getScriptCallback();
+
+ if (scriptCallback.isValid() && hit > -1)
+ {
+ Script *script = ScriptManager::currentState();
+ script->prepare(scriptCallback);
+ script->push(source);
+ script->push(mTarget);
+ script->push(hit);
+ script->execute(source->getMap());
+ }
+}
+
+/**
+ * Calls the damage function in Being and updates the aggro list
+ */
+int MonsterCombatComponent::damage(Being &target,
+ Being *source,
+ const Damage &damage)
+{
+ // Temporarily depend on monster as long as it does not exist as a component
+ Monster &monster = static_cast<Monster &>(target);
+ Damage newDamage = damage;
+ MonsterClass *specy = monster.getSpecy();
+ float factor = specy->getVulnerability(newDamage.element);
+ newDamage.base = newDamage.base * factor;
+ newDamage.delta = newDamage.delta * factor;
+ int hpLoss = CombatComponent::damage(target, source, newDamage);
+
+
+ if (specy->getDamageCallback().isValid())
+ {
+ Script *script = ScriptManager::currentState();
+ script->prepare(specy->getDamageCallback());
+ script->push(&target);
+ script->push(source);
+ script->push(hpLoss);
+ // TODO: add exact damage parameters as well
+ script->execute(monster.getMap());
+ }
+ return hpLoss;
+}
diff --git a/src/game-server/monstercombatcomponent.h b/src/game-server/monstercombatcomponent.h
new file mode 100644
index 0000000..766c773
--- /dev/null
+++ b/src/game-server/monstercombatcomponent.h
@@ -0,0 +1,54 @@
+/*
+ * The Mana Server
+ * Copyright (C) 2013 The Mana Developers
+ *
+ * This file is part of The Mana Server.
+ *
+ * The Mana Server is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MONSTERCOMBATCOMPONENT_H
+#define MONSTERCOMBATCOMPONENT_H
+
+#include "game-server/combatcomponent.h"
+
+#include "game-server/attack.h"
+#include "game-server/being.h"
+
+class Monster;
+
+class MonsterCombatComponent: public CombatComponent
+{
+public:
+ MonsterCombatComponent(Monster &monster);
+
+ void processAttack(Being *source, Attack &attack);
+ int damage(Being &target, Being *source, const Damage &damage);
+
+ void setDamageMutation(double mutation);
+
+private:
+ double mDamageMutation;
+};
+
+/**
+ * Sets the mutation of the damage compared to the default damage of the specy
+ * @param mutation
+ */
+inline void MonsterCombatComponent::setDamageMutation(double mutation)
+{
+ mDamageMutation = mutation;
+}
+
+#endif /* MONSTERCOMBATCOMPONENT_H */
diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp
index 673d48f..eaffe77 100644
--- a/src/game-server/state.cpp
+++ b/src/game-server/state.cpp
@@ -22,11 +22,12 @@
#include "common/configuration.h"
#include "game-server/accountconnection.h"
+#include "game-server/effect.h"
+#include "game-server/combatcomponent.h"
#include "game-server/gamehandler.h"
#include "game-server/inventory.h"
#include "game-server/item.h"
#include "game-server/itemmanager.h"
-#include "game-server/effect.h"
#include "game-server/map.h"
#include "game-server/mapcomposite.h"
#include "game-server/mapmanager.h"
@@ -166,7 +167,9 @@ static void informPlayer(MapComposite *map, Character *p)
MessageOut AttackMsg(GPMSG_BEING_ATTACK);
AttackMsg.writeInt16(oid);
AttackMsg.writeInt8(o->getDirection());
- AttackMsg.writeInt8(static_cast< Being * >(o)->getAttackId());
+ CombatComponent *combatComponent =
+ o->getComponent<CombatComponent>();
+ AttackMsg.writeInt8(combatComponent->getAttackId());
gameHandler->sendTo(p, AttackMsg);
}
@@ -217,8 +220,9 @@ static void informPlayer(MapComposite *map, Character *p)
// Send damage messages.
if (o->canFight())
{
- Being *victim = static_cast< Being * >(o);
- const Hits &hits = victim->getHitsTaken();
+ CombatComponent *combatComponent =
+ o->getComponent<CombatComponent>();
+ const Hits &hits = combatComponent->getHitsTaken();
for (Hits::const_iterator j = hits.begin(),
j_end = hits.end(); j != j_end; ++j)
{
@@ -462,7 +466,7 @@ void GameState::update(int tick)
a->clearUpdateFlags();
if (a->canFight())
{
- static_cast< Being * >(a)->clearHitsTaken();
+ a->getComponent<CombatComponent>()->clearHitsTaken();
}
}
}
diff --git a/src/game-server/trade.cpp b/src/game-server/trade.cpp
index e1779d2..0b906b1 100644
--- a/src/game-server/trade.cpp
+++ b/src/game-server/trade.cpp
@@ -131,14 +131,14 @@ void Trade::agree(Character *c)
// Check if both player has the objects in their inventories
// and enouth money, then swap them.
Inventory v1(mChar1), v2(mChar2);
- if (mChar1->getAttribute(mCurrencyId) >= mMoney1 - mMoney2 &&
- mChar2->getAttribute(mCurrencyId) >= mMoney2 - mMoney1 &&
+ if (mChar1->getAttributeBase(mCurrencyId) >= mMoney1 - mMoney2 &&
+ mChar2->getAttributeBase(mCurrencyId) >= mMoney2 - mMoney1 &&
perform(mItems1, v1, v2) &&
perform(mItems2, v2, v1))
{
- mChar1->setAttribute(mCurrencyId, mChar1->getAttribute(mCurrencyId)
+ mChar1->setAttribute(mCurrencyId, mChar1->getAttributeBase(mCurrencyId)
- mMoney1 + mMoney2);
- mChar2->setAttribute(mCurrencyId, mChar2->getAttribute(mCurrencyId)
+ mChar2->setAttribute(mCurrencyId, mChar2->getAttributeBase(mCurrencyId)
- mMoney2 + mMoney1);
}
else