diff options
author | Philipp Sehmisch <tmw@crushnet.org> | 2007-03-11 21:38:08 +0000 |
---|---|---|
committer | Philipp Sehmisch <tmw@crushnet.org> | 2007-03-11 21:38:08 +0000 |
commit | 99f34a8a72f63b4bd9fc2f3c370c8cbe9b9127cf (patch) | |
tree | c075dcc834e0720e6de819c7514f4fdfa7cfe554 /src | |
parent | 0a74cd8c92844e730cf6f56bdc3dd578c65a10d0 (diff) | |
download | manaserv-99f34a8a72f63b4bd9fc2f3c370c8cbe9b9127cf.tar.gz manaserv-99f34a8a72f63b4bd9fc2f3c370c8cbe9b9127cf.tar.bz2 manaserv-99f34a8a72f63b4bd9fc2f3c370c8cbe9b9127cf.tar.xz manaserv-99f34a8a72f63b4bd9fc2f3c370c8cbe9b9127cf.zip |
Implemented stat handling infrastructure and basic damage calculation.
Diffstat (limited to 'src')
-rw-r--r-- | src/controller.cpp | 16 | ||||
-rw-r--r-- | src/controller.h | 12 | ||||
-rw-r--r-- | src/game-server/being.cpp | 116 | ||||
-rw-r--r-- | src/game-server/being.hpp | 119 | ||||
-rw-r--r-- | src/game-server/item.hpp | 6 | ||||
-rw-r--r-- | src/game-server/itemmanager.cpp | 27 | ||||
-rw-r--r-- | src/game-server/player.cpp | 43 | ||||
-rw-r--r-- | src/game-server/player.hpp | 64 | ||||
-rw-r--r-- | src/game-server/testing.cpp | 10 |
9 files changed, 339 insertions, 74 deletions
diff --git a/src/controller.cpp b/src/controller.cpp index d71461f2..737a01b9 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -24,6 +24,15 @@ #include "utils/logger.h" +Controlled::Controlled(int type): + Being(type, 65535), + mCountDown(0) +{ + mStats.base.resize(NB_STATS_BEING, 1); //TODO: fill with the real values + mStats.absoluteModificator.resize(NB_STATS_BEING, 0); + mStats.percentModificators.resize(NB_STATS_BEING); +} + void Controlled::update() { /* Temporary "AI" behaviour that is purely artificial and not at all @@ -57,3 +66,10 @@ void Controlled::die() mCountDown = 600; Being::die(); } + +void Controlled::calculateStats() +{ + /* All base stats of a monster should be set directly by the monster + * database, so there is nothing we should have to calculate here. + */ +} diff --git a/src/controller.h b/src/controller.h index 40e4b22b..f99a2b74 100644 --- a/src/controller.h +++ b/src/controller.h @@ -34,10 +34,7 @@ class Controlled: public Being /** * Constructor. */ - Controlled(int type): - Being(type, 65535), - mCountDown(0) - {} + Controlled(int type); /** * Performs one step of controller logic. @@ -49,6 +46,13 @@ class Controlled: public Being */ virtual void die(); + /** + * Recalculates all stats of the being that are derived from others. + * Call whenever you change something that affects a derived stat. + * Called automatically when you manipulate a stat using setBaseStat() + */ + virtual void calculateStats(); + private: /** Count down till next random movement (temporary). */ unsigned int mCountDown; diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp index 046423d6..3364d23f 100644 --- a/src/game-server/being.cpp +++ b/src/game-server/being.cpp @@ -31,10 +31,29 @@ void Being::damage(Damage damage) { if (mAction == DEAD) return; - int HPloss; + // TODO: Implement dodge chance - HPloss = damage; // TODO: Implement complex damage calculation here + int HPloss = damage.value; + // TODO: Implement elemental modifier + + switch (damage.type) + { + case DAMAGETYPE_PHYSICAL: + HPloss -= getRealStat(STAT_PHYSICAL_DEFENCE) / damage.penetration; + break; + case DAMAGETYPE_MAGICAL: + // NIY + break; + case DAMAGETYPE_HAZARD: + // NIY + break; + case DAMAGETYPE_OTHER: + // nothing to do here + break; + } + + if (HPloss < 0) HPloss = 0; if (HPloss > mHitpoints) HPloss = mHitpoints; mHitpoints -= HPloss; @@ -70,8 +89,8 @@ void Being::move() void Being::performAttack(MapComposite *map) { - int SHORT_RANGE = 32; - int SMALL_ANGLE = 15; + int SHORT_RANGE = 64; + int SMALL_ANGLE = 45; Point ppos = getPosition(); int dir = getDirection(); @@ -95,10 +114,6 @@ void Being::performAttack(MapComposite *map) break; } -/* TODO: calculate real attack power and damage properties based on - character equipment and stats. */ - Damage damage = 1; - for (MovingObjectIterator i(map->getAroundObjectIterator(this, SHORT_RANGE)); i; ++i) { MovingObject *o = *i; @@ -115,7 +130,7 @@ void Being::performAttack(MapComposite *map) ppos, SHORT_RANGE, SMALL_ANGLE, attackAngle) ) { - static_cast< Being * >(o)->damage(damage); + static_cast< Being * >(o)->damage(getPhysicalAttackDamage()); } } } @@ -129,3 +144,86 @@ void Being::setAction(Action action) raiseUpdateFlags(UPDATEFLAG_ACTIONCHANGE); } } + +void Being::addAbsoluteStatModifier(unsigned numStat, short value) +{ + mStats.absoluteModificator.at(numStat) = mStats.absoluteModificator.at(numStat) + value; +} + +void Being::removeAbsoluteStatModifier(unsigned numStat, short value) +{ + mStats.absoluteModificator.at(numStat) = mStats.absoluteModificator.at(numStat) - value; +} + +void Being::addPercentStatModifier(unsigned numStat, short value) +{ + if (value < -100) + { + LOG_WARN( "Attempt to add a stat modificator for Being"<< + getPublicID()<< + "that would make the stat negative!" + ); + } + else + { + mStats.percentModificators.at(numStat).push_back(value); + } +} + +void Being::removePercentStatModifier(unsigned numStat, short value) +{ + std::list<short>::iterator i = mStats.percentModificators.at(numStat).begin(); + + while (i != mStats.percentModificators.at(numStat).end()) + { + if ((*i) = value) + { + mStats.percentModificators.at(numStat).erase(i); + return; + } + } + LOG_WARN( "Attempt to remove a stat modificator for Being"<< + getPublicID()<< + "that hasn't been added before!" + ); +} + +unsigned short Being::getRealStat(unsigned numStat) +{ + int value = mStats.base.at(numStat) + mStats.absoluteModificator.at(numStat); + std::list<short>::iterator i; + + float multiplier = 1.0f; + for ( i = mStats.percentModificators.at(numStat).begin(); + i != mStats.percentModificators.at(numStat).end(); + i++ + ) + { + multiplier *= (100.0f + (float)(*i)) / 100.0f; + } + + /* Floating point inaccuracies might result in a negative multiplier. That + * would result in a stat near 2^16. To make sure that this doesn't happen + * we return a value of 0 in that case + */ + if (multiplier < 0.0f) + { + return 0; + } + else + { + return (unsigned short)(value * multiplier); + } +} + +Damage Being::getPhysicalAttackDamage() +{ + Damage damage; + damage.type = DAMAGETYPE_PHYSICAL; + damage.value = getRealStat(STAT_PHYSICAL_ATTACK_MINIMUM) + (rand()%getRealStat(STAT_PHYSICAL_ATTACK_FLUCTUATION)); + damage.penetration = 1; // TODO: get from equipped weapon + damage.element = ELEMENT_NEUTRAL; // TODO: get from equipped weapon + damage.source = this; + + return damage; +} diff --git a/src/game-server/being.hpp b/src/game-server/being.hpp index 2eaffa16..77b7bfc8 100644 --- a/src/game-server/being.hpp +++ b/src/game-server/being.hpp @@ -30,12 +30,14 @@ #include "defines.h" #include "game-server/object.hpp" +class Being; class MapComposite; /** * Element attribute for beings, actors and items. + * Subject to change until pauan and dabe are finished with the element system. */ -enum +enum Element { ELEMENT_NEUTRAL = 0, ELEMENT_FIRE, @@ -75,38 +77,43 @@ enum DIRECTION_RIGHT }; - /** - * Computed statistics of a Being. + * Methods of damage calculation */ -enum +enum Damagetype { - STAT_HEAT = 0, - STAT_ATTACK, - STAT_DEFENCE, - STAT_MAGIC, - STAT_ACCURACY, - STAT_SPEED, - NB_CSTAT + DAMAGETYPE_PHYSICAL, + DAMAGETYPE_MAGICAL, + DAMAGETYPE_HAZARD, + DAMAGETYPE_OTHER }; /** - * Structure type for the computed statistics of a Being. + * Structure describing severity and nature of an attack a being can suffer of */ -struct Statistics +struct Damage { - unsigned short stats[NB_CSTAT]; + int value; + int penetration; + Element element; + Damagetype type; + Being *source; }; /** - * Placeholder for a more complex damage structure + * Type definition for a list of hits */ -typedef unsigned short Damage; +typedef std::list<unsigned int> Hits; /** - * Type definition for a list of hits + * Structure type for the stats of a Being. */ -typedef std::list<unsigned int> Hits; +struct Stats +{ + std::vector<unsigned short> base; + std::vector<short> absoluteModificator; + std::vector< std::list<short> > percentModificators; +}; /** * Generic Being (living object). @@ -115,6 +122,20 @@ typedef std::list<unsigned int> Hits; class Being : public MovingObject { public: + + /** + * Computed statistics of a Being. + */ + enum Stat + { + STAT_HP_MAXIMUM, + STAT_PHYSICAL_ATTACK_MINIMUM, + STAT_PHYSICAL_ATTACK_FLUCTUATION, + STAT_PHYSICAL_DEFENCE, + // add new computed statistics on demand + NB_STATS_BEING + }; + /** * Moves enum for beings and actors for others players vision. * WARNING: Has to be in sync with the same enum in the Being class @@ -128,6 +149,7 @@ class Being : public MovingObject DEAD, HURT }; + /** * Proxy constructor. */ @@ -137,27 +159,70 @@ class Being : public MovingObject {} /** - * Sets a computed statistic. + * Sets a being statistic. * * @param numStat the statistic number. * @param value the new value. */ - void setStat(int numStat, unsigned short value) - { mStats.stats[numStat] = value; } + void setBaseStat(unsigned numStat, unsigned short value) + { mStats.base[numStat] = value; + calculateBaseStats(); + } + + /** + * Adds a fixed value stat modifier + */ + void addAbsoluteStatModifier(unsigned numStat, short value); + + /** + * Removes a fixed value stat modifier + */ + void removeAbsoluteStatModifier(unsigned numStat, short value); + + /** + * Adds a multiplier stat modificator in percent + */ + void addPercentStatModifier(unsigned numStat, short value); /** - * Gets a computed statistic. + * Removes a previously added percent stat modifier. + * Does nothing and logs a warning when no modifier with the same + * value has been added before. + */ + void removePercentStatModifier(unsigned numStat, short value); + + /** + * Returns a being statistic without temporary modifiers * * @param numStat the statistic number. * @return the statistic value. */ - unsigned short getStat(int numStat) - { return mStats.stats[numStat]; } + unsigned short getBaseStat(unsigned stat) + { return mStats.base.at(stat); } + + /** + * Returns a being statistic with added temporary modifiers + */ + unsigned short getRealStat(unsigned stat); + + /** + * Recalculates all stats of the being that are derived from others. + * Call whenever you change something that affects a derived stat. + * Called automatically when you manipulate a stat using setBaseStat() + */ + virtual void calculateBaseStats() + { /*NOOP*/ }; + + /** + * Creates a damage structure for a normal melee attack based on the + * current being stats and equipment. + */ + Damage getPhysicalAttackDamage(); /** * sets the hit points */ - void setHitpoints(int hp) + void setHitpoints(unsigned hp) { mHitpoints = hp; } /** @@ -206,12 +271,12 @@ class Being : public MovingObject int mHitpoints; /**< Hitpoints of the being */ Action mAction; + Stats mStats; + private: Being(Being const &rhs); Being &operator=(Being const &rhs); - Statistics mStats; /**< stats modifiers or computed stats */ - Hits mHitsTaken; /**< List of punches taken since last update */ }; diff --git a/src/game-server/item.hpp b/src/game-server/item.hpp index 21b9e6dd..40a687ab 100644 --- a/src/game-server/item.hpp +++ b/src/game-server/item.hpp @@ -111,11 +111,7 @@ struct Modifiers unsigned short lifetime; /**< Modifiers lifetime in seconds. */ // Caracteristics Modifiers - short rawStats[NB_RSTAT]; /**< Raw Stats modifiers */ - short computedStats[NB_CSTAT]; /**< Computed Stats modifiers */ - - short hp; /**< HP modifier */ - short mp; /**< MP Modifier */ + short stat[Player::NB_STATS_PLAYER]; /**< Stat modifiers */ // Weapon unsigned short range; /**< Weapon Item Range */ diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp index b80f295b..ea060d06 100644 --- a/src/game-server/itemmanager.cpp +++ b/src/game-server/itemmanager.cpp @@ -84,20 +84,19 @@ ItemManager::ItemManager(std::string const &itemReferenceFile) Modifiers modifiers; modifiers.element = XML::getProperty(node, "element", 0); modifiers.lifetime = XML::getProperty(node, "lifetime", 0); - modifiers.rawStats[STAT_STRENGTH] = XML::getProperty(node, "strength", 0); - modifiers.rawStats[STAT_AGILITY] = XML::getProperty(node, "agility", 0); - modifiers.rawStats[STAT_VITALITY] = XML::getProperty(node, "vitality", 0); - modifiers.rawStats[STAT_INTELLIGENCE] = XML::getProperty(node, "intelligence", 0); - modifiers.rawStats[STAT_DEXTERITY] = XML::getProperty(node, "dexterity", 0); - modifiers.rawStats[STAT_LUCK] = XML::getProperty(node, "luck", 0); - modifiers.computedStats[STAT_HEAT] = XML::getProperty(node, "heat", 0); - modifiers.computedStats[STAT_ATTACK] = XML::getProperty(node, "attack", 0); - modifiers.computedStats[STAT_DEFENCE] = XML::getProperty(node, "defence", 0); - modifiers.computedStats[STAT_MAGIC] = XML::getProperty(node, "magic", 0); - modifiers.computedStats[STAT_ACCURACY] = XML::getProperty(node, "accuracy", 0); - modifiers.computedStats[STAT_SPEED] = XML::getProperty(node, "speed", 0); - modifiers.hp = XML::getProperty(node, "hp", 0); - modifiers.mp = XML::getProperty(node, "mp", 0); + modifiers.stat[Player::STRENGTH] = XML::getProperty(node, "strength", 0); + modifiers.stat[Player::AGILITY] = XML::getProperty(node, "agility", 0); + modifiers.stat[Player::VITALITY] = XML::getProperty(node, "vitality", 0); + modifiers.stat[Player::INTELLIGENCE] = XML::getProperty(node, "intelligence", 0); + modifiers.stat[Player::DEXTERITY] = XML::getProperty(node, "dexterity", 0); + modifiers.stat[Player::WILLPOWER] = XML::getProperty(node, "willpower", 0); + modifiers.stat[Player::CHARISMA] = XML::getProperty(node, "charisma", 0); + + modifiers.stat[Being::STAT_HP_MAXIMUM] = XML::getProperty(node, "hp", 0); + modifiers.stat[Being::STAT_PHYSICAL_ATTACK_MINIMUM] = XML::getProperty(node, "attack", 0); + modifiers.stat[Being::STAT_PHYSICAL_DEFENCE] = XML::getProperty(node, "defence", 0); + + modifiers.range = XML::getProperty(node, "range", 0); modifiers.weaponType = XML::getProperty(node, "weapon_type", 0); modifiers.beingStateEffect = XML::getProperty(node, "status_effect", 0); diff --git a/src/game-server/player.cpp b/src/game-server/player.cpp index 5580d094..923d2513 100644 --- a/src/game-server/player.cpp +++ b/src/game-server/player.cpp @@ -25,19 +25,30 @@ #include "defines.h" #include "game-server/player.hpp" +Player::Player(std::string const &name, int id) + : Being(OBJECT_PLAYER, 65535), + PlayerData(name, id), + mClient(NULL) +{ + mStats.base.resize(NB_STATS_PLAYER, 1); //TODO: fill with the real values + mStats.absoluteModificator.resize(NB_STATS_PLAYER, 0); + mStats.percentModificators.resize(NB_STATS_PLAYER); + + // some bogus values for testing purpose + mStats.base.at(STRENGTH) = 10; + mStats.base.at(SKILL_WEAPON_UNARMED) = 5; + + calculateBaseStats(); + + mHitpoints = getRealStat(STAT_HP_MAXIMUM); + mSize = 16; +} + /** * Update the internal status. */ void Player::update() { - // computed stats. - setStat(STAT_HEAT, 20 + (20 * getRawStat(STAT_VITALITY))); - setStat(STAT_ATTACK, 10 + getRawStat(STAT_STRENGTH)); - setStat(STAT_DEFENCE, 10 + getRawStat(STAT_STRENGTH)); - setStat(STAT_MAGIC, 10 + getRawStat(STAT_INTELLIGENCE)); - setStat(STAT_ACCURACY, 50 + getRawStat(STAT_DEXTERITY)); - setStat(STAT_SPEED, getRawStat(STAT_DEXTERITY)); - // attacking if (mAction == ATTACK) { @@ -51,3 +62,19 @@ void Player::update() } } } + +void Player::calculateBaseStats() +{ + mStats.base.at(STAT_HP_MAXIMUM) + = getRealStat(VITALITY); + + mStats.base.at(STAT_PHYSICAL_ATTACK_MINIMUM) + = getRealStat(STRENGTH) /* + weapon damage fluctuation*/; + + // TODO: get the skill that is skill required for weapon + mStats.base.at(STAT_PHYSICAL_ATTACK_FLUCTUATION) + = getRealStat(SKILL_WEAPON_UNARMED) /* + weapon damage fluctuation*/; + + mStats.base.at(STAT_PHYSICAL_DEFENCE) + = 42 /* + sum of equipment pieces */; +} diff --git a/src/game-server/player.hpp b/src/game-server/player.hpp index bd1ec997..76c3f591 100644 --- a/src/game-server/player.hpp +++ b/src/game-server/player.hpp @@ -48,15 +48,59 @@ class Player : public Being, public PlayerData { public: - Player(std::string const &name, int id = -1) - : Being(OBJECT_PLAYER, 65535), - PlayerData(name, id), - mClient(NULL) + /** + * Base attributes of a player character + */ + enum Attributes + { + STRENGTH = NB_STATS_BEING, + AGILITY, + DEXTERITY, + VITALITY, + INTELLIGENCE, + WILLPOWER, + CHARISMA, + NB_ATTRIBUTES + }; + + enum WeaponSkills + { + SKILL_WEAPON_UNARMED = NB_ATTRIBUTES, + SKILL_WEAPON_SWORD, + SKILL_WEAPON_AXE, + SKILL_WEAPON_POLEARM, + SKILL_WEAPON_JAVELIN, + SKILL_WEAPON_WHIP, + SKILL_WEAPON_DAGGER, + SKILL_WEAPON_STAFF, + SKILL_WEAPON_BOW, + SKILL_WEAPON_CROSSBOW, + SKILL_WEAPON_THROWN, + NB_WEAPONSKILLS + }; + + enum MagicSkills + { + SKILL_MAGIC_IAMJUSTAPLACEHOLDER = NB_WEAPONSKILLS, + NB_MAGICSKILLS + }; + + enum CraftSkills { - mHitpoints=5; - mSize = 16; + SKILL_CRAFT_IAMJUSTAPLACEHOLDER = NB_MAGICSKILLS, + NB_CRAFTSKILLS + }; + + enum OtherSkills + { + SKILL_IAMJUSTAPLACEHOLDER = NB_CRAFTSKILLS, + NB_OTHERSKILLS } + static const NB_STATS_PLAYER = NB_OTHERSKILLS; + + Player(std::string const &name, int id = -1); + /** * Updates the internal status. */ @@ -74,6 +118,14 @@ class Player : public Being, public PlayerData void setClient(GameClient *c) { mClient = c; } + /** + * Recalculates all player stats that are derived from others. + * Call whenever you change something that affects a derived stat. + * Called automatically when you manipulate a stat using setBaseStat() + */ + virtual void calculateBaseStats(); + + private: Player(Player const &); Player &operator=(Player const &); diff --git a/src/game-server/testing.cpp b/src/game-server/testing.cpp index b48a7e77..7c3f87af 100644 --- a/src/game-server/testing.cpp +++ b/src/game-server/testing.cpp @@ -37,7 +37,15 @@ void testingMap(int id) Being *being = new Controlled(OBJECT_MONSTER); being->setSpeed(150); being->setSize(8); - being->setHitpoints(3); + + // some bogus stats for testing + being->setBaseStat(Being::STAT_HP_MAXIMUM, 42); + being->setBaseStat(Being::STAT_PHYSICAL_ATTACK_MINIMUM, 1); + being->setBaseStat(Being::STAT_PHYSICAL_ATTACK_FLUCTUATION, 0); + being->setBaseStat(Being::STAT_PHYSICAL_DEFENCE, 5); + + being->setHitpoints(being->getRealStat(Being::STAT_HP_MAXIMUM)); + being->setMapId(1); Point pos(720, 900); being->setPosition(pos); |