summaryrefslogtreecommitdiff
path: root/src/game-server
diff options
context:
space:
mode:
authorPhilipp Sehmisch <tmw@crushnet.org>2008-09-27 03:55:19 +0000
committerPhilipp Sehmisch <tmw@crushnet.org>2008-09-27 03:55:19 +0000
commit29ae6d3f42ac55e7bc8c5ad0b044c7b0e662d358 (patch)
tree41ca7bb0f8b557e096a2b783f6c94ea4d3e5006a /src/game-server
parentc27d73abc52bdc463a029a5f6a95e1db5df5abf6 (diff)
downloadmanaserv-29ae6d3f42ac55e7bc8c5ad0b044c7b0e662d358.tar.gz
manaserv-29ae6d3f42ac55e7bc8c5ad0b044c7b0e662d358.tar.bz2
manaserv-29ae6d3f42ac55e7bc8c5ad0b044c7b0e662d358.tar.xz
manaserv-29ae6d3f42ac55e7bc8c5ad0b044c7b0e662d358.zip
Implemented getting the attack zone of weapons from the item database and implemented single target attacks useful for projectile weapons like bows.
Diffstat (limited to 'src/game-server')
-rw-r--r--src/game-server/attackzone.cpp0
-rw-r--r--src/game-server/attackzone.hpp46
-rw-r--r--src/game-server/being.cpp59
-rw-r--r--src/game-server/being.hpp3
-rw-r--r--src/game-server/character.cpp12
-rw-r--r--src/game-server/character.hpp2
-rw-r--r--src/game-server/item.cpp6
-rw-r--r--src/game-server/item.hpp18
-rw-r--r--src/game-server/itemmanager.cpp41
-rw-r--r--src/game-server/monster.cpp4
-rw-r--r--src/game-server/monster.hpp5
-rw-r--r--src/game-server/monstermanager.cpp9
12 files changed, 185 insertions, 20 deletions
diff --git a/src/game-server/attackzone.cpp b/src/game-server/attackzone.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/game-server/attackzone.cpp
diff --git a/src/game-server/attackzone.hpp b/src/game-server/attackzone.hpp
new file mode 100644
index 00000000..2bdf3b14
--- /dev/null
+++ b/src/game-server/attackzone.hpp
@@ -0,0 +1,46 @@
+/*
+ * The Mana World Server
+ * Copyright 2008 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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 World 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 World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+#ifndef _TMWSERV_ATTACKZONE
+#define _TMWSERV_ATTACKZONE
+
+
+/**
+ * Enumeration of different types of attack damage zones
+ */
+enum AttackZoneShape
+{
+ ATTZONESHAPE_CONE
+ //yes, there is currently only one but it is planned to add different kinds of attack zones
+};
+
+struct AttackZone
+{
+ AttackZoneShape shape;
+ bool multiTarget;
+ int range;
+ int angle;
+};
+
+
+#endif // _TMWSERV_ATTACKZONE
diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp
index a6ba4023..bcbbc6d9 100644
--- a/src/game-server/being.cpp
+++ b/src/game-server/being.cpp
@@ -25,6 +25,7 @@
#include "game-server/being.hpp"
#include "defines.h"
+#include "game-server/attackzone.hpp"
#include "game-server/collisiondetection.hpp"
#include "game-server/eventlistener.hpp"
#include "game-server/mapcomposite.hpp"
@@ -126,7 +127,7 @@ void Being::move()
}
}
-void Being::performAttack(Damage const &damage, int range, int angle)
+void Being::performAttack(Damage const &damage, AttackZone const *attackZone)
{
Point ppos = getPosition();
int dir = getDirection();
@@ -151,8 +152,10 @@ void Being::performAttack(Damage const &damage, int range, int angle)
break;
}
+ std::list<Being *> victims;
+
for (MovingObjectIterator
- i(getMap()->getAroundObjectIterator(this, range)); i; ++i)
+ i(getMap()->getAroundObjectIterator(this, attackZone->range)); i; ++i)
{
MovingObject *o = *i;
if (o == this) continue;
@@ -163,13 +166,55 @@ void Being::performAttack(Damage const &damage, int range, int angle)
Point opos = o->getPosition();
- if (Collision::diskWithCircleSector(
- opos, o->getSize(),
- ppos, range, angle, attackAngle)
- )
+ switch (attackZone->shape)
+ {
+ case ATTZONESHAPE_CONE:
+ if (Collision::diskWithCircleSector(
+ opos, o->getSize(),
+ ppos, attackZone->range,
+ attackZone->angle/2, attackAngle)
+ )
+ {
+ victims.push_back(static_cast< Being * >(o));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (attackZone->multiTarget)
+ {
+ // damage everyone
+ for (std::list<Being *>::iterator i = victims.begin();
+ i != victims.end();
+ i++)
+ {
+ (*i)->damage(this, damage);
+ }
+ }
+ else
+ {
+ // find the closest and damage this one
+ Being* closestVictim = NULL;
+ int closestDistance = INT_MAX;
+ for (std::list<Being *>::iterator i = victims.begin();
+ i != victims.end();
+ i++)
{
- static_cast< Being * >(o)->damage(this, damage);
+ Point opos = (*i)->getPosition();
+ int distance = abs(opos.x - ppos.x) + abs(opos.y - ppos.y);
+ /* not using pythagoras here is a) faster and b) results in more natural
+ target selection because targets closer to the center line of the
+ attack angle are prioritized
+ */
+ if (distance < closestDistance)
+ {
+ closestVictim = (*i);
+ closestDistance = distance;
+ }
}
+ if (closestVictim) closestVictim->damage(this, damage);
}
}
diff --git a/src/game-server/being.hpp b/src/game-server/being.hpp
index 870fd78a..58535487 100644
--- a/src/game-server/being.hpp
+++ b/src/game-server/being.hpp
@@ -31,6 +31,7 @@
class Being;
class MapComposite;
+class AttackZone;
/**
* Beings and actors directions
@@ -158,7 +159,7 @@ class Being : public MovingObject
/**
* Performs an attack.
*/
- void performAttack(Damage const &, int range, int angle);
+ void performAttack(Damage const &, AttackZone const *attackZone);
/**
* Sets the current action.
diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp
index 37fb3b8c..8758b64e 100644
--- a/src/game-server/character.cpp
+++ b/src/game-server/character.cpp
@@ -28,6 +28,7 @@
#include "game-server/character.hpp"
#include "defines.h"
+#include "game-server/attackzone.hpp"
#include "game-server/buysell.hpp"
#include "game-server/eventlistener.hpp"
#include "game-server/inventory.hpp"
@@ -48,6 +49,7 @@
const float Character::EXPCURVE_EXPONENT = 3.0f;
const float Character::EXPCURVE_FACTOR = 10.0f;
const float Character::LEVEL_SKILL_PRECEDENCE_FACTOR = 0.75f;
+const AttackZone Character::UNARMED_ATTACK_ZONE = {ATTZONESHAPE_CONE, true, 32, 90};
Character::Character(MessageIn &msg):
Being(OBJECT_CHARACTER, 65535),
@@ -102,21 +104,21 @@ void Character::perform()
damage.cth = getModifiedAttribute(BASE_ATTR_HIT) +
getModifiedAttribute(CHAR_SKILL_WEAPON_BEGIN + type);
damage.usedSkill = CHAR_SKILL_WEAPON_BEGIN + type;
- if (type)
+
+ if (ic)
{
+ // weapon fighting
ItemModifiers const &mods = ic->getModifiers();
damage.element = mods.getValue(MOD_ELEMENT_TYPE);
+ performAttack(damage, ic->getAttackZone());
}
else
{
// No-weapon fighting.
damage.element = ELEMENT_NEUTRAL;
+ performAttack(damage, &UNARMED_ATTACK_ZONE);
}
- int attackRange = 60; //TODO: get from weapon
- int attackAngle = 30; //TODO: get from weapon
-
- performAttack(damage, attackRange, attackAngle);
}
void Character::respawn()
diff --git a/src/game-server/character.hpp b/src/game-server/character.hpp
index d3b31276..30914344 100644
--- a/src/game-server/character.hpp
+++ b/src/game-server/character.hpp
@@ -288,6 +288,8 @@ class Character : public Being
static const int CORRECTIONPOINTS_PER_LEVELUP = 2;
static const int CORRECTIONPOINTS_MAX = 10;
+ static const AttackZone UNARMED_ATTACK_ZONE;
+
/**
* Advances the character by one level;
*/
diff --git a/src/game-server/item.cpp b/src/game-server/item.cpp
index f14374b6..4d0530f8 100644
--- a/src/game-server/item.cpp
+++ b/src/game-server/item.cpp
@@ -27,6 +27,7 @@
#include "game-server/item.hpp"
+#include "game-server/attackzone.hpp"
#include "game-server/being.hpp"
WeaponType weaponTypeFromString (const std::string &name)
@@ -136,6 +137,11 @@ void ItemModifiers::cancelAttributes(Being *b) const
}
}
+ItemClass::~ItemClass()
+{
+ if (mAttackZone) delete mAttackZone;
+}
+
bool ItemClass::use(Being *itemUser)
{
if (mType != ITEM_USABLE) return false;
diff --git a/src/game-server/item.hpp b/src/game-server/item.hpp
index 7b21ad87..e0d8943f 100644
--- a/src/game-server/item.hpp
+++ b/src/game-server/item.hpp
@@ -28,6 +28,7 @@
#include "game-server/object.hpp"
+class AttackZone;
class Being;
/**
@@ -175,9 +176,11 @@ class ItemClass
{
public:
ItemClass(int id, ItemType type)
- : mDatabaseID(id), mType(type)
+ : mDatabaseID(id), mType(type), mAttackZone(NULL)
{}
+ ~ItemClass();
+
/**
* Applies the modifiers of an item to a given user.
* @return true if the item was sucessfully used and should be removed.
@@ -256,6 +259,18 @@ class ItemClass
int getSpriteID()
{ return mSpriteID; }
+ /**
+ * Set attack zone (only needed when the item is a weapon)
+ */
+ void setAttackZone(AttackZone* attackZone) { mAttackZone = attackZone; }
+
+ /**
+ * Gets attack zone of weapon (returns NULL for non-weapon items)
+ */
+ AttackZone const *getAttackZone() const
+ { return mAttackZone ; }
+
+
private:
// Item reference information
@@ -266,6 +281,7 @@ class ItemClass
unsigned short mCost; /**< Unit cost the item. */
unsigned short mMaxPerSlot; /**< Max item amount per slot in inventory. */
ItemModifiers mModifiers; /**< Item modifiers. */
+ AttackZone *mAttackZone; /**< attack zone when used as a weapon */
};
class Item: public Object
diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp
index 211334ff..159b8356 100644
--- a/src/game-server/itemmanager.cpp
+++ b/src/game-server/itemmanager.cpp
@@ -27,6 +27,7 @@
#include "game-server/itemmanager.hpp"
#include "defines.h"
+#include "game-server/attackzone.hpp"
#include "game-server/item.hpp"
#include "game-server/resourcemanager.hpp"
#include "utils/logger.h"
@@ -122,6 +123,10 @@ void ItemManager::reload()
int maxPerSlot = XML::getProperty(node, "max-per-slot", 0);
int sprite = XML::getProperty(node, "sprite_id", 0);
std::string scriptName = XML::getProperty(node, "script_name", std::string());
+ std::string attackShape = XML::getProperty(node, "attack-shape", "cone");
+ std::string attackTarget = XML::getProperty(node, "attack-target", "multi");
+ int attackRange = XML::getProperty(node, "attack-range", 32);
+ int attackAngle = XML::getProperty(node, "attack-angle", 90);
ItemModifiers modifiers;
if (itemType == ITEM_EQUIPMENT_ONE_HAND_WEAPON ||
@@ -182,6 +187,42 @@ void ItemManager::reload()
item->setSpriteID(sprite ? sprite : id);
++nbItems;
+ if (itemType == ITEM_EQUIPMENT_ONE_HAND_WEAPON ||
+ itemType == ITEM_EQUIPMENT_TWO_HANDS_WEAPON)
+ {
+ AttackZone *zone = new AttackZone;
+
+ if (attackShape == "cone")
+ {
+ zone->shape = ATTZONESHAPE_CONE;
+ }
+ else
+ {
+ LOG_WARN("Item Manager: Unknown attack zone shape \"" << attackShape
+ <<"\" for weapon " << id << " in " << itemReferenceFile << '.');
+ zone->shape = ATTZONESHAPE_CONE;
+ }
+
+ if (attackTarget == "multi")
+ {
+ zone->multiTarget = true;
+ }
+ else if (attackTarget == "single")
+ {
+ zone->multiTarget = false;
+ }
+ else
+ {
+ LOG_WARN("Item Manager: Unknown target mode \"" << attackTarget
+ <<"\" for weapon " << id << " in " << itemReferenceFile << '.');
+ zone->multiTarget = true;
+ }
+ zone->range = attackRange;
+ zone->angle = attackAngle;
+
+ item->setAttackZone(zone);
+ }
+
LOG_DEBUG("Item: ID: " << id << ", itemType: " << itemType
<< ", weight: " << weight << ", value: " << value <<
", scriptName: " << scriptName << ", maxPerSlot: " << maxPerSlot << ".");
diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp
index d782eae0..f9cb1794 100644
--- a/src/game-server/monster.cpp
+++ b/src/game-server/monster.cpp
@@ -125,7 +125,7 @@ void Monster::perform()
damage.element = mCurrentAttack->element;
damage.type = mCurrentAttack->type;
damage.usedSkill = 0;
- performAttack(damage, mCurrentAttack->range, mCurrentAttack->angle);
+ performAttack(damage, &mCurrentAttack->attackZone);
}
if (!mAttackTime)
{
@@ -240,7 +240,7 @@ void Monster::update()
}
if (Collision::diskWithCircleSector(
bestAttackTarget->getPosition(), bestAttackTarget->getSize(),
- getPosition(), (*i)->range, (*i)->angle, attackAngle))
+ getPosition(), (*i)->attackZone.range, (*i)->attackZone.angle, attackAngle))
{
prioritySum += (*i)->priority;
workingAttacks[prioritySum] = (*i);
diff --git a/src/game-server/monster.hpp b/src/game-server/monster.hpp
index 0a614081..c17dfb7e 100644
--- a/src/game-server/monster.hpp
+++ b/src/game-server/monster.hpp
@@ -26,9 +26,11 @@
#include <map>
#include <vector>
+#include "game-server/attackzone.hpp"
#include "game-server/being.hpp"
#include "game-server/eventlistener.hpp"
+class attackZone;
class ItemClass;
class MapComposite;
@@ -55,8 +57,7 @@ struct MonsterAttack
int type;
int preDelay;
int aftDelay;
- int range;
- int angle;
+ AttackZone attackZone;
};
typedef std::vector< MonsterAttack *> MonsterAttacks;
diff --git a/src/game-server/monstermanager.cpp b/src/game-server/monstermanager.cpp
index d7a840e7..a6d22750 100644
--- a/src/game-server/monstermanager.cpp
+++ b/src/game-server/monstermanager.cpp
@@ -26,6 +26,7 @@
#include "game-server/monstermanager.hpp"
#include "defines.h"
+#include "game-server/attackzone.hpp"
#include "game-server/itemmanager.hpp"
#include "game-server/monster.hpp"
#include "game-server/resourcemanager.hpp"
@@ -223,13 +224,17 @@ void MonsterManager::reload()
else if (xmlStrEqual(subnode->name, BAD_CAST "attack"))
{
MonsterAttack *att = new MonsterAttack;
+ AttackZone attackZone;
att->id = XML::getProperty(subnode, "id", 0);
att->priority = XML::getProperty(subnode, "priority", 1);
att->damageFactor = XML::getFloatProperty(subnode, "damage-factor", 1.0f);
att->preDelay = XML::getProperty(subnode, "pre-delay", 1);
att->aftDelay = XML::getProperty(subnode, "aft-delay", 0);
- att->range = XML::getProperty(subnode, "range", 1);
- att->angle = XML::getProperty(subnode, "angle", 1);
+ attackZone.multiTarget = true;
+ attackZone.shape = ATTZONESHAPE_CONE;
+ attackZone.range = XML::getProperty(subnode, "range", 1);
+ attackZone.angle = XML::getProperty(subnode, "angle", 1);
+ att->attackZone = attackZone;
std::string sElement = XML::getProperty(subnode, "element", "neutral");
att->element = elementFromString(sElement);
std::string sType = XML::getProperty(subnode, "type", "physical");