00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "localplayer.h"
00023
00024 #include "configuration.h"
00025 #include "equipment.h"
00026 #include "floor_item.h"
00027 #include "game.h"
00028 #include "graphics.h"
00029 #include "inventory.h"
00030 #include "item.h"
00031 #include "map.h"
00032 #include "monster.h"
00033 #include "particle.h"
00034 #include "simpleanimation.h"
00035 #include "sound.h"
00036 #include "statuseffect.h"
00037 #include "text.h"
00038
00039 #include "gui/gui.h"
00040 #include "gui/ministatus.h"
00041 #include "gui/palette.h"
00042 #ifdef EATHENA_SUPPORT
00043 #include "gui/storagewindow.h"
00044 #endif
00045
00046 #include "net/inventoryhandler.h"
00047 #include "net/net.h"
00048 #include "net/partyhandler.h"
00049 #include "net/playerhandler.h"
00050 #include "net/tradehandler.h"
00051
00052 #ifdef TMWSERV_SUPPORT
00053 #include "effectmanager.h"
00054 #include "guild.h"
00055
00056 #include "net/tmwserv/gameserver/player.h"
00057 #include "net/tmwserv/chatserver/guild.h"
00058 #else
00059 #include "net/ea/partyhandler.h"
00060 #include "net/ea/skillhandler.h"
00061 #endif
00062
00063 #include "resources/animation.h"
00064 #include "resources/imageset.h"
00065 #include "resources/iteminfo.h"
00066 #include "resources/resourcemanager.h"
00067
00068 #include "utils/gettext.h"
00069 #include "utils/stringutils.h"
00070
00071 #include <cassert>
00072
00073 #ifdef TMWSERV_SUPPORT
00074 const short walkingKeyboardDelay = 100;
00075 #endif
00076
00077 LocalPlayer *player_node = NULL;
00078
00079 #ifdef TMWSERV_SUPPORT
00080 LocalPlayer::LocalPlayer():
00081 Player(65535, 0, NULL),
00082 mEquipment(new Equipment),
00083 #else
00084 LocalPlayer::LocalPlayer(int id, int job, Map *map):
00085 Player(id, job, map),
00086 mCharId(0),
00087 mJobXp(0),
00088 mJobLevel(0),
00089 mXpForNextLevel(0), mJobXpForNextLevel(0),
00090 mMp(0), mMaxMp(0),
00091 mAttackRange(0),
00092 ATK(0), MATK(0), DEF(0), MDEF(0), HIT(0), FLEE(0),
00093 ATK_BONUS(0), MATK_BONUS(0), DEF_BONUS(0), MDEF_BONUS(0), FLEE_BONUS(0),
00094 mStatPoint(0), mSkillPoint(0),
00095 mStatsPointsToAttribute(0),
00096 mEquipment(new Equipment),
00097 #endif
00098 mInStorage(false),
00099 #ifdef EATHENA_SUPPORT
00100 mXp(0),
00101 mTargetTime(-1),
00102 #endif
00103 mLastTarget(-1),
00104 #ifdef TMWSERV_SUPPORT
00105 mAttributeBase(NB_CHARACTER_ATTRIBUTES, -1),
00106 mAttributeEffective(NB_CHARACTER_ATTRIBUTES, -1),
00107 mExpCurrent(CHAR_SKILL_NB, -1),
00108 mExpNext(CHAR_SKILL_NB, -1),
00109 mCharacterPoints(-1),
00110 mCorrectionPoints(-1),
00111 mLevelProgress(0),
00112 #endif
00113 mLevel(1),
00114 mMoney(0),
00115 mTotalWeight(1), mMaxWeight(1),
00116 mHp(1), mMaxHp(1),
00117 mTarget(NULL), mPickUpTarget(NULL),
00118 mTrading(false), mGoingToTarget(false),
00119 mLastAction(-1),
00120 mWalkingDir(0),
00121 mDestX(0), mDestY(0),
00122 mInventory(new Inventory(INVENTORY_SIZE)),
00123 #ifdef TMWSERV_SUPPORT
00124 mLocalWalkTime(-1),
00125 #endif
00126 mStorage(new Inventory(STORAGE_SIZE))
00127 #ifdef TMWSERV_SUPPORT
00128 , mExpMessageTime(0)
00129 #endif
00130 {
00131
00132
00133
00134 mMapInitialized = false;
00135
00136 mUpdateName = true;
00137
00138 initTargetCursor();
00139 }
00140
00141 LocalPlayer::~LocalPlayer()
00142 {
00143 delete mInventory;
00144 #ifdef EATHENA_SUPPORT
00145 delete mStorage;
00146 #endif
00147
00148 for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++)
00149 {
00150 delete mTargetCursor[0][i];
00151 delete mTargetCursor[1][i];
00152 mTargetCursorImages[0][i]->decRef();
00153 mTargetCursorImages[1][i]->decRef();
00154 }
00155 }
00156
00157 void LocalPlayer::logic()
00158 {
00159
00160 if (get_elapsed_time(mLastAction) >= 1000)
00161 mLastAction = -1;
00162
00163 #ifdef TMWSERV_SUPPORT
00164
00165 if (!mExpMessages.empty())
00166 {
00167 if (mExpMessageTime == 0)
00168 {
00169 const Vector &pos = getPosition();
00170
00171 particleEngine->addTextRiseFadeOutEffect(
00172 mExpMessages.front(),
00173 (int) pos.x + 16,
00174 (int) pos.y - 16,
00175 &guiPalette->getColor(Palette::EXP_INFO),
00176 gui->getInfoParticleFont(), true);
00177
00178 mExpMessages.pop_front();
00179 mExpMessageTime = 30;
00180 }
00181 mExpMessageTime--;
00182 }
00183 #else
00184
00185 if (get_elapsed_time(mLastTarget) >= 250)
00186 mLastTarget = -1;
00187
00188
00189 if (get_elapsed_time(mTargetTime) >= 60000)
00190 {
00191 mTargetTime = -1;
00192 setTarget(NULL);
00193 mLastTarget = -1;
00194 }
00195
00196 if (mTarget)
00197 {
00198 if (mTarget->getType() == Being::NPC)
00199 {
00200
00201 mTarget->setTargetAnimation(
00202 mTargetCursor[0][mTarget->getTargetCursorSize()]);
00203 }
00204 else
00205 {
00206
00207 const int rangeX = abs(mTarget->mX - mX);
00208 const int rangeY = abs(mTarget->mY - mY);
00209 const int attackRange = getAttackRange();
00210 const int inRange = rangeX > attackRange || rangeY > attackRange
00211 ? 1 : 0;
00212
00213 mTarget->setTargetAnimation(
00214 mTargetCursor[inRange][mTarget->getTargetCursorSize()]);
00215
00216 if (mTarget->mAction == DEAD)
00217 stopAttack();
00218
00219 if (mKeepAttacking && mTarget)
00220 attack(mTarget, true);
00221 }
00222 }
00223 #endif
00224
00225 Player::logic();
00226 }
00227
00228 void LocalPlayer::setAction(Action action, int attackType)
00229 {
00230 if (action == DEAD)
00231 {
00232 mLastTarget = -1;
00233 setTarget(NULL);
00234 }
00235
00236 Player::setAction(action, attackType);
00237 }
00238
00239 void LocalPlayer::setGM()
00240 {
00241 mIsGM = !mIsGM;
00242 mNameColor = mIsGM ?
00243 &guiPalette->getColor(Palette::GM) :
00244 &guiPalette->getColor(Palette::PLAYER);
00245 setName(getName());
00246 config.setValue(getName() + "GMassert", mIsGM);
00247 }
00248
00249 void LocalPlayer::setName(const std::string &name)
00250 {
00251 if (mName)
00252 {
00253 delete mName;
00254 mName = 0;
00255 }
00256
00257 if (config.getValue("showownname", false) && mMapInitialized)
00258 Player::setName(name);
00259 else
00260 Being::setName(name);
00261 }
00262
00263 void LocalPlayer::nextStep()
00264 {
00265
00266
00267 if (mPath.empty())
00268 {
00269 if (mPickUpTarget)
00270 pickUp(mPickUpTarget);
00271
00272 if (mWalkingDir)
00273 walk(mWalkingDir);
00274 }
00275
00276
00277 if (mGoingToTarget && mTarget && withinAttackRange(mTarget))
00278 {
00279 mAction = Being::STAND;
00280 #ifdef EATHENA_SUPPORT
00281 attack(mTarget, true);
00282 #endif
00283 mGoingToTarget = false;
00284 mPath.clear();
00285 return;
00286 }
00287 else if (mGoingToTarget && !mTarget)
00288 {
00289 mGoingToTarget = false;
00290 mPath.clear();
00291 }
00292
00293 #ifdef EATHENA_SUPPORT
00294 Player::nextStep();
00295 #endif
00296 }
00297
00298 #ifdef TMWSERV_SUPPORT
00299 bool LocalPlayer::checkInviteRights(const std::string &guildName)
00300 {
00301 Guild *guild = getGuild(guildName);
00302 if (guild)
00303 {
00304 return guild->getInviteRights();
00305 }
00306
00307 return false;
00308 }
00309
00310 void LocalPlayer::inviteToGuild(Being *being)
00311 {
00312
00313
00314 std::map<int, Guild*>::iterator itr = mGuilds.begin();
00315 std::map<int, Guild*>::iterator itr_end = mGuilds.end();
00316 for (; itr != itr_end; ++itr)
00317 {
00318 if (checkInviteRights(itr->second->getName()))
00319 {
00320 Net::ChatServer::Guild::invitePlayer(being->getName(), itr->second->getId());
00321 return;
00322 }
00323 }
00324 }
00325
00326 void LocalPlayer::clearInventory()
00327 {
00328 mEquipment->clear();
00329 mInventory->clear();
00330 }
00331
00332 void LocalPlayer::setInvItem(int index, int id, int amount)
00333 {
00334 mInventory->setItem(index, id, amount);
00335 }
00336
00337 #endif
00338
00339 void LocalPlayer::inviteToParty(const std::string &name)
00340 {
00341 Net::getPartyHandler()->invite(name);
00342 }
00343
00344 void LocalPlayer::inviteToParty(Player *player)
00345 {
00346 Net::getPartyHandler()->invite(player);
00347 }
00348
00349 void LocalPlayer::moveInvItem(Item *item, int newIndex)
00350 {
00351
00352 if (item->getInvIndex() == newIndex)
00353 return;
00354
00355 #ifdef TMWSERV_SUPPORT
00356 Net::GameServer::Player::moveItem(
00357 item->getInvIndex(), newIndex, item->getQuantity());
00358 #endif
00359
00360 }
00361
00362 void LocalPlayer::equipItem(Item *item)
00363 {
00364 Net::getInventoryHandler()->equipItem(item);
00365 }
00366
00367 void LocalPlayer::unequipItem(Item *item)
00368 {
00369 Net::getInventoryHandler()->unequipItem(item);
00370
00371
00372 #ifdef TMWSERV_SUPPORT
00373 mEquipment->setEquipment(item->getInvIndex(), 0);
00374 #else
00375 mEquipment->removeEquipment(item->getInvIndex());
00376 #endif
00377 }
00378
00379 void LocalPlayer::useItem(Item *item)
00380 {
00381 Net::getInventoryHandler()->useItem(item);
00382 }
00383
00384 void LocalPlayer::dropItem(Item *item, int quantity)
00385 {
00386 Net::getInventoryHandler()->dropItem(item, quantity);
00387 }
00388
00389 #ifdef TMWSERV_SUPPORT
00390 void LocalPlayer::splitItem(Item *item, int quantity)
00391 {
00392 int newIndex = mInventory->getFreeSlot();
00393 if (newIndex > Inventory::NO_SLOT_INDEX)
00394 {
00395 Net::GameServer::Player::moveItem(
00396 item->getInvIndex(), newIndex, quantity);
00397 }
00398 }
00399 #endif
00400
00401 void LocalPlayer::pickUp(FloorItem *item)
00402 {
00403 #ifdef TMWSERV_SUPPORT
00404 int dx = item->getX() - (int) getPosition().x / 32;
00405 int dy = item->getY() - (int) getPosition().y / 32;
00406 #else
00407 int dx = item->getX() - mX;
00408 int dy = item->getY() - mY;
00409 #endif
00410
00411 if (dx * dx + dy * dy < 4)
00412 {
00413 Net::getPlayerHandler()->pickUp(item);
00414 mPickUpTarget = NULL;
00415 }
00416 else
00417 {
00418 #ifdef TMWSERV_SUPPORT
00419 setDestination(item->getX() * 32 + 16, item->getY() * 32 + 16);
00420 #else
00421 setDestination(item->getX(), item->getY());
00422 #endif
00423 mPickUpTarget = item;
00424 #ifdef EATHENA_SUPPORT
00425 stopAttack();
00426 #endif
00427 }
00428 }
00429
00430 void LocalPlayer::walk(unsigned char dir)
00431 {
00432
00433 if (!mMap || !dir)
00434 return;
00435
00436 #ifdef TMWSERV_SUPPORT
00437 const Vector &pos = getPosition();
00438 #endif
00439
00440 if (mAction == WALK && !mPath.empty())
00441 {
00442
00443 #ifdef TMWSERV_SUPPORT
00444 Being::setDestination(pos.x, pos.y);
00445 #else
00446 Being::setDestination(mX, mY);
00447 #endif
00448 return;
00449 }
00450
00451 int dx = 0, dy = 0;
00452 #ifdef TMWSERV_SUPPORT
00453 if (dir & UP)
00454 dy -= 32;
00455 if (dir & DOWN)
00456 dy += 32;
00457 if (dir & LEFT)
00458 dx -= 32;
00459 if (dir & RIGHT)
00460 dx += 32;
00461 #else
00462 if (dir & UP)
00463 dy--;
00464 if (dir & DOWN)
00465 dy++;
00466 if (dir & LEFT)
00467 dx--;
00468 if (dir & RIGHT)
00469 dx++;
00470 #endif
00471
00472
00473 #ifdef TMWSERV_SUPPORT
00474 if (dx && !mMap->getWalk(((int) pos.x + dx) / 32,
00475 (int) pos.y / 32, getWalkMask()))
00476 dx = 16 - (int) pos.x % 32;
00477 if (dy && !mMap->getWalk((int) pos.x / 32,
00478 ((int) pos.y + dy) / 32, getWalkMask()))
00479 dy = 16 - (int) pos.y % 32;
00480 #else
00481 if (dx && !mMap->getWalk(mX + dx, mY, getWalkMask()))
00482 dx = 0;
00483 if (dy && !mMap->getWalk(mX, mY + dy, getWalkMask()))
00484 dy = 0;
00485 #endif
00486
00487
00488 #ifdef TMWSERV_SUPPORT
00489 if (dx && dy && !mMap->getWalk((pos.x + dx) / 32,
00490 (pos.y + dy) / 32, getWalkMask()))
00491 dx = 16 - (int) pos.x % 32;
00492
00493 int dScaler;
00494
00495
00496
00497 for (dScaler = 1; dScaler <= 10; dScaler++)
00498 {
00499 if ( (dx || dy) &&
00500 !mMap->getWalk( ((int) pos.x + (dx * dScaler)) / 32,
00501 ((int) pos.y + (dy * dScaler)) / 32, getWalkMask()) )
00502 {
00503 dScaler--;
00504 break;
00505 }
00506 }
00507
00508 if (dScaler >= 0)
00509 {
00510 setDestination((int) pos.x + (dx * dScaler), (int) pos.y + (dy * dScaler));
00511 }
00512 #else
00513 if (dx && dy && !mMap->getWalk(mX + dx, mY + dy, getWalkMask()))
00514 dx = 0;
00515
00516
00517 if ((dx || dy) && mMap->getWalk(mX + dx, mY + dy, getWalkMask()))
00518 {
00519 setDestination(mX + dx, mY + dy);
00520 }
00521 #endif
00522 else if (dir)
00523 {
00524
00525 Net::getPlayerHandler()->setDirection(dir);
00526 setDirection(dir);
00527 }
00528 }
00529
00530 Being* LocalPlayer::getTarget() const
00531 {
00532 return mTarget;
00533 }
00534
00535 void LocalPlayer::setTarget(Being *target)
00536 {
00537 #ifdef EATHENA_SUPPORT
00538 if (mLastTarget != -1 || target == this)
00539 return;
00540
00541 mLastTarget = tick_time;
00542
00543 if (target == mTarget)
00544 target = NULL;
00545
00546 if (target || mAction == ATTACK)
00547 {
00548 mTargetTime = tick_time;
00549 }
00550 else
00551 {
00552 mKeepAttacking = false;
00553 mTargetTime = -1;
00554 }
00555 #endif
00556
00557 if (mTarget)
00558 mTarget->untarget();
00559
00560 if (mTarget && mTarget->getType() == Being::MONSTER)
00561 static_cast<Monster *>(mTarget)->setShowName(false);
00562
00563 mTarget = target;
00564
00565 if (target && target->getType() == Being::MONSTER)
00566 static_cast<Monster *>(target)->setShowName(true);
00567 }
00568
00569 #ifdef TMWSERV_SUPPORT
00570 void LocalPlayer::setDestination(int x, int y)
00571 #else
00572 void LocalPlayer::setDestination(Uint16 x, Uint16 y)
00573 #endif
00574 {
00575 #ifdef TMWSERV_SUPPORT
00576
00577 const int tx = x / 32;
00578 const int ty = y / 32;
00579 int fx = x % 32;
00580 int fy = y % 32;
00581
00582 if (fx != 16 && !mMap->getWalk(tx + fx / 16 * 2 - 1, ty, getWalkMask()))
00583 fx = 16;
00584 if (fy != 16 && !mMap->getWalk(tx, ty + fy / 16 * 2 - 1, getWalkMask()))
00585 fy = 16;
00586 if (fx != 16 && fy != 16 && !mMap->getWalk(tx + fx / 16 * 2 - 1,
00587 ty + fy / 16 * 2 - 1,
00588 getWalkMask()))
00589 fx = 16;
00590
00591 x = tx * 32 + fx;
00592 y = ty * 32 + fy;
00593 #endif
00594
00595
00596 if (x != mDestX || y != mDestY)
00597 {
00598 mDestX = x;
00599 mDestY = y;
00600
00601 Net::getPlayerHandler()->setDestination(x, y, mDirection);
00602 }
00603
00604 mPickUpTarget = NULL;
00605 Being::setDestination(x, y);
00606 }
00607
00608 void LocalPlayer::setWalkingDir(int dir)
00609 {
00610 mWalkingDir = dir;
00611
00612
00613 if (mAction != WALK && dir
00614 #ifdef TMWSERV_SUPPORT
00615 && get_elapsed_time(mLocalWalkTime) >= walkingKeyboardDelay
00616 #endif
00617 )
00618 {
00619 walk(dir);
00620 }
00621 }
00622
00623 #ifdef TMWSERV_SUPPORT
00624 void LocalPlayer::stopWalking(bool sendToServer)
00625 {
00626 if (mAction == WALK && mWalkingDir) {
00627 mWalkingDir = 0;
00628 mLocalWalkTime = 0;
00629 Being::setDestination(getPosition().x,getPosition().y);
00630 if (sendToServer)
00631 Net::GameServer::Player::walk(getPosition().x, getPosition().y);
00632 setAction(STAND);
00633 }
00634
00635 clearPath();
00636 }
00637 #endif
00638
00639 #ifdef EATHENA_SUPPORT
00640 void LocalPlayer::raiseSkill(Uint16 skillId)
00641 {
00642 if (mSkillPoint <= 0)
00643 return;
00644
00645 Net::getSkillHandler()->up(skillId);
00646 }
00647 #endif
00648
00649 void LocalPlayer::toggleSit()
00650 {
00651 if (mLastAction != -1)
00652 return;
00653 mLastAction = tick_time;
00654
00655 Being::Action newAction;
00656 switch (mAction)
00657 {
00658 case STAND: newAction = SIT; break;
00659 case SIT: newAction = STAND; break;
00660 default: return;
00661 }
00662
00663 Net::getPlayerHandler()->changeAction(newAction);
00664 }
00665
00666 void LocalPlayer::emote(Uint8 emotion)
00667 {
00668 if (mLastAction != -1)
00669 return;
00670 mLastAction = tick_time;
00671
00672 Net::getPlayerHandler()->emote(emotion);
00673 }
00674
00675 void LocalPlayer::tradeReply(bool accept)
00676 {
00677 if (!accept)
00678 mTrading = false;
00679
00680 Net::getTradeHandler()->respond(accept);
00681 }
00682
00683 void LocalPlayer::trade(Being *being) const
00684 {
00685 Net::getTradeHandler()->request(being);
00686 }
00687
00688 bool LocalPlayer::tradeRequestOk() const
00689 {
00690 return !mTrading;
00691 }
00692
00693 #ifdef TMWSERV_SUPPORT
00694
00695 void LocalPlayer::attack()
00696 {
00697 if (mLastAction != -1)
00698 return;
00699
00700
00701 if (mAction != STAND && mAction != ATTACK)
00702 return;
00703
00704
00705 if(mTarget){
00706 unsigned char dir = 0;
00707 int x = 0, y = 0;
00708 Vector plaPos = this->getPosition();
00709 Vector tarPos = mTarget->getPosition();
00710 x = plaPos.x - tarPos.x;
00711 y = plaPos.y - tarPos.y;
00712 if(abs(x) < abs(y)){
00713
00714 if(y > 0){
00715 dir = UP;
00716 } else {
00717 dir = DOWN;
00718 }
00719 } else {
00720
00721 if(x > 0){
00722 dir = LEFT;
00723 } else {
00724 dir = RIGHT;
00725 }
00726 }
00727 setDirection(dir);
00728 }
00729
00730 mLastAction = tick_time;
00731
00732 setAction(ATTACK);
00733
00734 if (mEquippedWeapon)
00735 {
00736 std::string soundFile = mEquippedWeapon->getSound(EQUIP_EVENT_STRIKE);
00737 if (soundFile != "") sound.playSfx(soundFile);
00738 }
00739 else {
00740 sound.playSfx("sfx/fist-swish.ogg");
00741 }
00742 Net::GameServer::Player::attack(getSpriteDirection());
00743 }
00744
00745 void LocalPlayer::useSpecial(int special)
00746 {
00747 Net::GameServer::Player::useSpecial(special);
00748 }
00749
00750 #else
00751
00752 void LocalPlayer::attack(Being *target, bool keep)
00753 {
00754 mKeepAttacking = keep;
00755
00756 if (!target || target->getType() == Being::NPC)
00757 return;
00758
00759 if ((mTarget != target) || !mTarget)
00760 {
00761 mLastTarget = -1;
00762 setTarget(target);
00763 }
00764
00765 int dist_x = target->mX - mX;
00766 int dist_y = target->mY - mY;
00767
00768
00769 if (mAction != STAND) return;
00770
00771 if (abs(dist_y) >= abs(dist_x))
00772 {
00773 if (dist_y > 0)
00774 setDirection(DOWN);
00775 else
00776 setDirection(UP);
00777 }
00778 else
00779 {
00780 if (dist_x > 0)
00781 setDirection(RIGHT);
00782 else
00783 setDirection(LEFT);
00784 }
00785
00786 mWalkTime = tick_time;
00787 mTargetTime = tick_time;
00788
00789 setAction(ATTACK);
00790
00791 if (mEquippedWeapon)
00792 {
00793 std::string soundFile = mEquippedWeapon->getSound(EQUIP_EVENT_STRIKE);
00794 if (!soundFile.empty())
00795 sound.playSfx(soundFile);
00796 }
00797 else
00798 {
00799 sound.playSfx("sfx/fist-swish.ogg");
00800 }
00801
00802 Net::getPlayerHandler()->attack(target);
00803
00804 if (!keep)
00805 stopAttack();
00806 }
00807
00808 #endif // no TMWSERV_SUPPORT
00809
00810 void LocalPlayer::stopAttack()
00811 {
00812 if (mTarget)
00813 {
00814 setAction(STAND);
00815 mLastTarget = -1;
00816 }
00817 setTarget(NULL);
00818 mLastTarget = -1;
00819 }
00820
00821 void LocalPlayer::revive()
00822 {
00823 Net::getPlayerHandler()->respawn();
00824 }
00825
00826 #ifdef TMWSERV_SUPPORT
00827
00828 void LocalPlayer::raiseAttribute(size_t attr)
00829 {
00830
00831 mCharacterPoints--;
00832 mAttributeBase.at(attr)++;
00833 Net::GameServer::Player::raiseAttribute(attr + CHAR_ATTR_BEGIN);
00834 }
00835
00836 void LocalPlayer::lowerAttribute(size_t attr)
00837 {
00838
00839 mCorrectionPoints--;
00840 mCharacterPoints++;
00841 mAttributeBase.at(attr)--;
00842 Net::GameServer::Player::lowerAttribute(attr + CHAR_ATTR_BEGIN);
00843 }
00844
00845 const struct LocalPlayer::SkillInfo& LocalPlayer::getSkillInfo(int skill)
00846 {
00847 static const SkillInfo skills[CHAR_SKILL_NB + 1] =
00848 {
00849 { _("Unarmed"), "graphics/images/unarmed.png" },
00850 { _("Knife"), "graphics/images/knife.png" },
00851 { _("Sword"), "graphics/images/sword.png" },
00852 { _("Polearm"), "graphics/images/polearm.png" },
00853 { _("Staff"), "graphics/images/staff.png" },
00854 { _("Whip"), "graphics/images/whip.png" },
00855 { _("Bow"), "graphics/images/bow.png" },
00856 { _("Shooting"), "graphics/images/shooting.png" },
00857 { _("Mace"), "graphics/images/mace.png" },
00858 { _("Axe"), "graphics/images/axe.png" },
00859 { _("Thrown"), "graphics/images/thrown.png" },
00860 { _("Magic"), "graphics/images/magic.png" },
00861 { _("Craft"), "graphics/images/craft.png" },
00862 { _("Unknown Skill"), "graphics/images/unknown.png" }
00863 };
00864
00865 if ((skill < 0) || (skill > CHAR_SKILL_NB))
00866 {
00867 return skills[CHAR_SKILL_NB];
00868 }
00869 else
00870 {
00871 return skills[skill];
00872 }
00873 }
00874
00875 void LocalPlayer::setExperience(int skill, int current, int next)
00876 {
00877 int diff = current - mExpCurrent.at(skill);
00878 if (mMap && mExpCurrent.at(skill) != -1 && diff > 0)
00879 {
00880 const std::string text = toString(diff) + " " + getSkillInfo(skill).name + " xp";
00881 mExpMessages.push_back(text);
00882 }
00883
00884 mExpCurrent.at(skill) = current;
00885 mExpNext.at(skill) = next;
00886 }
00887
00888 std::pair<int, int> LocalPlayer::getExperience(int skill)
00889 {
00890 return std::pair<int, int> (mExpCurrent.at(skill), mExpNext.at(skill));
00891 }
00892
00893 #else
00894
00895 void LocalPlayer::setXp(int xp)
00896 {
00897 if (mMap && xp > mXp)
00898 {
00899 const std::string text = toString(xp - mXp) + " xp";
00900
00901
00902 particleEngine->addTextRiseFadeOutEffect(
00903 text,
00904 getPixelX() + 16,
00905 getPixelY() - 16,
00906 &guiPalette->getColor(Palette::EXP_INFO),
00907 gui->getInfoParticleFont(), true);
00908 }
00909 mXp = xp;
00910 }
00911
00912 #endif
00913
00914 void LocalPlayer::pickedUp(const std::string &item)
00915 {
00916 if (mMap)
00917 {
00918
00919 particleEngine->addTextRiseFadeOutEffect(
00920 item,
00921 getPixelX() + 16,
00922 getPixelY() - 16,
00923 &guiPalette->getColor(Palette::PICKUP_INFO),
00924 gui->getInfoParticleFont(), true);
00925 }
00926 }
00927
00928 int LocalPlayer::getAttackRange()
00929 {
00930 #ifdef TMWSERV_SUPPORT
00931 Item *weapon = mEquipment->getEquipment(EQUIP_FIGHT1_SLOT);
00932 if (weapon)
00933 {
00934 const ItemInfo info = weapon->getInfo();
00935 return info.getAttackRange();
00936 }
00937 return 32;
00938 #else
00939 return mAttackRange;
00940 #endif
00941 }
00942
00943 bool LocalPlayer::withinAttackRange(Being *target)
00944 {
00945 #ifdef TMWSERV_SUPPORT
00946 const Vector &targetPos = target->getPosition();
00947 const Vector &pos = getPosition();
00948 const int dx = abs(targetPos.x - pos.x);
00949 const int dy = abs(targetPos.y - pos.y);
00950 const int range = getAttackRange();
00951
00952 return !(dx > range || dy > range);
00953 #else
00954 int dist_x = abs(target->mX - mX);
00955 int dist_y = abs(target->mY - mY);
00956
00957 if (dist_x > getAttackRange() || dist_y > getAttackRange())
00958 {
00959 return false;
00960 }
00961
00962 return true;
00963 #endif
00964 }
00965
00966 void LocalPlayer::setGotoTarget(Being *target)
00967 {
00968 mLastTarget = -1;
00969 #ifdef TMWSERV_SUPPORT
00970 mTarget = target;
00971 mGoingToTarget = true;
00972 const Vector &targetPos = target->getPosition();
00973 setDestination(targetPos.x, targetPos.y);
00974 #else
00975 setTarget(target);
00976 mGoingToTarget = true;
00977 setDestination(target->mX, target->mY);
00978 #endif
00979 }
00980
00981 extern MiniStatusWindow *miniStatusWindow;
00982
00983 void LocalPlayer::handleStatusEffect(StatusEffect *effect, int effectId)
00984 {
00985 Being::handleStatusEffect(effect, effectId);
00986
00987 if (effect)
00988 {
00989 effect->deliverMessage();
00990 effect->playSFX();
00991
00992 AnimatedSprite *sprite = effect->getIcon();
00993
00994 if (!sprite) {
00995
00996 for (unsigned int i = 0; i < mStatusEffectIcons.size();)
00997 if (mStatusEffectIcons[i] == effectId) {
00998 mStatusEffectIcons.erase(mStatusEffectIcons.begin() + i);
00999 miniStatusWindow->eraseIcon(i);
01000 } else i++;
01001 } else {
01002
01003 bool found = false;
01004
01005 for (unsigned int i = 0; i < mStatusEffectIcons.size(); i++)
01006 if (mStatusEffectIcons[i] == effectId) {
01007 miniStatusWindow->setIcon(i, sprite);
01008 found = true;
01009 break;
01010 }
01011
01012 if (!found) {
01013 int offset = mStatusEffectIcons.size();
01014 miniStatusWindow->setIcon(offset, sprite);
01015 mStatusEffectIcons.push_back(effectId);
01016 }
01017 }
01018 }
01019 }
01020
01021 void LocalPlayer::initTargetCursor()
01022 {
01023
01024 loadTargetCursor("graphics/gui/target-cursor-blue-s.png", 44, 35,
01025 false, TC_SMALL);
01026 loadTargetCursor("graphics/gui/target-cursor-red-s.png", 44, 35,
01027 true, TC_SMALL);
01028 loadTargetCursor("graphics/gui/target-cursor-blue-m.png", 62, 44,
01029 false, TC_MEDIUM);
01030 loadTargetCursor("graphics/gui/target-cursor-red-m.png", 62, 44,
01031 true, TC_MEDIUM);
01032 loadTargetCursor("graphics/gui/target-cursor-blue-l.png", 82, 60,
01033 false, TC_LARGE);
01034 loadTargetCursor("graphics/gui/target-cursor-red-l.png", 82, 60,
01035 true, TC_LARGE);
01036 }
01037
01038 void LocalPlayer::loadTargetCursor(std::string filename, int width, int height,
01039 bool outRange, TargetCursorSize size)
01040 {
01041 assert(size > -1);
01042 assert(size < 3);
01043
01044 ResourceManager *resman = ResourceManager::getInstance();
01045
01046 ImageSet *currentImageSet = resman->getImageSet(filename, width, height);
01047 Animation *anim = new Animation;
01048
01049 for (unsigned int i = 0; i < currentImageSet->size(); ++i)
01050 {
01051 anim->addFrame(currentImageSet->get(i), 75,
01052 (16 - (currentImageSet->getWidth() / 2)),
01053 (16 - (currentImageSet->getHeight() / 2)));
01054 }
01055
01056 SimpleAnimation *currentCursor = new SimpleAnimation(anim);
01057
01058 const int index = outRange ? 1 : 0;
01059
01060 mTargetCursorImages[index][size] = currentImageSet;
01061 mTargetCursor[index][size] = currentCursor;
01062 }
01063
01064 #ifdef EATHENA_SUPPORT
01065 void LocalPlayer::setInStorage(bool inStorage)
01066 {
01067 mInStorage = inStorage;
01068
01069 storageWindow->setVisible(inStorage);
01070 }
01071 #endif