summaryrefslogtreecommitdiff
path: root/src/localplayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/localplayer.cpp')
-rw-r--r--src/localplayer.cpp641
1 files changed, 588 insertions, 53 deletions
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index f72aa59a..4dd8f05f 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -1,9 +1,8 @@
/*
- * Aethyra
+ * The Mana World
* Copyright (C) 2004 The Mana World Development Team
*
- * This file is part of Aethyra based on original code
- * from The Mana World.
+ * This file is part of The Mana World.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,50 +33,95 @@
#include "particle.h"
#include "simpleanimation.h"
#include "sound.h"
+#include "statuseffect.h"
#include "text.h"
#include "gui/gui.h"
+#include "gui/ministatus.h"
#include "gui/palette.h"
+#ifdef EATHENA_SUPPORT
#include "gui/storagewindow.h"
+#endif
+#ifdef TMWSERV_SUPPORT
+#include "effectmanager.h"
+#include "guild.h"
+
+#include "net/tmwserv/gameserver/player.h"
+#include "net/tmwserv/chatserver/guild.h"
+#include "net/tmwserv/chatserver/party.h"
+#endif
+
+#ifdef EATHENA_SUPPORT
#include "net/messageout.h"
-#include "net/protocol.h"
+#include "net/ea/protocol.h"
+#endif
#include "resources/animation.h"
#include "resources/imageset.h"
+#include "resources/iteminfo.h"
#include "resources/resourcemanager.h"
+#include "utils/gettext.h"
#include "utils/stringutils.h"
+#ifdef TMWSERV_SUPPORT
+const short walkingKeyboardDelay = 100;
+#endif
+
LocalPlayer *player_node = NULL;
static const int NAME_X_OFFSET = 15;
static const int NAME_Y_OFFSET = 30;
+#ifdef TMWSERV_SUPPORT
+LocalPlayer::LocalPlayer():
+ Player(65535, 0, NULL),
+ mEquipment(new Equipment),
+ mAttributeBase(NB_CHARACTER_ATTRIBUTES, -1),
+ mAttributeEffective(NB_CHARACTER_ATTRIBUTES, -1),
+ mExpCurrent(CHAR_SKILL_NB, -1),
+ mExpNext(CHAR_SKILL_NB, -1),
+ mCharacterPoints(-1),
+ mCorrectionPoints(-1),
+ mLevelProgress(0),
+#else
LocalPlayer::LocalPlayer(Uint32 id, Uint16 job, Map *map):
Player(id, job, map),
mCharId(0),
mJobXp(0),
- mLevel(0),
mJobLevel(0),
mXpForNextLevel(0), mJobXpForNextLevel(0),
- mHp(0), mMaxHp(0), mMp(0), mMaxMp(0),
- mGp(0),
+ mMp(0), mMaxMp(0),
mAttackRange(0),
- mTotalWeight(0), mMaxWeight(0),
ATK(0), MATK(0), DEF(0), MDEF(0), HIT(0), FLEE(0),
ATK_BONUS(0), MATK_BONUS(0), DEF_BONUS(0), MDEF_BONUS(0), FLEE_BONUS(0),
mStatPoint(0), mSkillPoint(0),
mStatsPointsToAttribute(0),
- mEquipment(new Equipment()),
- mXp(0), mNetwork(0),
+ mEquipment(new Equipment),
+ mNetwork(0),
+ mXp(0),
+ mInStorage(false),
+ mTargetTime(-1),
+ mLastTarget(-1),
+#endif
+ mLevel(1),
+ mMoney(0),
+ mTotalWeight(1), mMaxWeight(1),
+ mHp(1), mMaxHp(1),
mTarget(NULL), mPickUpTarget(NULL),
mTrading(false), mGoingToTarget(false),
- mTargetTime(-1), mLastAction(-1),
- mLastTarget(-1), mWalkingDir(0),
+ mLastAction(-1),
+ mWalkingDir(0),
mDestX(0), mDestY(0),
+#ifdef TMWSERV_SUPPORT
+ mLocalWalkTime(-1),
+ mInventory(new Inventory(INVENTORY_SIZE)),
+ mExpMessageTime(0)
+#else
mInventory(new Inventory(INVENTORY_SIZE, 2)),
mStorage(new Inventory(STORAGE_SIZE, 1))
+#endif
{
// Variable to keep the local player from doing certain actions before a map
// is initialized. e.g. drawing a player's name using the TextManager, since
@@ -92,7 +136,9 @@ LocalPlayer::LocalPlayer(Uint32 id, Uint16 job, Map *map):
LocalPlayer::~LocalPlayer()
{
delete mInventory;
+#ifdef EATHENA_SUPPORT
delete mStorage;
+#endif
for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++)
{
@@ -105,6 +151,7 @@ LocalPlayer::~LocalPlayer()
void LocalPlayer::logic()
{
+#ifdef EATHENA_SUPPORT
switch (mAction)
{
case STAND:
@@ -120,7 +167,7 @@ void LocalPlayer::logic()
break;
case WALK:
- mFrame = (get_elapsed_time(mWalkTime) * 6) / mWalkSpeed;
+ mFrame = (get_elapsed_time(mWalkTime) * 6) / getWalkSpeed();
if (mFrame >= 6)
nextStep();
break;
@@ -161,11 +208,33 @@ void LocalPlayer::logic()
break;
}
+#endif
// Actions are allowed once per second
if (get_elapsed_time(mLastAction) >= 1000)
mLastAction = -1;
+#ifdef TMWSERV_SUPPORT
+ // Show XP messages
+ if (!mExpMessages.empty())
+ {
+ if (mExpMessageTime == 0)
+ {
+ const Vector &pos = getPosition();
+
+ particleEngine->addTextRiseFadeOutEffect(
+ mExpMessages.front(),
+ (int) pos.x + 16,
+ (int) pos.y - 16,
+ &guiPalette->getColor(Palette::EXP_INFO),
+ gui->getInfoParticleFont(), true);
+
+ mExpMessages.pop_front();
+ mExpMessageTime = 30;
+ }
+ mExpMessageTime--;
+ }
+#else
// Targeting allowed 4 times a second
if (get_elapsed_time(mLastTarget) >= 250)
mLastTarget = -1;
@@ -180,21 +249,32 @@ void LocalPlayer::logic()
if (mTarget)
{
- // Find whether target is in range
- const int rangeX = abs(mTarget->mX - mX);
- const int rangeY = abs(mTarget->mY - mY);
- const int attackRange = getAttackRange();
- const int inRange = rangeX > attackRange || rangeY > attackRange ? 1 : 0;
-
- mTarget->setTargetAnimation(
- mTargetCursor[inRange][mTarget->getTargetCursorSize()]);
-
- if (mTarget->mAction == DEAD)
- stopAttack();
-
- if (mKeepAttacking && mTarget)
- attack(mTarget, true);
+ if (mTarget->getType() == Being::NPC)
+ {
+ // NPCs are always in range
+ mTarget->setTargetAnimation(
+ mTargetCursor[0][mTarget->getTargetCursorSize()]);
+ }
+ else
+ {
+ // Find whether target is in range
+ const int rangeX = abs(mTarget->mX - mX);
+ const int rangeY = abs(mTarget->mY - mY);
+ const int attackRange = getAttackRange();
+ const int inRange = rangeX > attackRange || rangeY > attackRange
+ ? 1 : 0;
+
+ mTarget->setTargetAnimation(
+ mTargetCursor[inRange][mTarget->getTargetCursorSize()]);
+
+ if (mTarget->mAction == DEAD)
+ stopAttack();
+
+ if (mKeepAttacking && mTarget)
+ attack(mTarget, true);
+ }
}
+#endif
Being::logic();
}
@@ -225,6 +305,8 @@ void LocalPlayer::setName(const std::string &name)
void LocalPlayer::nextStep()
{
+ // TODO: Fix picking up when reaching target (this method is obsolete)
+ // TODO: Fix holding walking button to keep walking smoothly
if (mPath.empty())
{
if (mPickUpTarget)
@@ -234,10 +316,13 @@ void LocalPlayer::nextStep()
walk(mWalkingDir);
}
+ // TODO: Fix automatically walking within range of target, when wanted
if (mGoingToTarget && mTarget && withinAttackRange(mTarget))
{
mAction = Being::STAND;
+#ifdef EATHENA_SUPPORT
attack(mTarget, true);
+#endif
mGoingToTarget = false;
mPath.clear();
return;
@@ -248,17 +333,99 @@ void LocalPlayer::nextStep()
mPath.clear();
}
+#ifdef EATHENA_SUPPORT
Player::nextStep();
+#endif
+}
+
+#ifdef TMWSERV_SUPPORT
+bool LocalPlayer::checkInviteRights(const std::string &guildName)
+{
+ Guild *guild = getGuild(guildName);
+ if (guild)
+ {
+ return guild->getInviteRights();
+ }
+
+ return false;
+}
+
+void LocalPlayer::inviteToGuild(Being *being)
+{
+ // TODO: Allow user to choose which guild to invite being to
+ // For now, just invite to the first guild you have permissions to invite with
+ std::map<int, Guild*>::iterator itr = mGuilds.begin();
+ std::map<int, Guild*>::iterator itr_end = mGuilds.end();
+ for (; itr != itr_end; ++itr)
+ {
+ if (checkInviteRights(itr->second->getName()))
+ {
+ Net::ChatServer::Guild::invitePlayer(being->getName(), itr->second->getId());
+ return;
+ }
+ }
+}
+
+void LocalPlayer::inviteToParty(const std::string &name)
+{
+ Net::ChatServer::Party::invitePlayer(name);
+}
+
+void LocalPlayer::clearInventory()
+{
+ mEquipment->clear();
+ mInventory->clear();
+}
+
+void LocalPlayer::setInvItem(int index, int id, int amount)
+{
+ mInventory->setItem(index, id, amount);
+}
+
+#endif
+
+void LocalPlayer::moveInvItem(Item *item, int newIndex)
+{
+ // special case, the old and new cannot copy over each other.
+ if (item->getInvIndex() == newIndex)
+ return;
+
+#ifdef TMWSERV_SUPPORT
+ Net::GameServer::Player::moveItem(
+ item->getInvIndex(), newIndex, item->getQuantity());
+#endif
+ // TODO: eAthena support
}
void LocalPlayer::equipItem(Item *item)
{
+#ifdef TMWSERV_SUPPORT
+ Net::GameServer::Player::equip(item->getInvIndex());
+#else
MessageOut outMsg(mNetwork);
outMsg.writeInt16(CMSG_PLAYER_EQUIP);
outMsg.writeInt16(item->getInvIndex());
outMsg.writeInt16(0);
+#endif
+}
+
+#ifdef TMWSERV_SUPPORT
+
+void LocalPlayer::unequipItem(int slot)
+{
+ Net::GameServer::Player::unequip(slot);
+
+ // Tidy equipment directly to avoid weapon still shown bug, for instance
+ mEquipment->setEquipment(slot, 0);
+}
+
+void LocalPlayer::useItem(int slot)
+{
+ Net::GameServer::Player::useItem(slot);
}
+#else
+
void LocalPlayer::unequipItem(Item *item)
{
if (!item)
@@ -281,48 +448,102 @@ void LocalPlayer::useItem(Item *item)
// Note: id is dest of item, usually player_node->account_ID ??
}
+#endif
+
void LocalPlayer::dropItem(Item *item, int quantity)
{
+#ifdef TMWSERV_SUPPORT
+ Net::GameServer::Player::drop(item->getInvIndex(), quantity);
+#else
// TODO: Fix wrong coordinates of drops, serverside?
MessageOut outMsg(mNetwork);
outMsg.writeInt16(CMSG_PLAYER_INVENTORY_DROP);
outMsg.writeInt16(item->getInvIndex());
outMsg.writeInt16(quantity);
+#endif
}
+#ifdef TMWSERV_SUPPORT
+void LocalPlayer::splitItem(Item *item, int quantity)
+{
+ int newIndex = mInventory->getFreeSlot();
+ if (newIndex > Inventory::NO_SLOT_INDEX)
+ {
+ Net::GameServer::Player::moveItem(
+ item->getInvIndex(), newIndex, quantity);
+ }
+}
+#endif
+
void LocalPlayer::pickUp(FloorItem *item)
{
+#ifdef TMWSERV_SUPPORT
+ int dx = item->getX() - (int) getPosition().x / 32;
+ int dy = item->getY() - (int) getPosition().y / 32;
+#else
int dx = item->getX() - mX;
int dy = item->getY() - mY;
+#endif
if (dx * dx + dy * dy < 4)
{
+#ifdef TMWSERV_SUPPORT
+ int id = item->getId();
+ Net::GameServer::Player::pickUp(id >> 16, id & 0xFFFF);
+#else
MessageOut outMsg(mNetwork);
outMsg.writeInt16(CMSG_ITEM_PICKUP);
outMsg.writeInt32(item->getId());
+#endif
mPickUpTarget = NULL;
}
else
{
+#ifdef TMWSERV_SUPPORT
+ setDestination(item->getX() * 32 + 16, item->getY() * 32 + 16);
+#else
setDestination(item->getX(), item->getY());
+#endif
mPickUpTarget = item;
+#ifdef EATHENA_SUPPORT
stopAttack();
+#endif
}
}
void LocalPlayer::walk(unsigned char dir)
{
+ // TODO: Evaluate the implementation of this method for tmwserv
if (!mMap || !dir)
return;
+#ifdef TMWSERV_SUPPORT
+ const Vector &pos = getPosition();
+ int dScaler; // Distance to walk
+#endif
+
if (mAction == WALK && !mPath.empty())
{
// Just finish the current action, otherwise we get out of sync
+#ifdef TMWSERV_SUPPORT
+ Being::setDestination(pos.x, pos.y);
+#else
Being::setDestination(mX, mY);
+#endif
return;
}
int dx = 0, dy = 0;
+#ifdef TMWSERV_SUPPORT
+ if (dir & UP)
+ dy -= 32;
+ if (dir & DOWN)
+ dy += 32;
+ if (dir & LEFT)
+ dx -= 32;
+ if (dir & RIGHT)
+ dx += 32;
+#else
if (dir & UP)
dy--;
if (dir & DOWN)
@@ -331,26 +552,65 @@ void LocalPlayer::walk(unsigned char dir)
dx--;
if (dir & RIGHT)
dx++;
+#endif
+
// Prevent skipping corners over colliding tiles
- if (dx && mMap->tileCollides(mX + dx, mY))
+#ifdef TMWSERV_SUPPORT
+ if (dx && !mMap->getWalk(((int) pos.x + dx) / 32,
+ (int) pos.y / 32, getWalkMask()))
+ dx = 16 - (int) pos.x % 32;
+ if (dy && !mMap->getWalk((int) pos.x / 32,
+ ((int) pos.y + dy) / 32, getWalkMask()))
+ dy = 16 - (int) pos.y % 32;
+#else
+ if (dx && !mMap->getWalk(mX + dx, mY, getWalkMask()))
dx = 0;
- if (dy && mMap->tileCollides(mX, mY + dy))
+ if (dy && !mMap->getWalk(mX, mY + dy, getWalkMask()))
dy = 0;
+#endif
// Choose a straight direction when diagonal target is blocked
- if (dx && dy && mMap->tileCollides(mX + dx, mY + dy))
+#ifdef TMWSERV_SUPPORT
+ if (dx && dy && !mMap->getWalk((pos.x + dx) / 32,
+ (pos.y + dy) / 32, getWalkMask()))
+ dx = 16 - (int) pos.x % 32;
+
+ // Checks our path up to 5 tiles, if a blocking tile is found
+ // We go to the last good tile, and break out of the loop
+ for (dScaler = 1; dScaler <= 10; dScaler++)
+ {
+ if ( (dx || dy) &&
+ !mMap->getWalk( ((int) pos.x + (dx * dScaler)) / 32,
+ ((int) pos.y + (dy * dScaler)) / 32, getWalkMask()) )
+ {
+ dScaler--;
+ break;
+ }
+ }
+
+ if (dScaler >= 0)
+ {
+ setDestination((int) pos.x + (dx * dScaler), (int) pos.y + (dy * dScaler));
+ }
+#else
+ if (dx && dy && !mMap->getWalk(mX + dx, mY + dy, getWalkMask()))
dx = 0;
// Walk to where the player can actually go
- if ((dx || dy) && !mMap->tileCollides(mX + dx, mY + dy))
+ if ((dx || dy) && mMap->getWalk(mX + dx, mY + dy, getWalkMask()))
{
setDestination(mX + dx, mY + dy);
}
+#endif
else if (dir)
{
// If the being can't move, just change direction
- // TODO: Communicate this to the server (waiting on tmwserv)
+#ifdef TMWSERV_SUPPORT
+ Net::GameServer::Player::changeDir(dir);
+#else
+ // TODO: Communicate this to the server
+#endif
setDirection(dir);
}
}
@@ -362,6 +622,7 @@ Being* LocalPlayer::getTarget() const
void LocalPlayer::setTarget(Being *target)
{
+#ifdef EATHENA_SUPPORT
if (mLastTarget != -1 || target == this)
return;
@@ -379,32 +640,63 @@ void LocalPlayer::setTarget(Being *target)
mKeepAttacking = false;
mTargetTime = -1;
}
+#endif
if (mTarget)
mTarget->untarget();
if (mTarget && mTarget->getType() == Being::MONSTER)
- static_cast<Monster *>(mTarget)->showName(false);
+ static_cast<Monster *>(mTarget)->setShowName(false);
mTarget = target;
if (target && target->getType() == Being::MONSTER)
- static_cast<Monster *>(target)->showName(true);
+ static_cast<Monster *>(target)->setShowName(true);
}
+#ifdef TMWSERV_SUPPORT
+void LocalPlayer::setDestination(int x, int y)
+#else
void LocalPlayer::setDestination(Uint16 x, Uint16 y)
+#endif
{
+#ifdef TMWSERV_SUPPORT
+ // Fix coordinates so that the player does not seem to dig into walls.
+ const int tx = x / 32;
+ const int ty = y / 32;
+ int fx = x % 32;
+ int fy = y % 32;
+
+ if (fx != 16 && !mMap->getWalk(tx + fx / 16 * 2 - 1, ty, getWalkMask()))
+ fx = 16;
+ if (fy != 16 && !mMap->getWalk(tx, ty + fy / 16 * 2 - 1, getWalkMask()))
+ fy = 16;
+ if (fx != 16 && fy != 16 && !mMap->getWalk(tx + fx / 16 * 2 - 1,
+ ty + fy / 16 * 2 - 1,
+ getWalkMask()))
+ fx = 16;
+
+ x = tx * 32 + fx;
+ y = ty * 32 + fy;
+#endif
+
// Only send a new message to the server when destination changes
if (x != mDestX || y != mDestY)
{
mDestX = x;
mDestY = y;
+#ifdef TMWSERV_SUPPORT
+ Net::GameServer::Player::walk(x, y);
+ //Debugging fire burst
+ effectManager->trigger(15,x,y);
+#else
char temp[4] = "";
MessageOut outMsg(mNetwork);
set_coordinates(temp, x, y, mDirection);
outMsg.writeInt16(0x0085);
outMsg.writeString(temp, 3);
+#endif
}
mPickUpTarget = NULL;
@@ -413,18 +705,36 @@ void LocalPlayer::setDestination(Uint16 x, Uint16 y)
void LocalPlayer::setWalkingDir(int dir)
{
- if (mWalkingDir != dir)
- {
- mWalkingDir = dir;
- }
+ mWalkingDir = dir;
// If we're not already walking, start walking.
- if (mAction != WALK && dir)
+ if (mAction != WALK && dir
+#ifdef TMWSERV_SUPPORT
+ && get_elapsed_time(mLocalWalkTime) >= walkingKeyboardDelay
+#endif
+ )
{
walk(dir);
}
}
+#ifdef TMWSERV_SUPPORT
+void LocalPlayer::stopWalking(bool sendToServer)
+{
+ if (mAction == WALK && mWalkingDir) {
+ mWalkingDir = 0;
+ mLocalWalkTime = 0;
+ Being::setDestination(getPosition().x,getPosition().y);
+ if (sendToServer)
+ Net::GameServer::Player::walk(getPosition().x, getPosition().y);
+ setAction(STAND);
+ }
+
+ clearPath();
+}
+#endif
+
+#ifdef EATHENA_SUPPORT
void LocalPlayer::raiseAttribute(Attribute attr)
{
MessageOut outMsg(mNetwork);
@@ -468,6 +778,7 @@ void LocalPlayer::raiseSkill(Uint16 skillId)
outMsg.writeInt16(CMSG_SKILL_LEVELUP_REQUEST);
outMsg.writeInt16(skillId);
}
+#endif
void LocalPlayer::toggleSit()
{
@@ -475,18 +786,23 @@ void LocalPlayer::toggleSit()
return;
mLastAction = tick_time;
- char type;
+ Being::Action newAction;
switch (mAction)
{
- case STAND: type = 2; break;
- case SIT: type = 3; break;
+ case STAND: newAction = SIT; break;
+ case SIT: newAction = STAND; break;
default: return;
}
+#ifdef TMWSERV_SUPPORT
+ setAction(newAction);
+ Net::GameServer::Player::changeAction(newAction);
+#else
MessageOut outMsg(mNetwork);
outMsg.writeInt16(0x0089);
outMsg.writeInt32(0);
- outMsg.writeInt8(type);
+ outMsg.writeInt8((newAction == SIT) ? 2 : 3);
+#endif
}
void LocalPlayer::emote(Uint8 emotion)
@@ -495,11 +811,15 @@ void LocalPlayer::emote(Uint8 emotion)
return;
mLastAction = tick_time;
+ // XXX Convert for new server
+#ifdef EATHENA_SUPPORT
MessageOut outMsg(mNetwork);
outMsg.writeInt16(0x00bf);
outMsg.writeInt8(emotion);
+#endif
}
+#ifdef EATHENA_SUPPORT
void LocalPlayer::tradeReply(bool accept)
{
if (!accept)
@@ -509,12 +829,21 @@ void LocalPlayer::tradeReply(bool accept)
outMsg.writeInt16(CMSG_TRADE_RESPONSE);
outMsg.writeInt8(accept ? 3 : 4);
}
+#endif
void LocalPlayer::trade(Being *being) const
{
+#ifdef TMWSERV_SUPPORT
+ extern std::string tradePartnerName;
+ extern int tradePartnerID;
+ tradePartnerName = being->getName();
+ tradePartnerID = being->getId();
+ Net::GameServer::Player::requestTrade(tradePartnerID);
+#else
MessageOut outMsg(mNetwork);
outMsg.writeInt16(CMSG_TRADE_REQUEST);
outMsg.writeInt32(being->getId());
+#endif
}
bool LocalPlayer::tradeRequestOk() const
@@ -522,11 +851,70 @@ bool LocalPlayer::tradeRequestOk() const
return !mTrading;
}
+#ifdef TMWSERV_SUPPORT
+
+void LocalPlayer::attack()
+{
+ if (mLastAction != -1)
+ return;
+
+ // Can only attack when standing still
+ if (mAction != STAND && mAction != ATTACK)
+ return;
+
+ //Face direction of the target
+ if(mTarget){
+ unsigned char dir = 0;
+ int x = 0, y = 0;
+ Vector plaPos = this->getPosition();
+ Vector tarPos = mTarget->getPosition();
+ x = plaPos.x - tarPos.x;
+ y = plaPos.y - tarPos.y;
+ if(abs(x) < abs(y)){
+ //Check to see if target is above me or below me
+ if(y > 0){
+ dir = UP;
+ } else {
+ dir = DOWN;
+ }
+ } else {
+ //check to see if the target is to the left or right of me
+ if(x > 0){
+ dir = LEFT;
+ } else {
+ dir = RIGHT;
+ }
+ }
+ setDirection(dir);
+ }
+
+ mLastAction = tick_time;
+
+ setAction(ATTACK);
+
+ if (mEquippedWeapon)
+ {
+ std::string soundFile = mEquippedWeapon->getSound(EQUIP_EVENT_STRIKE);
+ if (soundFile != "") sound.playSfx(soundFile);
+ }
+ else {
+ sound.playSfx("sfx/fist-swish.ogg");
+ }
+ Net::GameServer::Player::attack(getSpriteDirection());
+}
+
+void LocalPlayer::useSpecial(int special)
+{
+ Net::GameServer::Player::useSpecial(special);
+}
+
+#else
+
void LocalPlayer::attack(Being *target, bool keep)
{
mKeepAttacking = keep;
- if (!target)
+ if (!target || target->getType() == Being::NPC)
return;
if ((mTarget != target) || !mTarget)
@@ -538,10 +926,8 @@ void LocalPlayer::attack(Being *target, bool keep)
int dist_x = target->mX - mX;
int dist_y = target->mY - mY;
- // Must be standing and be within attack range to continue
- if ((mAction != STAND) || (mAttackRange < abs(dist_x)) ||
- (mAttackRange < abs(dist_y)))
- return;
+ // Must be standing to attack
+ if (mAction != STAND) return;
if (abs(dist_y) >= abs(dist_x))
{
@@ -558,9 +944,6 @@ void LocalPlayer::attack(Being *target, bool keep)
setDirection(LEFT);
}
- // Implement charging attacks here
- mLastAttackTime = 0;
-
mWalkTime = tick_time;
mTargetTime = tick_time;
@@ -597,13 +980,87 @@ void LocalPlayer::stopAttack()
mLastTarget = -1;
}
+#endif // no TMWSERV_SUPPORT
+
void LocalPlayer::revive()
{
+ // XXX Convert for new server
+#ifdef EATHENA_SUPPORT
MessageOut outMsg(mNetwork);
outMsg.writeInt16(0x00b2);
outMsg.writeInt8(0);
+#endif
+}
+
+#ifdef TMWSERV_SUPPORT
+
+void LocalPlayer::raiseAttribute(size_t attr)
+{
+ // we assume that the server allows the change. When not we will undo it later.
+ mCharacterPoints--;
+ mAttributeBase.at(attr)++;
+ Net::GameServer::Player::raiseAttribute(attr + CHAR_ATTR_BEGIN);
+}
+
+void LocalPlayer::lowerAttribute(size_t attr)
+{
+ // we assume that the server allows the change. When not we will undo it later.
+ mCorrectionPoints--;
+ mCharacterPoints++;
+ mAttributeBase.at(attr)--;
+ Net::GameServer::Player::lowerAttribute(attr + CHAR_ATTR_BEGIN);
+}
+
+const struct LocalPlayer::SkillInfo& LocalPlayer::getSkillInfo(int skill)
+{
+ static const SkillInfo skills[CHAR_SKILL_NB + 1] =
+ {
+ { _("Unarmed"), "graphics/images/unarmed.png" }, // CHAR_SKILL_WEAPON_NONE
+ { _("Knife"), "graphics/images/knife.png" }, // CHAR_SKILL_WEAPON_KNIFE
+ { _("Sword"), "graphics/images/sword.png" }, // CHAR_SKILL_WEAPON_SWORD
+ { _("Polearm"), "graphics/images/polearm.png" }, // CHAR_SKILL_WEAPON_POLEARM
+ { _("Staff"), "graphics/images/staff.png" }, // CHAR_SKILL_WEAPON_STAFF
+ { _("Whip"), "graphics/images/whip.png" }, // CHAR_SKILL_WEAPON_WHIP
+ { _("Bow"), "graphics/images/bow.png" }, // CHAR_SKILL_WEAPON_BOW
+ { _("Shooting"), "graphics/images/shooting.png" }, // CHAR_SKILL_WEAPON_SHOOTING
+ { _("Mace"), "graphics/images/mace.png" }, // CHAR_SKILL_WEAPON_MACE
+ { _("Axe"), "graphics/images/axe.png" }, // CHAR_SKILL_WEAPON_AXE
+ { _("Thrown"), "graphics/images/thrown.png" }, // CHAR_SKILL_WEAPON_THROWN
+ { _("Magic"), "graphics/images/magic.png" }, // CHAR_SKILL_MAGIC_IAMJUSTAPLACEHOLDER
+ { _("Craft"), "graphics/images/craft.png" }, // CHAR_SKILL_CRAFT_IAMJUSTAPLACEHOLDER
+ { _("Unknown Skill"), "graphics/images/unknown.png" }
+ };
+
+ if ((skill < 0) || (skill > CHAR_SKILL_NB))
+ {
+ return skills[CHAR_SKILL_NB];
+ }
+ else
+ {
+ return skills[skill];
+ }
}
+void LocalPlayer::setExperience(int skill, int current, int next)
+{
+ int diff = current - mExpCurrent.at(skill);
+ if (mMap && mExpCurrent.at(skill) != -1 && diff > 0)
+ {
+ const std::string text = toString(diff) + " " + getSkillInfo(skill).name + " xp";
+ mExpMessages.push_back(text);
+ }
+
+ mExpCurrent.at(skill) = current;
+ mExpNext.at(skill) = next;
+}
+
+std::pair<int, int> LocalPlayer::getExperience(int skill)
+{
+ return std::pair<int, int> (mExpCurrent.at(skill), mExpNext.at(skill));
+}
+
+#else
+
void LocalPlayer::setXp(int xp)
{
if (mMap && xp > mXp)
@@ -618,7 +1075,9 @@ void LocalPlayer::setXp(int xp)
mXp = xp;
}
-void LocalPlayer::pickedUp(std::string item)
+#endif
+
+void LocalPlayer::pickedUp(const std::string &item)
{
if (mMap)
{
@@ -629,8 +1088,32 @@ void LocalPlayer::pickedUp(std::string item)
}
}
+int LocalPlayer::getAttackRange()
+{
+#ifdef TMWSERV_SUPPORT
+ Item *weapon = mEquipment->getEquipment(EQUIP_FIGHT1_SLOT);
+ if (weapon)
+ {
+ const ItemInfo info = weapon->getInfo();
+ return info.getAttackRange();
+ }
+ return 32; // unarmed range
+#else
+ return mAttackRange;
+#endif
+}
+
bool LocalPlayer::withinAttackRange(Being *target)
{
+#ifdef TMWSERV_SUPPORT
+ const Vector &targetPos = target->getPosition();
+ const Vector &pos = getPosition();
+ const int dx = abs(targetPos.x - pos.x);
+ const int dy = abs(targetPos.y - pos.y);
+ const int range = getAttackRange();
+
+ return !(dx > range || dy > range);
+#else
int dist_x = abs(target->mX - mX);
int dist_y = abs(target->mY - mY);
@@ -640,14 +1123,64 @@ bool LocalPlayer::withinAttackRange(Being *target)
}
return true;
+#endif
}
void LocalPlayer::setGotoTarget(Being *target)
{
+#ifdef TMWSERV_SUPPORT
+ mTarget = target;
+ mGoingToTarget = true;
+ const Vector &targetPos = target->getPosition();
+ setDestination(targetPos.x, targetPos.y);
+#else
mLastTarget = -1;
setTarget(target);
mGoingToTarget = true;
setDestination(target->mX, target->mY);
+#endif
+}
+
+
+extern MiniStatusWindow *miniStatusWindow;
+
+void LocalPlayer::handleStatusEffect(StatusEffect *effect, int effectId)
+{
+ Being::handleStatusEffect(effect, effectId);
+
+
+
+ if (effect) {
+ effect->deliverMessage();
+ effect->playSFX();
+
+ AnimatedSprite *sprite = effect->getIcon();
+
+ if (!sprite) {
+ // delete sprite, if necessary
+ for (unsigned int i = 0; i < mStatusEffectIcons.size();)
+ if (mStatusEffectIcons[i] == effectId) {
+ mStatusEffectIcons.erase(mStatusEffectIcons.begin() + i);
+ miniStatusWindow->eraseIcon(i);
+ } else i++;
+ } else {
+ // replace sprite or append
+ bool found = false;
+
+ for (unsigned int i = 0; i < mStatusEffectIcons.size(); i++)
+ if (mStatusEffectIcons[i] == effectId) {
+ miniStatusWindow->setIcon(i, sprite);
+ found = true;
+ break;
+ }
+
+ if (!found) { // add new
+ int offset = mStatusEffectIcons.size();
+ miniStatusWindow->setIcon(offset, sprite);
+ mStatusEffectIcons.push_back(effectId);
+ }
+ }
+ }
}
void LocalPlayer::initTargetCursor()
@@ -679,7 +1212,7 @@ void LocalPlayer::loadTargetCursor(std::string filename, int width, int height,
ResourceManager *resman = ResourceManager::getInstance();
currentImageSet = resman->getImageSet(filename, width, height);
- Animation *anim = new Animation();
+ Animation *anim = new Animation;
for (unsigned int i = 0; i < currentImageSet->size(); ++i)
{
@@ -696,9 +1229,11 @@ void LocalPlayer::loadTargetCursor(std::string filename, int width, int height,
mTargetCursor[index][size] = currentCursor;
}
+#ifdef EATHENA_SUPPORT
void LocalPlayer::setInStorage(bool inStorage)
{
mInStorage = inStorage;
storageWindow->setVisible(inStorage);
}
+#endif