diff options
Diffstat (limited to 'src/game-server/character.cpp')
-rw-r--r-- | src/game-server/character.cpp | 186 |
1 files changed, 169 insertions, 17 deletions
diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index c87f310f..02094edd 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -22,6 +22,7 @@ #include <algorithm> #include <cassert> +#include <cmath> #include "game-server/character.hpp" @@ -39,14 +40,17 @@ #include "net/messageout.hpp" #include "serialize/characterdata.hpp" +#include "utils/logger.h" + Character::Character(MessageIn &msg): Being(OBJECT_CHARACTER, 65535), mClient(NULL), mTransactionHandler(NULL), mDatabaseID(-1), - mGender(0), mHairStyle(0), mHairColor(0), mLevel(1), - mTransaction(TRANS_NONE) + mGender(0), mHairStyle(0), mHairColor(0), mLevel(1), mLevelProgress(0), + mUpdateLevelProgress(false), mRecalculateLevel(true), mTransaction(TRANS_NONE) { Attribute attr = { 0, 0 }; mAttributes.resize(NB_CHARACTER_ATTRIBUTES, attr); + mExperience.resize(CHAR_SKILL_NB, 0); // Get character data. mDatabaseID = msg.readLong(); mName = msg.readString(); @@ -59,6 +63,16 @@ Character::Character(MessageIn &msg): Inventory(this).initialize(); } +void Character::update() +{ + if (mRecalculateLevel) + { + mRecalculateLevel = false; + recalculateLevel(); + } + Being::update(); +} + void Character::perform() { if (mAction != ATTACK || mActionTime > 0) return; @@ -78,6 +92,7 @@ void Character::perform() damage.type = DAMAGE_PHYSICAL; damage.cth = getModifiedAttribute(BASE_ATTR_HIT) + getModifiedAttribute(CHAR_SKILL_WEAPON_BEGIN + type); + damage.usedSkill = CHAR_SKILL_WEAPON_BEGIN + type; if (type) { ItemModifiers const &mods = ic->getModifiers(); @@ -166,20 +181,37 @@ 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(), + MessageOut attribMsg(GPMSG_PLAYER_ATTRIBUTE_CHANGE); + for (std::set<size_t>::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)); + attribMsg.writeByte(attr); + attribMsg.writeShort(getAttribute(attr)); + attribMsg.writeShort(getModifiedAttribute(attr)); } - gameHandler->sendTo(this, msg); - + if (attribMsg.getLength() > 2) gameHandler->sendTo(this, attribMsg); mModifiedAttributes.clear(); + + MessageOut expMsg(GPMSG_PLAYER_EXP_CHANGE); + for (std::set<size_t>::const_iterator i = mModifiedExperience.begin(), + i_end = mModifiedExperience.end(); i != i_end; ++i) + { + int skill = *i; + expMsg.writeByte(skill); + expMsg.writeLong(getExpGot(skill)); + expMsg.writeLong(getExpNeeded(skill)); + } + if (expMsg.getLength() > 2) gameHandler->sendTo(this, expMsg); + mModifiedExperience.clear(); + + if (mUpdateLevelProgress) + { + mUpdateLevelProgress = false; + MessageOut progressMessage(GPMSG_LEVEL_PROGRESS); + progressMessage.writeByte(mLevelProgress); + gameHandler->sendTo(this, progressMessage); + } } void Character::modifiedAttribute(int attr) @@ -211,8 +243,12 @@ void Character::modifiedAttribute(int attr) /* weapon attack is applied through equip modifiers */ } else if (i == BASE_ATTR_PHY_ATK_DELTA) { - newValue = 0 /* + skill in class of currently equipped weapon */; - /* weapon attack is applied through equip modifiers */ + newValue = 0; + /* + skill in class of currently equipped weapon ( is + * applied during the damage calculation) + * weapon attack bonus is applied through equip + * modifiers. + */ } else if (i == BASE_ATTR_MAG_RES) { newValue = getModifiedAttribute(CHAR_ATTR_WILLPOWER); @@ -234,10 +270,126 @@ void Character::modifiedAttribute(int 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); + mModifiedAttributes.insert(attr); +} + +int Character::expForLevel(int level) +{ + return int(pow(level, EXPCURVE_EXPONENT) * EXPCURVE_FACTOR); +} + +void Character::receiveExperience(size_t skill, int experience) +{ + if (skill >= CHAR_SKILL_BEGIN && skill < CHAR_SKILL_END) + { + // add exp + long int newExp = mExperience.at(skill - CHAR_SKILL_BEGIN) + experience; + if (newExp > INT_MAX) newExp = INT_MAX; // avoid integer overflow. + mExperience.at(skill - CHAR_SKILL_BEGIN) = newExp; + mModifiedExperience.insert(skill - CHAR_SKILL_BEGIN); + + // check for skill levelup + while (newExp >= Character::expForLevel(getAttribute(skill) + 1)) + { + setAttribute(skill, getAttribute(skill) + 1); + modifiedAttribute(skill); + } + + mRecalculateLevel = true; + } +} + +void Character::recalculateLevel() +{ + std::list<float> levels; + for (int a = CHAR_SKILL_BEGIN; a < CHAR_SKILL_END; a++) + { + float expGot = getExpGot(a - CHAR_SKILL_BEGIN); + float expNeed = getExpNeeded(a - CHAR_SKILL_BEGIN); + levels.push_back(getAttribute(a) + expGot / expNeed); + } + levels.sort(); + + std::list<float>::iterator i = levels.end(); + float level = 0.0f; + float factor = 1.0f; + float factorSum = 0.0f; + while (i != levels.begin()) //maybe it wouldn't be a bad idea to unroll this loop + { + i--; + level += *i * factor; + factorSum += factor; + factor *= LEVEL_SKILL_PRECEDENCE_FACTOR; + } + level /= factorSum; + level += 1.0f; // + 1.0f because the lowest level is 1 and not 0 + + while (mLevel < level) + { + levelup(); + } + + int levelProgress = int((level - floor(level)) * 100); + if (levelProgress != mLevelProgress) + { + mLevelProgress = levelProgress; + mUpdateLevelProgress = true; + } +} + +int Character::getExpNeeded(size_t skill) +{ + int level = getAttribute(skill + CHAR_SKILL_BEGIN); + return Character::expForLevel(level + 1) - expForLevel(level); +} + +int Character::getExpGot(size_t skill) +{ + int level = getAttribute(skill + CHAR_SKILL_BEGIN); + return mExperience.at(skill) - Character::expForLevel(level); +} + +void Character::levelup() +{ + mLevel++; + + mCharacterPoints += CHARPOINTS_PER_LEVELUP; + mCorrectionPoints += CORRECTIONPOINTS_PER_LEVELUP; + if (mCorrectionPoints > CORRECTIONPOINTS_MAX) + mCorrectionPoints = CORRECTIONPOINTS_MAX; + + MessageOut levelupMsg(GPMSG_LEVELUP); + levelupMsg.writeShort(mLevel); + levelupMsg.writeShort(mCharacterPoints); + levelupMsg.writeShort(mCorrectionPoints); + gameHandler->sendTo(this, levelupMsg); + LOG_INFO(mName<<" reached level "<<mLevel); +} + +AttribmodResponseCode Character::useCharacterPoint(size_t attribute) +{ + if (attribute < CHAR_ATTR_BEGIN) return ATTRIBMOD_INVALID_ATTRIBUTE; + if (attribute >= CHAR_ATTR_END) return ATTRIBMOD_INVALID_ATTRIBUTE; + if (!mCharacterPoints) return ATTRIBMOD_NO_POINTS_LEFT; + + mCharacterPoints--; + setAttribute(attribute, getAttribute(attribute) + 1); + modifiedAttribute(attribute); + return ATTRIBMOD_OK; +} + +AttribmodResponseCode Character::useCorrectionPoint(size_t attribute) +{ + if (attribute < CHAR_ATTR_BEGIN) return ATTRIBMOD_INVALID_ATTRIBUTE; + if (attribute >= CHAR_ATTR_END) return ATTRIBMOD_INVALID_ATTRIBUTE; + if (!mCorrectionPoints) return ATTRIBMOD_NO_POINTS_LEFT; + if (getAttribute(attribute) <= 1) return ATTRIBMOD_DENIED; + + mCorrectionPoints--; + mCharacterPoints++; + setAttribute(attribute, getAttribute(attribute) - 1); + modifiedAttribute(attribute); + return ATTRIBMOD_OK; } void Character::disconnected() |