summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/account-server/accounthandler.cpp14
-rw-r--r--src/account-server/characterdata.cpp4
-rw-r--r--src/account-server/characterdata.hpp13
-rw-r--r--src/account-server/dalstorage.cpp82
-rw-r--r--src/defines.h132
-rw-r--r--src/game-server/accountconnection.cpp3
-rw-r--r--src/game-server/being.cpp163
-rw-r--r--src/game-server/being.hpp144
-rw-r--r--src/game-server/character.cpp145
-rw-r--r--src/game-server/character.hpp54
-rw-r--r--src/game-server/gamehandler.cpp5
-rw-r--r--src/game-server/item.cpp31
-rw-r--r--src/game-server/item.hpp56
-rw-r--r--src/game-server/itemmanager.cpp30
-rw-r--r--src/game-server/monster.cpp78
-rw-r--r--src/game-server/monster.hpp19
-rw-r--r--src/game-server/movingobject.hpp5
-rw-r--r--src/game-server/object.hpp3
-rw-r--r--src/game-server/spawnarea.cpp6
-rw-r--r--src/game-server/state.cpp43
-rw-r--r--src/serialize/characterdata.hpp8
21 files changed, 533 insertions, 505 deletions
diff --git a/src/account-server/accounthandler.cpp b/src/account-server/accounthandler.cpp
index 5c553afa..d0346ade 100644
--- a/src/account-server/accounthandler.cpp
+++ b/src/account-server/accounthandler.cpp
@@ -160,9 +160,9 @@ void AccountHandler::sendCharacterData(AccountClient &computer, int slot, Charac
charInfo.writeByte(ch.getLevel());
charInfo.writeLong(ch.getPossessions().money);
- for (int j = 0; j < NB_BASE_ATTRIBUTES; ++j)
+ for (int j = BASE_ATTR_BEGIN; j < BASE_ATTR_END; ++j)
{
- charInfo.writeShort(ch.getBaseAttribute(j));
+ charInfo.writeShort(ch.getAttribute(j));
}
computer.send(charInfo);
@@ -557,13 +557,13 @@ AccountHandler::handleCharacterCreateMessage(AccountClient &computer,
// LATER_ON: Add race, face and maybe special attributes.
// Customization of character's attributes...
- unsigned short attributes[NB_BASE_ATTRIBUTES];
- for (int i = 0; i < NB_BASE_ATTRIBUTES; ++i)
+ int attributes[CHAR_ATTR_NB];
+ for (int i = 0; i < CHAR_ATTR_NB; ++i)
attributes[i] = msg.readShort();
unsigned int totalAttributes = 0;
bool validNonZeroAttributes = true;
- for (int i = 0; i < NB_BASE_ATTRIBUTES; ++i)
+ for (int i = 0; i < CHAR_ATTR_NB; ++i)
{
// For good total attributes check.
totalAttributes += attributes[i];
@@ -587,8 +587,8 @@ AccountHandler::handleCharacterCreateMessage(AccountClient &computer,
else
{
CharacterPtr newCharacter(new CharacterData(name));
- for (int i = 0; i < NB_BASE_ATTRIBUTES; ++i)
- newCharacter->setBaseAttribute(i, attributes[i]);
+ for (int i = CHAR_ATTR_BEGIN; i < CHAR_ATTR_END; ++i)
+ newCharacter->setAttribute(i, attributes[i - CHAR_ATTR_BEGIN]);
newCharacter->setLevel(1);
newCharacter->setGender(gender);
newCharacter->setHairStyle(hairStyle);
diff --git a/src/account-server/characterdata.cpp b/src/account-server/characterdata.cpp
index 0bf79ff0..d82100d5 100644
--- a/src/account-server/characterdata.cpp
+++ b/src/account-server/characterdata.cpp
@@ -26,9 +26,9 @@ CharacterData::CharacterData(std::string const &name, int id):
mDatabaseID(id), mAccountID(-1), mName(name), mGender(0), mHairStyle(0),
mHairColor(0), mLevel(0), mMapId(0), mPos(0,0)
{
- for (int i = 0; i < NB_BASE_ATTRIBUTES; ++i)
+ for (int i = 0; i < CHAR_ATTR_NB; ++i)
{
- mBaseAttributes[i] = 0;
+ mAttributes[i] = 0;
}
}
diff --git a/src/account-server/characterdata.hpp b/src/account-server/characterdata.hpp
index 1655082d..499b5bef 100644
--- a/src/account-server/characterdata.hpp
+++ b/src/account-server/characterdata.hpp
@@ -100,14 +100,12 @@ class CharacterData
setLevel(int level) { mLevel = level; }
/** Gets the value of a base attribute of the character. */
- unsigned short
- getBaseAttribute(int attributeNumber) const
- { return mBaseAttributes[attributeNumber]; }
+ int getAttribute(int n) const
+ { return mAttributes[n - CHAR_ATTR_BEGIN]; }
/** Sets the value of a base attribute of the character. */
- void
- setBaseAttribute(int attributeNumber, int value)
- { mBaseAttributes[attributeNumber] = value; }
+ void setAttribute(int n, int value)
+ { mAttributes[n - CHAR_ATTR_BEGIN] = value; }
/** Gets the Id of the map that the character is on. */
int
@@ -157,8 +155,7 @@ class CharacterData
unsigned char mHairStyle; //!< Hair Style of the being.
unsigned char mHairColor; //!< Hair Color of the being.
unsigned char mLevel; //!< Level of the being.
- unsigned short mBaseAttributes[NB_BASE_ATTRIBUTES]; //!< The attributes of the
- //!< character.
+ unsigned short mAttributes[CHAR_ATTR_NB]; //!< Attributes.
unsigned short mMapId; //!< Map the being is on.
Point mPos; //!< Position the being is at.
Possessions mPossessions; //!< All the possesions of the character.
diff --git a/src/account-server/dalstorage.cpp b/src/account-server/dalstorage.cpp
index 17bfa386..e9887fad 100644
--- a/src/account-server/dalstorage.cpp
+++ b/src/account-server/dalstorage.cpp
@@ -357,9 +357,10 @@ CharacterPtr DALStorage::getCharacterBySQL(std::string const &query)
character->getPossessions().money = toUint(charInfo(0, 7));
Point pos(toUshort(charInfo(0, 8)), toUshort(charInfo(0, 9)));
character->setPosition(pos);
- for (int i = 0; i < NB_BASE_ATTRIBUTES; ++i)
+ for (int i = 0; i < CHAR_ATTR_NB; ++i)
{
- character->setBaseAttribute(i, toUshort(charInfo(0, 11 + i)));
+ character->setAttribute(CHAR_ATTR_BEGIN + i,
+ toUshort(charInfo(0, 11 + i)));
}
int mapId = toUint(charInfo(0, 10));
@@ -573,44 +574,27 @@ DALStorage::updateCharacter(CharacterPtr character)
sqlUpdateCharacterInfo
<< "update " << CHARACTERS_TBL_NAME << " "
<< "set "
- << "gender = '" << character->getGender()
- << "', "
- << "hair_style = '" << character->getHairStyle()
- << "', "
- << "hair_color = '" << character->getHairColor()
- << "', "
- << "level = '" << character->getLevel()
- << "', "
- << "money = '" << character->getPossessions().money
- << "', "
- << "x = '" << character->getPosition().x
- << "', "
- << "y = '" << character->getPosition().y
- << "', "
- << "map_id = '" << character->getMapId()
- << "', "
- << "str = '" << character->getBaseAttribute(BASE_ATTR_STRENGTH)
- << "', "
- << "agi = '" << character->getBaseAttribute(BASE_ATTR_AGILITY)
- << "', "
- << "dex = '" << character->getBaseAttribute(BASE_ATTR_DEXTERITY)
- << "', "
- << "vit = '" << character->getBaseAttribute(BASE_ATTR_VITALITY)
- << "', "
+ << "gender = '" << character->getGender() << "', "
+ << "hair_style = '" << character->getHairStyle() << "', "
+ << "hair_color = '" << character->getHairColor() << "', "
+ << "level = '" << character->getLevel() << "', "
+ << "money = '" << character->getPossessions().money << "', "
+ << "x = '" << character->getPosition().x << "', "
+ << "y = '" << character->getPosition().y << "', "
+ << "map_id = '" << character->getMapId() << "', "
+ << "str = '" << character->getAttribute(CHAR_ATTR_STRENGTH) << "', "
+ << "agi = '" << character->getAttribute(CHAR_ATTR_AGILITY) << "', "
+ << "dex = '" << character->getAttribute(CHAR_ATTR_DEXTERITY) << "', "
+ << "vit = '" << character->getAttribute(CHAR_ATTR_VITALITY) << "', "
#if defined(MYSQL_SUPPORT) || defined(POSTGRESQL_SUPPORT)
<< "`int` = '"
#else
<< "int = '"
#endif
- << character->getBaseAttribute(BASE_ATTR_INTELLIGENCE)
- << "', "
-
- << "will = '" << character->getBaseAttribute(BASE_ATTR_WILLPOWER)
- << "', "
- << "charisma = '" << character->getBaseAttribute(BASE_ATTR_CHARISMA)
- << "' "
- << "where id = '" << character->getDatabaseID()
- << "';";
+ << character->getAttribute(CHAR_ATTR_INTELLIGENCE) << "', "
+ << "will = '" << character->getAttribute(CHAR_ATTR_WILLPOWER) << "', "
+ << "charisma = '" << character->getAttribute(CHAR_ATTR_CHARISMA) << "' "
+ << "where id = '" << character->getDatabaseID() << "';";
mDb->execSql(sqlUpdateCharacterInfo.str());
}
@@ -924,13 +908,13 @@ void DALStorage::flush(AccountPtr const &account)
<< (*it)->getPosition().x << ", "
<< (*it)->getPosition().y << ", "
<< (*it)->getMapId() << ", "
- << (*it)->getBaseAttribute(BASE_ATTR_STRENGTH) << ", "
- << (*it)->getBaseAttribute(BASE_ATTR_AGILITY) << ", "
- << (*it)->getBaseAttribute(BASE_ATTR_DEXTERITY) << ", "
- << (*it)->getBaseAttribute(BASE_ATTR_VITALITY) << ", "
- << (*it)->getBaseAttribute(BASE_ATTR_INTELLIGENCE) << ", "
- << (*it)->getBaseAttribute(BASE_ATTR_WILLPOWER) << ", "
- << (*it)->getBaseAttribute(BASE_ATTR_CHARISMA) << ");";
+ << (*it)->getAttribute(CHAR_ATTR_STRENGTH) << ", "
+ << (*it)->getAttribute(CHAR_ATTR_AGILITY) << ", "
+ << (*it)->getAttribute(CHAR_ATTR_DEXTERITY) << ", "
+ << (*it)->getAttribute(CHAR_ATTR_VITALITY) << ", "
+ << (*it)->getAttribute(CHAR_ATTR_INTELLIGENCE) << ", "
+ << (*it)->getAttribute(CHAR_ATTR_WILLPOWER) << ", "
+ << (*it)->getAttribute(CHAR_ATTR_CHARISMA) << ");";
mDb->execSql(sqlInsertCharactersTable.str());
} else {
@@ -946,18 +930,18 @@ void DALStorage::flush(AccountPtr const &account)
<< " x = " << (*it)->getPosition().x << ", "
<< " y = " << (*it)->getPosition().y << ", "
<< " map_id = " << (*it)->getMapId() << ", "
- << " str = " << (*it)->getBaseAttribute(BASE_ATTR_STRENGTH) << ", "
- << " agi = " << (*it)->getBaseAttribute(BASE_ATTR_AGILITY) << ", "
- << " dex = " << (*it)->getBaseAttribute(BASE_ATTR_DEXTERITY) << ", "
- << " vit = " << (*it)->getBaseAttribute(BASE_ATTR_VITALITY) << ", "
+ << " str = " << (*it)->getAttribute(CHAR_ATTR_STRENGTH) << ", "
+ << " agi = " << (*it)->getAttribute(CHAR_ATTR_AGILITY) << ", "
+ << " dex = " << (*it)->getAttribute(CHAR_ATTR_DEXTERITY) << ", "
+ << " vit = " << (*it)->getAttribute(CHAR_ATTR_VITALITY) << ", "
#if defined(MYSQL_SUPPORT) || defined(POSTGRESQL_SUPPORT)
<< " `int` = "
#else
<< " int = "
#endif
- << (*it)->getBaseAttribute(BASE_ATTR_INTELLIGENCE) << ", "
- << " will = " << (*it)->getBaseAttribute(BASE_ATTR_WILLPOWER) << ", "
- << " charisma = " << (*it)->getBaseAttribute(BASE_ATTR_CHARISMA)
+ << (*it)->getAttribute(CHAR_ATTR_INTELLIGENCE) << ", "
+ << " will = " << (*it)->getAttribute(CHAR_ATTR_WILLPOWER) << ", "
+ << " charisma = " << (*it)->getAttribute(CHAR_ATTR_CHARISMA)
<< " where id = " << (*it)->getDatabaseID() << ";";
mDb->execSql(sqlUpdateCharactersTable.str());
diff --git a/src/defines.h b/src/defines.h
index 5d82c86f..f3a91a34 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -151,7 +151,7 @@ enum {
PGMSG_MOVE_ITEM = 0x0114, // B slot1, B slot2, B amount
GPMSG_INVENTORY = 0x0120, // { B slot, W item id [, B amount] }*
GPMSG_INVENTORY_FULL = 0x0121, // { B slot, W item id [, B amount] }*
- GPMSG_PLAYER_ATTRIBUTE_UPDATE = 0x0130, // { W attribute, W value }*
+ GPMSG_PLAYER_ATTRIBUTE_CHANGE = 0x0130, // { B attribute, W base value, W modified value }*
GPMSG_BEING_ENTER = 0x0200, // B type, W being id, B action, W*2 position
// character: S name, B hair style, B hair color, B gender, B item bitmask, { W item id }*
// monster: W type id
@@ -324,10 +324,6 @@ enum {
};
/**
- * Enumerations for the handling of attributes and their modifiers.
- */
-
-/**
* Possible states of beings.
* States can be multiple for the same being.
* To be used as bitmask values.
@@ -346,76 +342,92 @@ enum BeingState
};
/**
- * Stats every being needs
+ * Element attribute for beings, actors, and items.
+ * Subject to change until Pauan and Dabe are finished with the element system.
+ * Please keep element modifier of BeingAttribute in sync.
*/
-enum BeingStats
+enum
{
- BASE_ATTR_STRENGTH = 0, // Basic attributes
- BASE_ATTR_AGILITY,
- BASE_ATTR_DEXTERITY,
- BASE_ATTR_VITALITY,
- BASE_ATTR_INTELLIGENCE,
- BASE_ATTR_WILLPOWER,
- BASE_ATTR_CHARISMA,
- NB_BASE_ATTRIBUTES,
-
- ATTR_EFF_STRENGTH = NB_BASE_ATTRIBUTES, // modified basic attributes
- ATTR_EFF_AGILITY,
- ATTR_EFF_DEXTERITY,
- ATTR_EFF_VITALITY,
- ATTR_EFF_INTELLIGENCE,
- ATTR_EFF_WILLPOWER,
- ATTR_EFF_CHARISMA,
- NB_EFFECTIVE_ATTRIBUTES,
-
- DERIVED_ATTR_HP_MAXIMUM = NB_EFFECTIVE_ATTRIBUTES, // Computed stats
- DERIVED_ATTR_PHYSICAL_ATTACK_MINIMUM,
- DERIVED_ATTR_PHYSICAL_ATTACK_FLUCTUATION,
- DERIVED_ATTR_PHYSICAL_DEFENCE,
- // add new computed statistics when they are needed
- NB_ATTRIBUTES_BEING
+ ELEMENT_NEUTRAL = 0,
+ ELEMENT_FIRE,
+ ELEMENT_WATER,
+ ELEMENT_EARTH,
+ ELEMENT_AIR,
+ ELEMENT_SACRED,
+ ELEMENT_DEATH
};
+/**
+ * Attributes used during combat. Available to all the beings.
+ */
+enum
+{
+ BASE_ATTR_BEGIN = 0,
+ BASE_ATTR_PHY_ATK = BASE_ATTR_BEGIN,
+ /**< Physical attack power. */
+ BASE_ATTR_MAG_ATK, /**< Magical attack power. */
+ BASE_ATTR_PHY_RES, /**< Resistance to physical damage. */
+ BASE_ATTR_MAG_RES, /**< Resistance to magical damage. */
+ BASE_ATTR_EVADE, /**< Ability to avoid hits. */
+ BASE_ATTR_HP, /**< Remaining Hit Points. */
+ BASE_ATTR_END,
+ BASE_ATTR_NB = BASE_ATTR_END - BASE_ATTR_BEGIN,
+
+ BASE_ELEM_BEGIN = BASE_ATTR_END,
+ BASE_ELEM_NEUTRAL = BASE_ELEM_BEGIN,
+ BASE_ELEM_FIRE,
+ BASE_ELEM_WATER,
+ BASE_ELEM_EARTH,
+ BASE_ELEM_AIR,
+ BASE_ELEM_SACRED,
+ BASE_ELEM_DEATH,
+ BASE_ELEM_END,
+ BASE_ELEM_NB = BASE_ELEM_END - BASE_ELEM_BEGIN,
+
+ NB_BEING_ATTRIBUTES = BASE_ELEM_END
+};
/**
- * Player character specific stats
+ * Attributes of characters. Used to derive being attributes.
+ * Please keep weapon skills in sync with item weapon types.
*/
-enum CharacterStats
+enum
{
- CHAR_SKILL_WEAPON_UNARMED = NB_ATTRIBUTES_BEING,
+ CHAR_ATTR_BEGIN = BASE_ATTR_END,
+ CHAR_ATTR_STRENGTH = CHAR_ATTR_BEGIN,
+ CHAR_ATTR_AGILITY,
+ CHAR_ATTR_DEXTERITY,
+ CHAR_ATTR_VITALITY,
+ CHAR_ATTR_INTELLIGENCE,
+ CHAR_ATTR_WILLPOWER,
+ CHAR_ATTR_CHARISMA,
+ CHAR_ATTR_END,
+ CHAR_ATTR_NB = CHAR_ATTR_END - CHAR_ATTR_BEGIN,
+
+ CHAR_SKILL_WEAPON_BEGIN = CHAR_ATTR_END,
+ CHAR_SKILL_WEAPON_NONE = CHAR_SKILL_WEAPON_BEGIN,
+ CHAR_SKILL_WEAPON_KNIFE,
CHAR_SKILL_WEAPON_SWORD,
- CHAR_SKILL_WEAPON_AXE,
- CHAR_SKILL_WEAPON_POLEARM,
+ CHAR_SKILL_WEAPON_SPEAR,
CHAR_SKILL_WEAPON_JAVELIN,
- CHAR_SKILL_WEAPON_WHIP,
- CHAR_SKILL_WEAPON_DAGGER,
+ CHAR_SKILL_WEAPON_ROD,
CHAR_SKILL_WEAPON_STAFF,
+ CHAR_SKILL_WEAPON_WHIP,
+ CHAR_SKILL_WEAPON_PROJECTILE,
+ CHAR_SKILL_WEAPON_BOOMERANG,
CHAR_SKILL_WEAPON_BOW,
+ CHAR_SKILL_WEAPON_SICKLE,
CHAR_SKILL_WEAPON_CROSSBOW,
- CHAR_SKILL_WEAPON_THROWN,
- NB_CHAR_WEAPONSKILLS,
-
- CHAR_SKILL_MAGIC_IAMJUSTAPLACEHOLDER = NB_CHAR_WEAPONSKILLS,
- NB_CHAR_MAGICSKILLS,
-
- CHAR_SKILL_CRAFT_IAMJUSTAPLACEHOLDER = NB_CHAR_MAGICSKILLS,
- NB_CHAR_CRAFTSKILLS,
-
- CHAR_SKILL_IAMJUSTAPLACEHOLDER = NB_CHAR_CRAFTSKILLS,
- NB_CHAR_OTHERSKILLS,
-
- NB_ATTRIBUTES_CHAR = NB_CHAR_OTHERSKILLS
-};
+ CHAR_SKILL_WEAPON_STICK,
+ CHAR_SKILL_WEAPON_HAMMER,
+ CHAR_SKILL_WEAPON_AXE,
+ CHAR_SKILL_WEAPON_HAND_PROJECTILE,
+ CHAR_SKILL_WEAPON_END,
+ CHAR_SKILL_WEAPON_NB = CHAR_SKILL_WEAPON_END - CHAR_SKILL_WEAPON_BEGIN,
+ // Magic skills should follow.
-/**
- * Monster-specific stats
- */
-enum MonsterStats
-{
- MONSTER_SKILL_WEAPON = NB_ATTRIBUTES_BEING,
- NB_ATTRIBUTES_CONTROLLED
+ NB_CHARACTER_ATTRIBUTES = CHAR_SKILL_WEAPON_END
};
-
#endif // _TMWSERV_DEFINES_H_
diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp
index 5b8b6f3d..8a03bb24 100644
--- a/src/game-server/accountconnection.cpp
+++ b/src/game-server/accountconnection.cpp
@@ -74,7 +74,8 @@ void AccountConnection::processMessage(MessageIn &msg)
std::string token = msg.readString(MAGIC_TOKEN_LENGTH);
Character *ptr = new Character(msg);
ptr->setSpeed(250); // TODO
- ptr->fillHitpoints();// TODO: the current hit points should be saved in the database. Otherwise players could heal their characters by logging in and out again.
+ // FIXME: for testing purpose.
+ ptr->setAttribute(CHAR_SKILL_WEAPON_NONE, 10);
gameHandler->addPendingCharacter(token, ptr);
} break;
diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp
index 45b6b8aa..0fd03af1 100644
--- a/src/game-server/being.cpp
+++ b/src/game-server/being.cpp
@@ -20,6 +20,8 @@
* $Id$
*/
+#include <cassert>
+
#include "game-server/being.hpp"
#include "defines.h"
@@ -32,7 +34,8 @@ Being::Being(int type, int id):
MovingObject(type, id),
mAction(STAND)
{
- mAttributes.resize(NB_ATTRIBUTES_BEING);
+ Attribute attr = { 0, 0 };
+ mAttributes.resize(NB_BEING_ATTRIBUTES, attr);
}
Being::~Being()
@@ -47,47 +50,84 @@ Being::~Being()
}
-int Being::damage(Damage damage)
+int Being::damage(Object *, Damage const &damage)
{
if (mAction == DEAD) return 0;
- // TODO: Implement dodge chance
+ int HPloss = damage.base;
+ if (damage.delta)
+ {
+ HPloss += rand() / (RAND_MAX / (damage.delta + 1));
+ }
- int HPloss = damage.value;
+ /* Damage can either be avoided, or applied, or critical (applied twice).
+ This is decided by comparing CTH and Evade. If they are equal, the
+ probabilities are 10%, 80%, 10%. Otherwise, the bigger the CTH, the
+ higher the chance to do a critical, up to 50%; and the bigger the Evade,
+ the higher the chance to do evade the hit, up to 50% again. */
- // TODO: Implement elemental modifier
+ int avoidChance = 10, criticalChance = 10;
+ int diff = damage.cth - getModifiedAttribute(BASE_ATTR_EVADE);
+ if (diff > 0)
+ {
+ // CTH - Evade >= 200 => 50% critical
+ criticalChance += diff * diff / 1000;
+ if (criticalChance > 50) criticalChance = 50;
+ }
+ else if (diff < 0)
+ {
+ // Evade - CTH >= 200 => 50% avoid
+ avoidChance += diff * diff / 10000;
+ if (avoidChance > 50) avoidChance = 50;
+ }
+ int chance = rand() / (RAND_MAX / 100);
+ LOG_INFO("Chance: " << chance << " (" << avoidChance << ", " << 100 - criticalChance << "); Damage: " << HPloss);
+ if (chance <= avoidChance)
+ {
+ mHitsTaken.push_back(0);
+ return 0;
+ }
+ if (chance >= 100 - criticalChance) HPloss *= 2;
+ /* Elemental modifier at 0 means normal damage. At -100, it means immune.
+ And at 100, it means vulnerable (double damage). */
+ int mod1 = 100 + getModifiedAttribute(BASE_ELEM_BEGIN + damage.element);
+
+ /* Resistance to damage at 0 gives normal damage. At 100, it gives halved
+ damage. At 200, it divides damage by 3. And so on. */
+ int mod2 = 0;
switch (damage.type)
{
- case DAMAGETYPE_PHYSICAL:
- HPloss -= getAttribute(DERIVED_ATTR_PHYSICAL_DEFENCE) / damage.piercing;
- HPloss -= getAttribute(ATTR_EFF_VITALITY);
- break;
- case DAMAGETYPE_MAGICAL:
- HPloss /= getAttribute(ATTR_EFF_WILLPOWER) + 1;
+ case DAMAGE_PHYSICAL:
+ mod2 = getModifiedAttribute(BASE_ATTR_PHY_RES);
break;
- case DAMAGETYPE_HAZARD:
- HPloss /= getAttribute(ATTR_EFF_VITALITY) + 1;
+ case DAMAGE_MAGICAL:
+ mod2 = getModifiedAttribute(BASE_ATTR_MAG_RES);
break;
- case DAMAGETYPE_OTHER:
- // nothing to do here
+ default:
break;
}
+ HPloss = HPloss * mod1 / (100 + mod2);
- if (HPloss < 0) HPloss = 0;
- if (HPloss > mHitpoints) HPloss = mHitpoints;
-
- mHitpoints -= HPloss;
mHitsTaken.push_back(HPloss);
LOG_DEBUG("Being " << getPublicID() << " got hit.");
- if (mHitpoints == 0) die();
+ Attribute &HP = mAttributes[BASE_ATTR_HP];
+ if (HPloss >= HP.base + HP.mod) HPloss = HP.base + HP.mod;
+ if (HPloss > 0)
+ {
+ HP.mod -= HPloss;
+ modifiedAttribute(BASE_ATTR_HP);
+ if (HP.base + HP.mod == 0) die();
+ }
return HPloss;
}
void Being::die()
{
+ if (mAction == DEAD) return;
+
LOG_DEBUG("Being " << getPublicID() << " died.");
setAction(DEAD);
// dead beings stay where they are
@@ -118,7 +158,7 @@ void Being::move()
}
}
-void Being::performAttack(MapComposite *map)
+void Being::performAttack(Damage const &damage)
{
int SHORT_RANGE = 60;
int SMALL_ANGLE = 35;
@@ -145,7 +185,8 @@ void Being::performAttack(MapComposite *map)
break;
}
- for (MovingObjectIterator i(map->getAroundObjectIterator(this, SHORT_RANGE)); i; ++i)
+ for (MovingObjectIterator
+ i(getMap()->getAroundObjectIterator(this, SHORT_RANGE)); i; ++i)
{
MovingObject *o = *i;
if (o == this) continue;
@@ -161,7 +202,7 @@ void Being::performAttack(MapComposite *map)
ppos, SHORT_RANGE, SMALL_ANGLE, attackAngle)
)
{
- static_cast< Being * >(o)->damage(getPhysicalAttackDamage());
+ static_cast< Being * >(o)->damage(this, damage);
}
}
}
@@ -176,54 +217,48 @@ void Being::setAction(Action action)
}
}
-void Being::calculateDerivedAttributes()
+void Being::addModifier(AttributeModifier const &mod)
+{
+ mModifiers.push_back(mod);
+ mAttributes[mod.attr].mod += mod.value;
+ modifiedAttribute(mod.attr);
+}
+
+void Being::removeEquipmentModifier(int attr, int value)
{
- // effective values for basic attributes
- for (int i = NB_BASE_ATTRIBUTES; i < NB_EFFECTIVE_ATTRIBUTES; i++)
+ bool found = false;
+ for (AttributeModifiers::iterator i = mModifiers.begin(),
+ i_end = mModifiers.end(); i != i_end; ++i)
{
- mAttributes.at(i)
- = getAttribute(i - NB_BASE_ATTRIBUTES); // TODO: add modifiers
+ found = i->level == 0 && i->attr == attr && i->value == value;
+ if (found)
+ {
+ // Remove one equivalent modifier.
+ mModifiers.erase(i);
+ break;
+ }
}
-
- // combat-related derived stats
- mAttributes.at(DERIVED_ATTR_HP_MAXIMUM)
- = getAttribute(ATTR_EFF_VITALITY); // TODO: find a better formula
-
- mAttributes.at(DERIVED_ATTR_PHYSICAL_ATTACK_MINIMUM)
- = getAttribute(ATTR_EFF_STRENGTH);
-
- mAttributes.at(DERIVED_ATTR_PHYSICAL_ATTACK_FLUCTUATION)
- = getAttribute(getWeaponStats().skill);
-
- mAttributes.at(DERIVED_ATTR_PHYSICAL_DEFENCE)
- = 0 /* + sum of equipment pieces */;
+ assert(found);
+ mAttributes[attr].mod -= value;
+ modifiedAttribute(attr);
}
-Damage Being::getPhysicalAttackDamage()
+void Being::dispellModifiers(int level)
{
- Damage damage;
- WeaponStats weaponStats = getWeaponStats();
-
- damage.type = DAMAGETYPE_PHYSICAL;
- damage.value = getAttribute(DERIVED_ATTR_PHYSICAL_ATTACK_MINIMUM)
- + (rand()%getAttribute(DERIVED_ATTR_PHYSICAL_ATTACK_FLUCTUATION));
- damage.piercing = weaponStats.piercing;
- damage.element = weaponStats.element;
- damage.source = this;
-
- return damage;
+ for (AttributeModifiers::iterator i = mModifiers.begin();
+ i != mModifiers.end(); ++i)
+ {
+ if (i->level && i->level <= level)
+ {
+ mAttributes[i->attr].mod -= i->value;
+ modifiedAttribute(i->attr);
+ i = mModifiers.erase(i);
+ }
+ }
}
-WeaponStats Being::getWeaponStats()
+int Being::getModifiedAttribute(int attr) const
{
- /* this function should never be called. it is just here to pacify the
- * compiler.
- */
- WeaponStats weaponStats;
-
- weaponStats.piercing = 1;
- weaponStats.element = ELEMENT_NEUTRAL;
- weaponStats.skill = 0;
-
- return weaponStats;
+ int res = mAttributes[attr].base + mAttributes[attr].mod;
+ return res <= 0 ? 0 : res;
}
diff --git a/src/game-server/being.hpp b/src/game-server/being.hpp
index 3872e661..1cceb303 100644
--- a/src/game-server/being.hpp
+++ b/src/game-server/being.hpp
@@ -23,7 +23,6 @@
#ifndef _TMWSERV_BEING_H_
#define _TMWSERV_BEING_H_
-#include <list>
#include <string>
#include <vector>
@@ -35,29 +34,6 @@ class DeathListener;
class MapComposite;
/**
- * Derived attributes of a Being.
- */
-enum
-{
-
-};
-
-/**
- * Element attribute for beings, actors and items.
- * Subject to change until pauan and dabe are finished with the element system.
- */
-enum Element
-{
- ELEMENT_NEUTRAL = 0,
- ELEMENT_FIRE,
- ELEMENT_WATER,
- ELEMENT_EARTH,
- ELEMENT_AIR,
- ELEMENT_SACRED,
- ELEMENT_DEATH
-};
-
-/**
* Beings and actors directions
*/
enum Direction
@@ -71,12 +47,11 @@ enum Direction
/**
* Methods of damage calculation
*/
-enum Damagetype
+enum
{
- DAMAGETYPE_PHYSICAL,
- DAMAGETYPE_MAGICAL,
- DAMAGETYPE_HAZARD,
- DAMAGETYPE_OTHER
+ DAMAGE_PHYSICAL,
+ DAMAGE_MAGICAL,
+ DAMAGE_OTHER
};
/**
@@ -85,28 +60,44 @@ enum Damagetype
*/
struct Damage
{
- int value;
- int piercing;
- Element element;
- Damagetype type;
- Being *source;
+ unsigned short base; /**< Base amount of damage. */
+ unsigned short delta; /**< Additional damage when lucky. */
+ unsigned short cth; /**< Chance to hit. Opposes the evade attribute. */
+ unsigned char element; /**< Elemental damage. */
+ unsigned char type; /**< Damage type: Physical or magical? */
};
/**
- * Structure that holds weapon stats that are relevant for damage calculation
+ * Holds the base value of an attribute and the sum of all its modifiers.
+ * While base + mod may be negative, the modified attribute is not.
*/
-struct WeaponStats
+struct Attribute
+{
+ unsigned short base;
+ short mod;
+};
+
+struct AttributeModifier
{
- int piercing;
- Element element;
- int skill;
+ /**< Number of ticks (0 means permanent, e.g. equipment). */
+ unsigned short duration;
+ short value; /**< Positive or negative amount. */
+ unsigned char attr; /**< Attribute to modify. */
+ /**
+ * Strength of the modification.
+ * - Zero means permanent, e.g. equipment.
+ * - Non-zero means spell. Can only be removed by a wizard with a
+ * dispell level higher than this value.
+ */
+ unsigned char level;
};
+typedef std::vector< AttributeModifier > AttributeModifiers;
+
/**
* Type definition for a list of hits
*/
-typedef std::list<unsigned int> Hits;
-
+typedef std::vector<unsigned int> Hits;
/**
* Generic Being (living object).
@@ -138,23 +129,11 @@ class Being : public MovingObject
~Being();
/**
- * Creates a damage structure for a normal melee attack based on the
- * current being stats and equipment.
- */
- Damage getPhysicalAttackDamage();
-
- /**
- * Sets the hit points to maximum
- */
- void fillHitpoints()
- { mHitpoints = getAttribute(DERIVED_ATTR_HP_MAXIMUM); }
-
- /**
* 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(Damage damage);
+ virtual int damage(Object *source, Damage const &damage);
/**
* Kills the being
@@ -176,38 +155,55 @@ class Being : public MovingObject
/**
* Performs an attack.
*/
- void performAttack(MapComposite *);
+ void performAttack(Damage const &);
/**
* Sets the current action.
*/
- virtual void setAction(Action action);
+ void setAction(Action action);
/**
* Sets the current action.
*/
- virtual Action getAction() const
+ Action getAction() const
{ return mAction; }
/**
* Moves the being toward its destination.
*/
- virtual void move();
+ void move();
/**
- * Sets an attribute (doesn't work on derived attributes)
+ * Sets an attribute.
*/
- void setAttribute(int attributeNumber, unsigned short value)
- {
- mAttributes.at(attributeNumber) = value;
- calculateDerivedAttributes();
- }
+ void setAttribute(int n, int value)
+ { mAttributes[n].base = value; }
/**
* Gets an attribute.
*/
- unsigned short getAttribute(int attributeNumber) const
- { return mAttributes.at(attributeNumber); }
+ int getAttribute(int n) const
+ { return mAttributes[n].base; }
+
+ /**
+ * Gets an attribute after applying modifiers.
+ */
+ int getModifiedAttribute(int) const;
+
+ /**
+ * Adds a modifier to one attribute.
+ */
+ void addModifier(AttributeModifier const &);
+
+ /**
+ * Removes a modifier due to an equipment.
+ */
+ void removeEquipmentModifier(int attr, int value);
+
+ /**
+ * Removes all the modifiers with a level low enough.
+ */
+ void dispellModifiers(int level);
/**
* Adds a death listener.
@@ -225,21 +221,14 @@ class Being : public MovingObject
mDeathListeners.remove(listener);
}
- protected:
/**
- * Calculates all derived attributes of a beings
+ * Called when an attribute modifier is changed.
*/
- void calculateDerivedAttributes();
+ virtual void modifiedAttribute(int) {}
- /**
- * Gets the stats of the currently equipped weapon that are relevant
- * for damage calculation
- */
- virtual WeaponStats getWeaponStats();
-
- int mHitpoints; /**< Hitpoints of the being */
+ protected:
Action mAction;
- std::vector<unsigned short> mAttributes;
+ std::vector< Attribute > mAttributes;
std::list<DeathListener*> mDeathListeners;
private:
@@ -247,6 +236,7 @@ class Being : public MovingObject
Being &operator=(Being const &rhs);
Hits mHitsTaken; /**< List of punches taken since last update */
+ AttributeModifiers mModifiers; /**< Currently modified attributes. */
};
#endif // _TMWSERV_BEING_H_
diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp
index 913e05ef..29eda735 100644
--- a/src/game-server/character.cpp
+++ b/src/game-server/character.cpp
@@ -20,12 +20,17 @@
* $Id$
*/
+#include <algorithm>
#include <cassert>
#include "game-server/character.hpp"
#include "defines.h"
#include "game-server/buysell.hpp"
+#include "game-server/inventory.hpp"
+#include "game-server/item.hpp"
+#include "game-server/itemmanager.hpp"
+#include "game-server/gamehandler.hpp"
#include "game-server/mapcomposite.hpp"
#include "game-server/mapmanager.hpp"
#include "game-server/trade.hpp"
@@ -33,86 +38,52 @@
#include "net/messageout.hpp"
#include "serialize/characterdata.hpp"
-Character::Character(MessageIn & msg):
+Character::Character(MessageIn &msg):
Being(OBJECT_CHARACTER, 65535),
mClient(NULL), mTransactionHandler(NULL), mDatabaseID(-1),
mGender(0), mHairStyle(0), mHairColor(0), mLevel(0),
- mTransaction(TRANS_NONE), mAttributesChanged(true)
+ mTransaction(TRANS_NONE)
{
- // prepare attributes vector
- mAttributes.resize(NB_ATTRIBUTES_CHAR, 1);
- mOldAttributes.resize(NB_ATTRIBUTES_CHAR, 0);
- // get character data
+ Attribute attr = { 0, 0 };
+ mAttributes.resize(NB_CHARACTER_ATTRIBUTES, attr);
+ // Get character data.
mDatabaseID = msg.readLong();
mName = msg.readString();
deserializeCharacterData(*this, msg);
- // give the player 10 weapon skill for testing purpose
- setAttribute(CHAR_SKILL_WEAPON_UNARMED, 10);
-
setSize(16);
}
-/**
- * Update the internal status.
- */
-void
-Character::update()
-{
- // attacking
- if (mAction == ATTACK)
- {
- // plausibility check of attack command
- if (mActionTime <= 0)
- {
- // request perform attack
- mActionTime = 1000;
- mAction = STAND;
- raiseUpdateFlags(UPDATEFLAG_ATTACK);
- }
- }
-}
-void Character::calculateDerivedAttributes()
+void Character::perform()
{
- Being::calculateDerivedAttributes();
- /*
- * Do any player character specific attribute calculation here
- */
+ if (mAction != ATTACK || mActionTime > 0) return;
- mAttributesChanged = true;
-}
+ mActionTime = 1000;
+ mAction = STAND;
+ raiseUpdateFlags(UPDATEFLAG_ATTACK);
-WeaponStats
-Character::getWeaponStats()
-{
- WeaponStats weaponStats;
+ // TODO: Check slot 2 too.
+ int itemId = mPossessions.equipment[EQUIP_FIGHT1_SLOT];
+ ItemClass *ic = ItemManager::getItem(itemId);
+ int type = ic ? ic->getModifiers().getValue(MOD_WEAPON_TYPE) : WPNTYPE_NONE;
- /*
- * TODO: get all this stuff from the currently equipped weapon
- */
- weaponStats.piercing = 1;
- weaponStats.element = ELEMENT_NEUTRAL;
- weaponStats.skill = CHAR_SKILL_WEAPON_UNARMED;
-
- return weaponStats;
-}
-
-void
-Character::writeAttributeUpdateMessage(MessageOut &msg)
-{
- if (!mAttributesChanged) return;
-
- for (int i = 0; i<NB_ATTRIBUTES_CHAR; i++)
+ Damage damage;
+ damage.base = getModifiedAttribute(BASE_ATTR_PHY_ATK) / 10;
+ damage.type = DAMAGE_PHYSICAL;
+ if (type)
{
- unsigned short attribute = getAttribute(i);
- if (attribute != mOldAttributes[i])
- {
- msg.writeShort(i);
- msg.writeShort(attribute);
- mOldAttributes[i] = attribute;
- }
+ ItemModifiers const &mods = ic->getModifiers();
+ damage.delta = mods.getValue(MOD_WEAPON_DAMAGE);
+ damage.cth = getModifiedAttribute(CHAR_SKILL_WEAPON_BEGIN + type);
+ damage.element = mods.getValue(MOD_ELEMENT_TYPE);
}
-
- mAttributesChanged = false;
+ else
+ {
+ // No-weapon fighting.
+ damage.delta = 1;
+ damage.cth = getModifiedAttribute(CHAR_SKILL_WEAPON_NONE);
+ damage.element = ELEMENT_NEUTRAL;
+ }
+ performAttack(damage);
}
int Character::getMapId() const
@@ -184,3 +155,49 @@ void Character::setBuySell(BuySell *t)
}
}
+void Character::sendStatus()
+{
+ if (mModifiedAttributes.empty()) return;
+
+ MessageOut msg(GPMSG_PLAYER_ATTRIBUTE_CHANGE);
+ for (std::vector< unsigned char >::const_iterator i = mModifiedAttributes.begin(),
+ i_end = mModifiedAttributes.end(); i != i_end; ++i)
+ {
+ int attr = *i;
+ msg.writeByte(attr);
+ msg.writeShort(getAttribute(attr));
+ msg.writeShort(getModifiedAttribute(attr));
+ }
+ gameHandler->sendTo(this, msg);
+
+ mModifiedAttributes.clear();
+}
+
+void Character::modifiedAttribute(int attr)
+{
+ if (attr >= CHAR_ATTR_BEGIN && attr < CHAR_ATTR_END)
+ {
+ /* FIXME: The following formulas are for testing purpose only. They
+ should be replaced by a real system once designed. */
+ setAttribute(BASE_ATTR_HP, getModifiedAttribute(CHAR_ATTR_VITALITY));
+ setAttribute(BASE_ATTR_PHY_ATK, getModifiedAttribute(CHAR_ATTR_STRENGTH));
+ setAttribute(BASE_ATTR_PHY_RES, getModifiedAttribute(CHAR_ATTR_VITALITY));
+ setAttribute(BASE_ATTR_MAG_RES, getModifiedAttribute(CHAR_ATTR_WILLPOWER));
+ setAttribute(BASE_ATTR_EVADE, getModifiedAttribute(CHAR_ATTR_DEXTERITY));
+ // We have just modified the computed attributes. Mark them as such.
+ for (int i = BASE_ATTR_BEGIN; i < BASE_ATTR_END; ++i)
+ {
+ mModifiedAttributes.push_back(i);
+ }
+ }
+ mModifiedAttributes.push_back(attr);
+}
+
+void Character::flagAttribute(int attr)
+{
+ // Warn the player of this attribute modification.
+ std::vector< unsigned char >::iterator
+ i_end = mModifiedAttributes.end(),
+ i = std::find(mModifiedAttributes.begin(), i_end, (unsigned char)attr);
+ if (i == i_end) mModifiedAttributes.push_back(attr);
+}
diff --git a/src/game-server/character.hpp b/src/game-server/character.hpp
index 6d6e3376..3119da7a 100644
--- a/src/game-server/character.hpp
+++ b/src/game-server/character.hpp
@@ -52,7 +52,12 @@ class Character : public Being
/**
* Updates the internal status.
*/
- void update();
+ void update() {}
+
+ /**
+ * Perform actions.
+ */
+ void perform();
/**
* Gets client computer.
@@ -177,23 +182,10 @@ class Character : public Being
{ mLevel = level; }
/**
- * Gets the value of an attribute of the character.
- */
- int getBaseAttribute(int attributeNumber) const
- { return getAttribute(attributeNumber); }
-
- /**
- * Sets the value of an attribute of the character.
+ * Sends a message that informs the client about attribute
+ * modified since last call.
*/
- void setBaseAttribute(int attributeNumber, int value)
- { setAttribute(attributeNumber, value); }
-
- /**
- * Creates a message that informs the client about the attribute
- * changes since last call.
- */
- void
- writeAttributeUpdateMessage(MessageOut &msg);
+ void sendStatus();
/**
* Gets the ID of the map that the character is on.
@@ -207,22 +199,20 @@ class Character : public Being
*/
void setMapId(int);
- protected:
/**
- * Calculates all derived attributes
+ * Updates base Being attributes.
*/
- void calculateDerivedAttributes();
-
- /**
- * Gets the stats of the currently equipped weapon that are relevant
- * for damage calculation
- */
- virtual WeaponStats getWeaponStats();
+ void modifiedAttribute(int);
private:
Character(Character const &);
Character &operator=(Character const &);
+ /**
+ * Marks attribute as recently modified.
+ */
+ void flagAttribute(int);
+
enum TransactionType
{ TRANS_NONE, TRANS_TRADE, TRANS_BUYSELL };
@@ -230,11 +220,11 @@ class Character : public Being
/** Handler of the transaction the character is involved in. */
void *mTransactionHandler;
- /** Atributes as the client should currently know them. */
- std::vector<unsigned short> mOldAttributes;
-
Possessions mPossessions; /**< Possesssions of the character. */
+ /** Attributes modified since last update. */
+ std::vector< unsigned char > mModifiedAttributes;
+
std::string mName; /**< Name of the character. */
int mDatabaseID; /**< Character's database ID. */
unsigned char mGender; /**< Gender of the character. */
@@ -242,12 +232,6 @@ class Character : public Being
unsigned char mHairColor; /**< Hair Color of the character. */
unsigned char mLevel; /**< Level of the character. */
TransactionType mTransaction; /**< Trade/buy/sell action the character is involved in. */
-
- /**
- * true when one or more attributes might have changed since the
- * client has been updated about them.
- */
- bool mAttributesChanged;
};
#endif // _TMWSERV_CHARACTER_HPP_
diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp
index 2913e3f0..59cb9b31 100644
--- a/src/game-server/gamehandler.cpp
+++ b/src/game-server/gamehandler.cpp
@@ -490,7 +490,12 @@ GameHandler::tokenMatched(GameClient* computer, Character* character)
GameState::insert(character);
+ // Force sending the whole character to the client.
Inventory(character).sendFull();
+ for (int i = 0; i < NB_CHARACTER_ATTRIBUTES; ++i)
+ {
+ character->modifiedAttribute(i);
+ }
}
void
diff --git a/src/game-server/item.cpp b/src/game-server/item.cpp
index e3934202..dc99511d 100644
--- a/src/game-server/item.cpp
+++ b/src/game-server/item.cpp
@@ -23,6 +23,37 @@
#include "game-server/item.hpp"
+int ItemModifiers::getValue(int type) const
+{
+ for (std::vector< ItemModifier >::const_iterator i = mModifiers.begin(),
+ i_end = mModifiers.end(); i != i_end; ++i)
+ {
+ if (i->type == type) return i->value;
+ }
+ return 0;
+}
+
+int ItemModifiers::getAttributeValue(int attr) const
+{
+ return getValue(MOD_ATTRIBUTE + attr);
+}
+
+void ItemModifiers::setValue(int type, int value)
+{
+ if (value)
+ {
+ ItemModifier m;
+ m.type = type;
+ m.value = value;
+ mModifiers.push_back(m);
+ }
+}
+
+void ItemModifiers::setAttributeValue(int attr, int value)
+{
+ setValue(MOD_ATTRIBUTE + attr, value);
+}
+
bool ItemClass::use(Being *itemUser)
{
bool usedSuccessfully = true;
diff --git a/src/game-server/item.hpp b/src/game-server/item.hpp
index e563d67a..83706b9c 100644
--- a/src/game-server/item.hpp
+++ b/src/game-server/item.hpp
@@ -24,8 +24,7 @@
#ifndef _TMWSERV_ITEM
#define _TMWSERV_ITEM
-// For NB_BASE_ATTRIBUTES and NB_DERIVED_ATTRIBUTES
-#include "defines.h"
+#include <vector>
#include "game-server/character.hpp"
@@ -61,7 +60,7 @@ enum
WPNTYPE_JAVELIN, // 4
WPNTYPE_ROD, // 5
WPNTYPE_STAFF, // 6
- WPNTYPE_WIPE, // 7
+ WPNTYPE_WHIP, // 7
WPNTYPE_PROJECTILE, // 8
WPNTYPE_BOOMERANG, // 9
WPNTYPE_BOW, // 10
@@ -102,25 +101,41 @@ enum
};
/**
- * statistics modifiers.
- * once for usables.
- * Permanent for equipment.
+ * Item modifier types.
*/
-struct Modifiers
+enum
{
- // General
- unsigned char element; /**< Item Element */
- unsigned char beingStateEffect; /**< Being State (dis)alteration */
- unsigned short lifetime; /**< Modifiers lifetime in seconds. */
-
- // Characteristics Modifiers
- short attributes[NB_ATTRIBUTES_CHAR]; /**< Attribute modifiers */
+ MOD_WEAPON_TYPE = 0,
+ MOD_WEAPON_RANGE,
+ MOD_WEAPON_DAMAGE,
+ MOD_ELEMENT_TYPE,
+ MOD_LIFETIME,
+ MOD_ATTRIBUTE
+};
- // Weapon
- unsigned short range; /**< Weapon Item Range */
- unsigned char weaponType; /**< Weapon Type enum */
+/**
+ * Characteristic of an item.
+ */
+struct ItemModifier
+{
+ unsigned char type;
+ signed short value;
};
+/**
+ * Set of item characteristics.
+ */
+class ItemModifiers
+{
+ public:
+ int getValue(int type) const;
+ void setValue(int type, int amount);
+ int getAttributeValue(int attr) const;
+ void setAttributeValue(int attr, int amount);
+
+ private:
+ std::vector< ItemModifier > mModifiers;
+};
/**
* Class for simple reference to item information.
@@ -183,13 +198,13 @@ class ItemClass
/**
* Gets item modifiers.
*/
- Modifiers const &getModifiers() const
+ ItemModifiers const &getModifiers() const
{ return mModifiers; }
/**
* Sets item modifiers.
*/
- void setModifiers(Modifiers const &modifiers)
+ void setModifiers(ItemModifiers const &modifiers)
{ mModifiers = modifiers; }
/**
@@ -216,7 +231,6 @@ class ItemClass
int getSpriteID()
{ return mSpriteID; }
-
private:
/**
@@ -232,7 +246,7 @@ class ItemClass
unsigned short mCost; /**< Unit cost the item. */
unsigned short mMaxPerSlot; /**< Max item amount per slot in inventory. */
std::string mScriptName; /**< Item script. */
- Modifiers mModifiers; /**< Item modifiers. */
+ ItemModifiers mModifiers; /**< Item modifiers. */
};
class Item: public Object
diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp
index 0346ada7..f22f2bf2 100644
--- a/src/game-server/itemmanager.cpp
+++ b/src/game-server/itemmanager.cpp
@@ -90,21 +90,21 @@ void ItemManager::initialize(std::string const &itemReferenceFile)
std::string scriptName = XML::getProperty(node, "script_name", std::string());
//TODO: add child nodes for these modifiers (additive and factor)
- Modifiers modifiers;
- modifiers.element = XML::getProperty(node, "element", 0);
- modifiers.lifetime = XML::getProperty(node, "lifetime", 0);
- modifiers.attributes[BASE_ATTR_STRENGTH] = XML::getProperty(node, "strength", 0);
- modifiers.attributes[BASE_ATTR_AGILITY] = XML::getProperty(node, "agility", 0);
- modifiers.attributes[BASE_ATTR_DEXTERITY] = XML::getProperty(node, "dexterity", 0);
- modifiers.attributes[BASE_ATTR_VITALITY] = XML::getProperty(node, "vitality", 0);
- modifiers.attributes[BASE_ATTR_INTELLIGENCE] = XML::getProperty(node, "intelligence", 0);
- modifiers.attributes[BASE_ATTR_WILLPOWER] = XML::getProperty(node, "willpower", 0);
- modifiers.attributes[BASE_ATTR_CHARISMA] = XML::getProperty(node, "charisma", 0);
- modifiers.attributes[DERIVED_ATTR_HP_MAXIMUM] = XML::getProperty(node, "hp", 0);
- modifiers.attributes[DERIVED_ATTR_PHYSICAL_ATTACK_MINIMUM] = XML::getProperty(node, "attack", 0);
- modifiers.attributes[DERIVED_ATTR_PHYSICAL_DEFENCE] = XML::getProperty(node, "defence", 0);
- modifiers.range = XML::getProperty(node, "range", 0);
- modifiers.weaponType = XML::getProperty(node, "weapon_type", 0);
+ ItemModifiers modifiers;
+ modifiers.setValue(MOD_WEAPON_TYPE, XML::getProperty(node, "weapon_type", 0));
+ modifiers.setValue(MOD_WEAPON_RANGE, XML::getProperty(node, "range", 0));
+ modifiers.setValue(MOD_WEAPON_DAMAGE, XML::getProperty(node, "attack", 0));
+ modifiers.setValue(MOD_ELEMENT_TYPE, XML::getProperty(node, "element", 0));
+ modifiers.setValue(MOD_LIFETIME, XML::getProperty(node, "lifetime", 0));
+ modifiers.setAttributeValue(BASE_ATTR_HP, XML::getProperty(node, "hp", 0));
+ modifiers.setAttributeValue(BASE_ATTR_PHY_RES, XML::getProperty(node, "defense", 0));
+ modifiers.setAttributeValue(CHAR_ATTR_STRENGTH, XML::getProperty(node, "strength", 0));
+ modifiers.setAttributeValue(CHAR_ATTR_AGILITY, XML::getProperty(node, "agility", 0));
+ modifiers.setAttributeValue(CHAR_ATTR_DEXTERITY, XML::getProperty(node, "dexterity", 0));
+ modifiers.setAttributeValue(CHAR_ATTR_VITALITY, XML::getProperty(node, "vitality", 0));
+ modifiers.setAttributeValue(CHAR_ATTR_INTELLIGENCE, XML::getProperty(node, "intelligence", 0));
+ modifiers.setAttributeValue(CHAR_ATTR_WILLPOWER, XML::getProperty(node, "willpower", 0));
+ modifiers.setAttributeValue(CHAR_ATTR_CHARISMA, XML::getProperty(node, "charisma", 0));
if (maxPerSlot == 0)
{
diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp
index d22c60b2..45b0fbbe 100644
--- a/src/game-server/monster.cpp
+++ b/src/game-server/monster.cpp
@@ -53,12 +53,12 @@ Monster::Monster(MonsterClass *specy):
LOG_DEBUG("Monster spawned!");
mAgressive = false; // TODO: Get from monster database
mAgressionRange = 10; // TODO: Get from monster database
- // TODO: Fill with the real attributes
- mAttributes.resize(NB_ATTRIBUTES_CONTROLLED, 1);
- // Some bogus values for testing monster attacks on players
- setAttribute(BASE_ATTR_STRENGTH, 10);
- setAttribute(MONSTER_SKILL_WEAPON, 3);
+ // Some bogus stats for testing.
+ setSpeed(300);
+ setSize(8);
+ setAttribute(BASE_ATTR_HP, 10);
+ setAttribute(BASE_ATTR_PHY_ATK, 10);
// Set positions relative to target from which the monster can attack
mAttackPositions.push_back(AttackPosition(+32, 0, DIRECTION_LEFT));
@@ -77,6 +77,23 @@ Monster::~Monster()
}
}
+void Monster::perform()
+{
+ if (mAttackTime != mAttackAftDelay) return;
+
+ mAction = ATTACK;
+ raiseUpdateFlags(UPDATEFLAG_ATTACK);
+
+ // Hard-coded values for now.
+ Damage damage;
+ damage.base = getModifiedAttribute(BASE_ATTR_PHY_ATK) / 10;
+ damage.delta = 2;
+ damage.cth = 50;
+ damage.element = ELEMENT_NEUTRAL;
+ damage.type = DAMAGE_PHYSICAL;
+ performAttack(damage);
+}
+
void Monster::update()
{
// If dead do nothing but rot
@@ -85,7 +102,8 @@ void Monster::update()
mCountDown--;
if (mCountDown <= 0)
{
- raiseUpdateFlags(UPDATEFLAG_REMOVE);
+ DelayedEvent e = { EVENT_REMOVE};
+ GameState::enqueueEvent(this, e);
}
return;
}
@@ -93,11 +111,6 @@ void Monster::update()
// If currently attacking finish attack;
if (mAttackTime)
{
- if (mAttackTime == mAttackAftDelay)
- {
- mAction = ATTACK;
- raiseUpdateFlags(UPDATEFLAG_ATTACK);
- }
mAttackTime--;
return;
}
@@ -219,22 +232,23 @@ void Monster::died(Being *being)
mDeathListeners.remove((DeathListener *)being);
}
-int Monster::damage(Damage damage)
+int Monster::damage(Object *source, Damage const &damage)
{
- int HPLoss = Being::damage(damage);
- if ( HPLoss
- && damage.source
- && damage.source->getType() == OBJECT_CHARACTER
- )
+ int HPLoss = Being::damage(source, damage);
+ if (getModifiedAttribute(BASE_ATTR_HP) && HPLoss && source &&
+ source->getType() == OBJECT_CHARACTER)
{
- if (mAnger.find(damage.source) == mAnger.end())
+ Being *s = static_cast< Being * >(source);
+ std::pair< std::map< Being *, int >::iterator, bool > ib =
+ mAnger.insert(std::make_pair(s, HPLoss));
+
+ if (ib.second)
{
- damage.source->addDeathListener(this);
- mAnger[damage.source] = HPLoss;
+ s->addDeathListener(this);
}
else
{
- mAnger[damage.source] += HPLoss;
+ ib.first->second += HPLoss;
}
}
return HPLoss;
@@ -254,25 +268,3 @@ void Monster::die()
}
}
-WeaponStats Monster::getWeaponStats()
-{
-
- WeaponStats weaponStats;
-
- /*
- * TODO: This should all be set by the monster database
- */
- weaponStats.piercing = 1;
- weaponStats.element = ELEMENT_NEUTRAL;
- weaponStats.skill = MONSTER_SKILL_WEAPON;
-
- return weaponStats;
-}
-
-void Monster::calculateDerivedAttributes()
-{
- Being::calculateDerivedAttributes();
- /*
- * Do any monster specific attribute calculation here
- */
-}
diff --git a/src/game-server/monster.hpp b/src/game-server/monster.hpp
index 19625f56..15e2a558 100644
--- a/src/game-server/monster.hpp
+++ b/src/game-server/monster.hpp
@@ -119,6 +119,11 @@ class Monster : public Being, public DeathListener
void update();
/**
+ * Performs an attack, if needed.
+ */
+ void perform();
+
+ /**
* Kills the being
*/
virtual void die();
@@ -126,7 +131,7 @@ class Monster : public Being, public DeathListener
/**
* Calls the damage function in Being and updates the aggro list
*/
- virtual int damage(Damage damage);
+ virtual int damage(Object *source, Damage const &damage);
/**
* Getting informed that a being that might be on the target list died
@@ -142,18 +147,6 @@ class Monster : public Being, public DeathListener
died(being);
}
- protected:
- /**
- * Gets the stats of the currently equipped weapon that are relevant
- * for damage calculation
- */
- virtual WeaponStats getWeaponStats();
-
- /**
- * Calculates all derived attributes
- */
- void calculateDerivedAttributes();
-
private:
int calculatePositionPriority(Point position, int targetPriority);
diff --git a/src/game-server/movingobject.hpp b/src/game-server/movingobject.hpp
index 2763c530..abad313a 100644
--- a/src/game-server/movingobject.hpp
+++ b/src/game-server/movingobject.hpp
@@ -105,6 +105,11 @@ class MovingObject : public Object
{ return mSize; }
/**
+ * Performs actions scheduled by the object.
+ */
+ virtual void perform() {}
+
+ /**
* Moves the object toward its destination.
*/
virtual void move();
diff --git a/src/game-server/object.hpp b/src/game-server/object.hpp
index 0fb63783..ed0af356 100644
--- a/src/game-server/object.hpp
+++ b/src/game-server/object.hpp
@@ -36,8 +36,7 @@ enum
UPDATEFLAG_NEW_DESTINATION = 2,
UPDATEFLAG_ATTACK = 4,
UPDATEFLAG_ACTIONCHANGE = 8,
- UPDATEFLAG_LOOKSCHANGE = 16,
- UPDATEFLAG_REMOVE = 32
+ UPDATEFLAG_LOOKSCHANGE = 16
};
/**
diff --git a/src/game-server/spawnarea.cpp b/src/game-server/spawnarea.cpp
index 47a98493..0f2ff595 100644
--- a/src/game-server/spawnarea.cpp
+++ b/src/game-server/spawnarea.cpp
@@ -70,12 +70,6 @@ SpawnArea::update()
Being *being = new Monster(mSpecy);
being->addDeathListener(this);
- // some bogus stats for testing
- being->setSpeed(300);
- being->setSize(8);
- being->setAttribute(BASE_ATTR_VITALITY, 10);
- being->fillHitpoints();
-
being->setMap(map);
being->setPosition(position);
being->clearDestination();
diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp
index f5dcf917..ed111abc 100644
--- a/src/game-server/state.cpp
+++ b/src/game-server/state.cpp
@@ -62,36 +62,22 @@ static void updateMap(MapComposite *map)
(*i)->update();
}
- // 2. perform attacks.
- for (MovingObjectIterator i(map->getWholeMapIterator()); i; ++i)
+ // 2. run scripts.
+ if (Script *s = map->getScript())
{
- MovingObject *o = *i;
- if (o->getUpdateFlags() & UPDATEFLAG_ATTACK)
- {
- static_cast< Being * >(o)->performAttack(map);
- }
+ s->update();
}
- // 3. move objects around and update zones.
+ // 3. perform actions.
for (MovingObjectIterator i(map->getWholeMapIterator()); i; ++i)
{
- (*i)->move();
+ (*i)->perform();
}
- // 4. remove dead beings.
+ // 4. move objects around and update zones.
for (MovingObjectIterator i(map->getWholeMapIterator()); i; ++i)
{
- if ((*i)->getUpdateFlags() & UPDATEFLAG_REMOVE)
- {
- DelayedEvent e = { EVENT_REMOVE};
- GameState::enqueueEvent((*i), e);
- }
- }
-
- // 5. update the map itself.
- if (Script *s = map->getScript())
- {
- s->update();
+ (*i)->move();
}
map->update();
}
@@ -212,14 +198,6 @@ static void informPlayer(MapComposite *map, Character *p)
gameHandler->sendTo(p, LooksMsg);
}
- // Send leave messages of dead beings
- if ((oflags & UPDATEFLAG_REMOVE))
- {
- MessageOut leaveMsg(GPMSG_BEING_LEAVE);
- leaveMsg.writeShort(oid);
- gameHandler->sendTo(p, leaveMsg);
- }
-
// Send damage messages.
if (o->canFight())
{
@@ -330,11 +308,8 @@ static void informPlayer(MapComposite *map, Character *p)
if (damageMsg.getLength() > 2)
gameHandler->sendTo(p, damageMsg);
- // Inform client about attribute changes of its character
- MessageOut attributeUpdateMsg(GPMSG_PLAYER_ATTRIBUTE_UPDATE);
- p->writeAttributeUpdateMessage(attributeUpdateMsg);
- if (attributeUpdateMsg.getLength() > 2)
- gameHandler->sendTo(p, attributeUpdateMsg);
+ // Inform client about status change.
+ p->sendStatus();
// Inform client about items on the ground around its character
MessageOut itemMsg(GPMSG_ITEMS);
diff --git a/src/serialize/characterdata.hpp b/src/serialize/characterdata.hpp
index 59edf642..b1ccc6d5 100644
--- a/src/serialize/characterdata.hpp
+++ b/src/serialize/characterdata.hpp
@@ -37,9 +37,9 @@ void serializeCharacterData(T const &data, MessageOut &msg)
msg.writeByte(data.getHairColor());
msg.writeByte(data.getLevel());
- for (int i = 0; i < NB_BASE_ATTRIBUTES; ++i)
+ for (int i = CHAR_ATTR_BEGIN; i < CHAR_ATTR_END; ++i)
{
- msg.writeByte(data.getBaseAttribute(i));
+ msg.writeByte(data.getAttribute(i));
}
msg.writeShort(data.getMapId());
@@ -69,9 +69,9 @@ void deserializeCharacterData(T &data, MessageIn &msg)
data.setHairColor(msg.readByte());
data.setLevel(msg.readByte());
- for (int i = 0; i < NB_BASE_ATTRIBUTES; ++i)
+ for (int i = CHAR_ATTR_BEGIN; i < CHAR_ATTR_END; ++i)
{
- data.setBaseAttribute(i, msg.readByte());
+ data.setAttribute(i, msg.readByte());
}
data.setMapId(msg.readShort());