summaryrefslogtreecommitdiff
path: root/src/game-server/character.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game-server/character.cpp')
-rw-r--r--src/game-server/character.cpp186
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()