00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <algorithm>
00022 #include <cassert>
00023 #include <cmath>
00024 #include <limits.h>
00025
00026 #include "game-server/character.hpp"
00027
00028 #include "defines.h"
00029 #include "common/configuration.hpp"
00030 #include "game-server/accountconnection.hpp"
00031 #include "game-server/attackzone.hpp"
00032 #include "game-server/buysell.hpp"
00033 #include "game-server/eventlistener.hpp"
00034 #include "game-server/inventory.hpp"
00035 #include "game-server/item.hpp"
00036 #include "game-server/itemmanager.hpp"
00037 #include "game-server/gamehandler.hpp"
00038 #include "game-server/mapcomposite.hpp"
00039 #include "game-server/mapmanager.hpp"
00040 #include "game-server/state.hpp"
00041 #include "game-server/trade.hpp"
00042 #include "scripting/script.hpp"
00043 #include "net/messagein.hpp"
00044 #include "net/messageout.hpp"
00045 #include "serialize/characterdata.hpp"
00046
00047 #include "utils/logger.h"
00048
00049
00050 const float Character::EXPCURVE_EXPONENT = 3.0f;
00051 const float Character::EXPCURVE_FACTOR = 10.0f;
00052 const float Character::LEVEL_SKILL_PRECEDENCE_FACTOR = 0.75f;
00053 const AttackZone Character::UNARMED_ATTACK_ZONE = {ATTZONESHAPE_RECT, true, 48, 16};
00054
00055 Character::Character(MessageIn &msg):
00056 Being(OBJECT_CHARACTER),
00057 mClient(NULL), mTransactionHandler(NULL), mDatabaseID(-1),
00058 mGender(0), mHairStyle(0), mHairColor(0), mLevel(1), mLevelProgress(0),
00059 mUpdateLevelProgress(false), mRecalculateLevel(true), mParty(0),
00060 mTransaction(TRANS_NONE)
00061 {
00062 Attribute attr = { 0, 0 };
00063 mAttributes.resize(NB_CHARACTER_ATTRIBUTES, attr);
00064 mExperience.resize(CHAR_SKILL_NB, 0);
00065
00066 mDatabaseID = msg.readLong();
00067 setName(msg.readString());
00068 deserializeCharacterData(*this, msg);
00069 for (int i = CHAR_ATTR_BEGIN; i < CHAR_ATTR_END; ++i)
00070 {
00071 modifiedAttribute(i);
00072 }
00073 setSize(16);
00074 Inventory(this).initialize();
00075
00076
00077
00078 giveSpecial(1);
00079 giveSpecial(2);
00080 giveSpecial(3);
00081
00082 }
00083
00084 void Character::update()
00085 {
00086
00087 if (mRecalculateLevel)
00088 {
00089 mRecalculateLevel = false;
00090 recalculateLevel();
00091 }
00092
00093
00094 std::list<Special *> rechargeNeeded;
00095 int numRechargeNeeded = 0;
00096 for (std::map<int, Special*>::iterator i = mSpecials.begin(); i != mSpecials.end(); i++)
00097 {
00098 Special * s = i->second;
00099 if (s->currentMana < s->neededMana)
00100 {
00101 rechargeNeeded.push_back(s);
00102 numRechargeNeeded++;
00103 }
00104 }
00105 if (numRechargeNeeded > 0)
00106 {
00107 int rechargePerSpecial = getModifiedAttribute(CHAR_ATTR_INTELLIGENCE) / numRechargeNeeded;
00108 for (std::list<Special*>::iterator i = rechargeNeeded.begin(); i != rechargeNeeded.end(); i++)
00109 {
00110 (*i)->currentMana += rechargePerSpecial;
00111 }
00112 }
00113
00114 Being::update();
00115 }
00116
00117 void Character::perform()
00118 {
00119 if (mAction != ATTACK || mActionTime > 0) return;
00120
00121 mActionTime = 1000;
00122 mAction = STAND;
00123 raiseUpdateFlags(UPDATEFLAG_ATTACK);
00124
00125
00126 int itemId = mPossessions.equipment[EQUIP_FIGHT1_SLOT];
00127 ItemClass *ic = ItemManager::getItem(itemId);
00128 int type = ic ? ic->getModifiers().getValue(MOD_WEAPON_TYPE) : WPNTYPE_NONE;
00129
00130 Damage damage;
00131 damage.base = getModifiedAttribute(BASE_ATTR_PHY_ATK_MIN);
00132 damage.delta = getModifiedAttribute(BASE_ATTR_PHY_ATK_DELTA) +
00133 getModifiedAttribute(CHAR_SKILL_WEAPON_BEGIN + type);
00134 damage.type = DAMAGE_PHYSICAL;
00135 damage.cth = getModifiedAttribute(BASE_ATTR_HIT) +
00136 getModifiedAttribute(CHAR_SKILL_WEAPON_BEGIN + type);
00137 damage.usedSkills.push_back(CHAR_SKILL_WEAPON_BEGIN + type);
00138
00139 if (ic)
00140 {
00141
00142 const ItemModifiers &mods = ic->getModifiers();
00143 damage.element = mods.getValue(MOD_ELEMENT_TYPE);
00144 performAttack(damage, ic->getAttackZone());
00145 }
00146 else
00147 {
00148
00149 damage.element = ELEMENT_NEUTRAL;
00150 performAttack(damage, &UNARMED_ATTACK_ZONE);
00151 }
00152
00153 }
00154
00155 void Character::respawn()
00156 {
00157 if (mAction != DEAD)
00158 {
00159 LOG_WARN("Character \""<<getName()<<"\" tried to respawn without being dead");
00160 return;
00161 }
00162
00163
00164 int spawnMap = Configuration::getValue("respawnMap", 1);
00165 int spawnX = Configuration::getValue("respawnX", 1024);
00166 int spawnY = Configuration::getValue("respawnY", 1024);
00167 GameState::enqueueWarp(this, MapManager::getMap(spawnMap), spawnX, spawnY);
00168
00169
00170 setAction(STAND);
00171 mAttributes[BASE_ATTR_HP].mod = -mAttributes[BASE_ATTR_HP].base + 1;
00172 modifiedAttribute(BASE_ATTR_HP);
00173 }
00174
00175 void Character::useSpecial(int id)
00176 {
00177
00178 std::map<int, Special*>::iterator i = mSpecials.find(id);
00179 if (i == mSpecials.end())
00180 {
00181 LOG_INFO("Character uses special "<<id<<" without autorisation.");
00182 return;
00183 }
00184
00185
00186 Special *special = i->second;
00187 if (special->currentMana < special->neededMana)
00188 {
00189 LOG_INFO("Character uses special "<<id<<" which is not recharged. ("
00190 <<special->currentMana<<"/"<<special->neededMana<<")");
00191 return;
00192 }
00193
00194
00195 special->currentMana = 0;
00196 Script *script = getMap()->getScript();
00197 if (script) {
00198 script->prepare("cast");
00199 script->push(this);
00200 script->push(id);
00201 script->execute();
00202 }
00203
00204 return;
00205 }
00206
00207 int Character::getMapId() const
00208 {
00209 return getMap()->getID();
00210 }
00211
00212 void Character::setMapId(int id)
00213 {
00214 setMap(MapManager::getMap(id));
00215 }
00216
00217 void Character::cancelTransaction()
00218 {
00219 TransactionType t = mTransaction;
00220 mTransaction = TRANS_NONE;
00221 switch (t)
00222 {
00223 case TRANS_TRADE:
00224 static_cast< Trade * >(mTransactionHandler)->cancel();
00225 break;
00226 case TRANS_BUYSELL:
00227 static_cast< BuySell * >(mTransactionHandler)->cancel();
00228 break;
00229 case TRANS_NONE:
00230 return;
00231 }
00232 }
00233
00234 Trade *Character::getTrading() const
00235 {
00236 return mTransaction == TRANS_TRADE
00237 ? static_cast< Trade * >(mTransactionHandler) : NULL;
00238 }
00239
00240 BuySell *Character::getBuySell() const
00241 {
00242 return mTransaction == TRANS_BUYSELL
00243 ? static_cast< BuySell * >(mTransactionHandler) : NULL;
00244 }
00245
00246 void Character::setTrading(Trade *t)
00247 {
00248 if (t)
00249 {
00250 cancelTransaction();
00251 mTransactionHandler = t;
00252 mTransaction = TRANS_TRADE;
00253 }
00254 else
00255 {
00256 assert(mTransaction == TRANS_NONE || mTransaction == TRANS_TRADE);
00257 mTransaction = TRANS_NONE;
00258 }
00259 }
00260
00261 void Character::setBuySell(BuySell *t)
00262 {
00263 if (t)
00264 {
00265 cancelTransaction();
00266 mTransactionHandler = t;
00267 mTransaction = TRANS_BUYSELL;
00268 }
00269 else
00270 {
00271 assert(mTransaction == TRANS_NONE || mTransaction == TRANS_BUYSELL);
00272 mTransaction = TRANS_NONE;
00273 }
00274 }
00275
00276 void Character::sendStatus()
00277 {
00278 MessageOut attribMsg(GPMSG_PLAYER_ATTRIBUTE_CHANGE);
00279 for (std::set<size_t>::const_iterator i = mModifiedAttributes.begin(),
00280 i_end = mModifiedAttributes.end(); i != i_end; ++i)
00281 {
00282 int attr = *i;
00283 attribMsg.writeByte(attr);
00284 attribMsg.writeShort(getAttribute(attr));
00285 attribMsg.writeShort(getModifiedAttribute(attr));
00286 }
00287 if (attribMsg.getLength() > 2) gameHandler->sendTo(this, attribMsg);
00288 mModifiedAttributes.clear();
00289
00290 MessageOut expMsg(GPMSG_PLAYER_EXP_CHANGE);
00291 for (std::set<size_t>::const_iterator i = mModifiedExperience.begin(),
00292 i_end = mModifiedExperience.end(); i != i_end; ++i)
00293 {
00294 int skill = *i;
00295 expMsg.writeByte(skill);
00296 expMsg.writeLong(getExpGot(skill));
00297 expMsg.writeLong(getExpNeeded(skill));
00298 }
00299 if (expMsg.getLength() > 2) gameHandler->sendTo(this, expMsg);
00300 mModifiedExperience.clear();
00301
00302 if (mUpdateLevelProgress)
00303 {
00304 mUpdateLevelProgress = false;
00305 MessageOut progressMessage(GPMSG_LEVEL_PROGRESS);
00306 progressMessage.writeByte(mLevelProgress);
00307 gameHandler->sendTo(this, progressMessage);
00308 }
00309 }
00310
00311 void Character::modifiedAttribute(int attr)
00312 {
00313 if (attr >= CHAR_ATTR_BEGIN && attr < CHAR_ATTR_END)
00314 {
00315 for (int i = BASE_ATTR_BEGIN; i < BASE_ATTR_END; ++i)
00316 {
00317 int newValue = getAttribute(i);
00318
00319 if (i == BASE_ATTR_HP_REGEN){
00320 newValue = (getModifiedAttribute(CHAR_ATTR_VITALITY) + 10)
00321 * (getModifiedAttribute(CHAR_ATTR_VITALITY) + 10)
00322 / (600 / TICKS_PER_HP_REGENERATION);
00323
00324 }
00325 else if (i == BASE_ATTR_HP){
00326 newValue = (getModifiedAttribute(CHAR_ATTR_VITALITY) + 10)
00327 * (mLevel + 10);
00328 }
00329 else if (i == BASE_ATTR_HIT) {
00330 newValue = getModifiedAttribute(CHAR_ATTR_DEXTERITY)
00331 ;
00332 }
00333 else if (i == BASE_ATTR_EVADE) {
00334 newValue = getModifiedAttribute(CHAR_ATTR_AGILITY);
00335
00336 }
00337 else if (i == BASE_ATTR_PHY_RES) {
00338 newValue = getModifiedAttribute(CHAR_ATTR_VITALITY);
00339
00340 }
00341 else if (i == BASE_ATTR_PHY_ATK_MIN) {
00342 newValue = getModifiedAttribute(CHAR_ATTR_STRENGTH);
00343
00344 }
00345 else if (i == BASE_ATTR_PHY_ATK_DELTA) {
00346 newValue = 0;
00347
00348
00349
00350
00351
00352 }
00353 else if (i == BASE_ATTR_MAG_RES) {
00354 newValue = getModifiedAttribute(CHAR_ATTR_WILLPOWER);
00355 }
00356 else if (i == BASE_ATTR_MAG_ATK) {
00357 newValue = getModifiedAttribute(CHAR_ATTR_WILLPOWER);
00358 }
00359
00360 if (newValue != getAttribute(i))
00361 {
00362 setAttribute(i, newValue);
00363 flagAttribute(i);
00364 }
00365 }
00366 }
00367 flagAttribute(attr);
00368 }
00369
00370 void Character::flagAttribute(int attr)
00371 {
00372
00373 mModifiedAttributes.insert(attr);
00374 }
00375
00376 int Character::expForLevel(int level)
00377 {
00378 return int(pow(level, EXPCURVE_EXPONENT) * EXPCURVE_FACTOR);
00379 }
00380
00381 void Character::receiveExperience(size_t skill, int experience)
00382 {
00383 if (skill >= CHAR_SKILL_BEGIN && skill < CHAR_SKILL_END)
00384 {
00385
00386 long int newExp = mExperience.at(skill - CHAR_SKILL_BEGIN) + experience;
00387 if (newExp > INT_MAX) newExp = INT_MAX;
00388 if (newExp < 0) newExp = 0;
00389 mExperience.at(skill - CHAR_SKILL_BEGIN) = newExp;
00390 mModifiedExperience.insert(skill - CHAR_SKILL_BEGIN);
00391
00392
00393 accountHandler->updateExperience(getDatabaseID(),
00394 skill - CHAR_SKILL_BEGIN, newExp);
00395
00396
00397 while (newExp >= Character::expForLevel(getAttribute(skill) + 1))
00398 {
00399 setAttribute(skill, getAttribute(skill) + 1);
00400 modifiedAttribute(skill);
00401 }
00402
00403 mRecalculateLevel = true;
00404 }
00405 }
00406
00407 void Character::recalculateLevel()
00408 {
00409 std::list<float> levels;
00410 for (int a = CHAR_SKILL_BEGIN; a < CHAR_SKILL_END; a++)
00411 {
00412 float expGot = getExpGot(a - CHAR_SKILL_BEGIN);
00413 float expNeed = getExpNeeded(a - CHAR_SKILL_BEGIN);
00414 levels.push_back(getAttribute(a) + expGot / expNeed);
00415 }
00416 levels.sort();
00417
00418 std::list<float>::iterator i = levels.end();
00419 float level = 0.0f;
00420 float factor = 1.0f;
00421 float factorSum = 0.0f;
00422 while (i != levels.begin())
00423 {
00424 i--;
00425 level += *i * factor;
00426 factorSum += factor;
00427 factor *= LEVEL_SKILL_PRECEDENCE_FACTOR;
00428 }
00429 level /= factorSum;
00430 level += 1.0f;
00431
00432 while (mLevel < level)
00433 {
00434 levelup();
00435 }
00436
00437 int levelProgress = int((level - floor(level)) * 100);
00438 if (levelProgress != mLevelProgress)
00439 {
00440 mLevelProgress = levelProgress;
00441 mUpdateLevelProgress = true;
00442 }
00443 }
00444
00445 int Character::getExpNeeded(size_t skill)
00446 {
00447 int level = getAttribute(skill + CHAR_SKILL_BEGIN);
00448 return Character::expForLevel(level + 1) - expForLevel(level);
00449 }
00450
00451 int Character::getExpGot(size_t skill)
00452 {
00453 int level = getAttribute(skill + CHAR_SKILL_BEGIN);
00454 return mExperience.at(skill) - Character::expForLevel(level);
00455 }
00456
00457 void Character::levelup()
00458 {
00459 mLevel++;
00460
00461 mCharacterPoints += CHARPOINTS_PER_LEVELUP;
00462 mCorrectionPoints += CORRECTIONPOINTS_PER_LEVELUP;
00463 if (mCorrectionPoints > CORRECTIONPOINTS_MAX)
00464 mCorrectionPoints = CORRECTIONPOINTS_MAX;
00465
00466 MessageOut levelupMsg(GPMSG_LEVELUP);
00467 levelupMsg.writeShort(mLevel);
00468 levelupMsg.writeShort(mCharacterPoints);
00469 levelupMsg.writeShort(mCorrectionPoints);
00470 gameHandler->sendTo(this, levelupMsg);
00471 LOG_INFO(getName()<<" reached level "<<mLevel);
00472 }
00473
00474 AttribmodResponseCode Character::useCharacterPoint(size_t attribute)
00475 {
00476 if (attribute < CHAR_ATTR_BEGIN) return ATTRIBMOD_INVALID_ATTRIBUTE;
00477 if (attribute >= CHAR_ATTR_END) return ATTRIBMOD_INVALID_ATTRIBUTE;
00478 if (!mCharacterPoints) return ATTRIBMOD_NO_POINTS_LEFT;
00479
00480 mCharacterPoints--;
00481 setAttribute(attribute, getAttribute(attribute) + 1);
00482 modifiedAttribute(attribute);
00483 return ATTRIBMOD_OK;
00484 }
00485
00486 AttribmodResponseCode Character::useCorrectionPoint(size_t attribute)
00487 {
00488 if (attribute < CHAR_ATTR_BEGIN) return ATTRIBMOD_INVALID_ATTRIBUTE;
00489 if (attribute >= CHAR_ATTR_END) return ATTRIBMOD_INVALID_ATTRIBUTE;
00490 if (!mCorrectionPoints) return ATTRIBMOD_NO_POINTS_LEFT;
00491 if (getAttribute(attribute) <= 1) return ATTRIBMOD_DENIED;
00492
00493 mCorrectionPoints--;
00494 mCharacterPoints++;
00495 setAttribute(attribute, getAttribute(attribute) - 1);
00496 modifiedAttribute(attribute);
00497 return ATTRIBMOD_OK;
00498 }
00499
00500 void Character::disconnected()
00501 {
00502 for (Listeners::iterator i = mListeners.begin(),
00503 i_end = mListeners.end(); i != i_end;)
00504 {
00505 const EventListener &l = **i;
00506 ++i;
00507 if (l.dispatch->disconnected) l.dispatch->disconnected(&l, this);
00508 }
00509 }
00510
00511 Character::~Character()
00512 {
00513 if (getMap())
00514 {
00515 Point oldP = getPosition();
00516 getMap()->getMap()->freeTile(oldP.x / 32, oldP.y / 32, getBlockType());
00517 }
00518 }
00519
00520 void Character::giveSpecial(int id)
00521 {
00522 if (mSpecials.find(id) == mSpecials.end())
00523 {
00524
00525 int neededMana;
00526 if (id == 1) neededMana = 10;
00527 if (id == 2) neededMana = 100;
00528 if (id == 3) neededMana = 1000;
00529
00530 Special *s = new Special(neededMana);
00531 mSpecials[id] = s;
00532 }
00533 }