diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/account-server/accounthandler.cpp | 14 | ||||
-rw-r--r-- | src/account-server/characterdata.cpp | 4 | ||||
-rw-r--r-- | src/account-server/characterdata.hpp | 13 | ||||
-rw-r--r-- | src/account-server/dalstorage.cpp | 82 | ||||
-rw-r--r-- | src/defines.h | 132 | ||||
-rw-r--r-- | src/game-server/accountconnection.cpp | 3 | ||||
-rw-r--r-- | src/game-server/being.cpp | 163 | ||||
-rw-r--r-- | src/game-server/being.hpp | 144 | ||||
-rw-r--r-- | src/game-server/character.cpp | 145 | ||||
-rw-r--r-- | src/game-server/character.hpp | 54 | ||||
-rw-r--r-- | src/game-server/gamehandler.cpp | 5 | ||||
-rw-r--r-- | src/game-server/item.cpp | 31 | ||||
-rw-r--r-- | src/game-server/item.hpp | 56 | ||||
-rw-r--r-- | src/game-server/itemmanager.cpp | 30 | ||||
-rw-r--r-- | src/game-server/monster.cpp | 78 | ||||
-rw-r--r-- | src/game-server/monster.hpp | 19 | ||||
-rw-r--r-- | src/game-server/movingobject.hpp | 5 | ||||
-rw-r--r-- | src/game-server/object.hpp | 3 | ||||
-rw-r--r-- | src/game-server/spawnarea.cpp | 6 | ||||
-rw-r--r-- | src/game-server/state.cpp | 43 | ||||
-rw-r--r-- | src/serialize/characterdata.hpp | 8 |
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()); |