summaryrefslogtreecommitdiff
path: root/src/being.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/being.cpp')
-rw-r--r--src/being.cpp322
1 files changed, 310 insertions, 12 deletions
diff --git a/src/being.cpp b/src/being.cpp
index f14fd64e..c0623255 100644
--- a/src/being.cpp
+++ b/src/being.cpp
@@ -56,6 +56,11 @@
#include <cassert>
#include <cmath>
+namespace {
+const bool debug_movement = true;
+}
+
+
#define BEING_EFFECTS_FILE "effects.xml"
#define HAIR_FILE "hair.xml"
@@ -70,22 +75,31 @@ static const int DEFAULT_WIDTH = 32;
static const int DEFAULT_HEIGHT = 32;
Being::Being(int id, int job, Map *map):
- mJob(job),
+#ifdef EATHENA_SUPPORT
mX(0), mY(0),
- mAction(STAND),
mWalkTime(0),
+#endif
mEmotion(0), mEmotionTime(0),
mSpeechTime(0),
mAttackSpeed(350),
+ mAction(STAND),
+ mJob(job),
mId(id),
- mWalkSpeed(150),
mDirection(DOWN),
+#ifdef TMWSERV_SUPPORT
+ mSpriteDirection(DIRECTION_DOWN),
+#endif
mMap(NULL),
mName(""),
mIsGM(false),
mParticleEffects(config.getValue("particleeffects", 1)),
mEquippedWeapon(NULL),
- mHairStyle(1), mHairColor(0),
+#ifdef TMWSERV_SUPPORT
+ mHairStyle(0),
+#else
+ mHairStyle(1),
+#endif
+ mHairColor(0),
mGender(GENDER_UNSPECIFIED),
mPx(0), mPy(0),
mStunMode(0),
@@ -95,6 +109,11 @@ Being::Being(int id, int job, Map *map):
mStatusParticleEffects(&mStunParticleEffects, false),
mChildParticleEffects(&mStatusParticleEffects, false),
mMustResetParticles(false),
+#ifdef TMWSERV_SUPPORT
+ mWalkSpeed(100),
+#else
+ mWalkSpeed(150),
+#endif
mUsedTargetCursor(NULL)
{
setMap(map);
@@ -121,12 +140,188 @@ Being::~Being()
delete mText;
}
+void Being::setPosition(const Vector &pos)
+{
+ mPos = pos;
+ mDest = pos;
+ mPath.clear();
+}
+
+#ifdef EATHENA_SUPPORT
void Being::setDestination(Uint16 destX, Uint16 destY)
{
if (mMap)
- setPath(mMap->findPath(mX, mY, destX, destY));
+ setPath(mMap->findPath(mX, mY, destX, destY, getWalkMask()));
+}
+#endif
+
+#ifdef TMWSERV_SUPPORT
+void Being::adjustCourse(int srcX, int srcY, int dstX, int dstY)
+{
+ if (debug_movement)
+ printf("%p adjustCourse(%d, %d, %d, %d)\n",
+ (void*) this, srcX, srcY, dstX, dstY);
+
+ mDest.x = dstX;
+ mDest.y = dstY;
+
+ // Find a path to the destination when it is at least a tile away
+ if (mMap && fabsf((mDest - mPos).length()) > 32) {
+ setPath(mMap->findPath((int) mPos.x / 32, (int) mPos.y / 32,
+ dstX / 32, dstY / 32, getWalkMask()));
+ } else {
+ setPath(Path());
+ }
+
+ // TODO: Evaluate the implementation of this method
+ /*
+ if (mX / 32 == dstX / 32 && mY / 32 == dstY / 32)
+ {
+ // The being is already on the last tile of the path.
+ Path p;
+ p.push_back(Position(dstX, dstY));
+ setPath(p);
+ return;
+ }
+
+ Path p1;
+ int p1_size, p1_length;
+ Uint16 *p1_dist;
+ int onPath = -1;
+ if (srcX / 32 == dstX / 32 && srcY / 32 == dstY / 32)
+ {
+ p1_dist = new Uint16[1];
+ p1_size = 1;
+ p1_dist[0] = 0;
+ p1_length = 0;
+ }
+ else
+ {
+ p1 = mMap->findPath(srcX / 32, srcY / 32, dstX / 32, dstY / 32, getWalkMask());
+ if (p1.empty())
+ {
+ // No path, but don't teleport since it could be user input.
+ setPath(p1);
+ return;
+ }
+ p1_size = p1.size();
+ p1_dist = new Uint16[p1_size];
+ int j = 0;
+ // Remove last tile so that it can be replaced by the exact destination.
+ p1.pop_back();
+ for (Path::iterator i = p1.begin(), i_end = p1.end(); i != i_end; ++i)
+ {
+ // Get distance from source to tile i.
+ p1_dist[j] = mMap->getMetaTile(i->x, i->y)->Gcost;
+ // Check if the being is already walking on the path.
+ if (i->x == mX / 32 && i->y == mY / 32)
+ {
+ onPath = j;
+ }
+ // Do not set any offset for intermediate steps.
+ i->x = i->x * 32;
+ i->y = i->y * 32;
+ ++j;
+ }
+ p1_length = mMap->getMetaTile(dstX / 32, dstY / 32)->Gcost;
+ p1_dist[p1_size - 1] = p1_length;
+ }
+ p1.push_back(Position(dstX, dstY));
+
+ if (mX / 32 == srcX / 32 && mY / 32 == srcY / 32)
+ {
+ // The being is at the start of the path.
+ setPath(p1);
+ delete[] p1_dist;
+ return;
+ }
+
+ if (onPath >= 0)
+ {
+ // The being is already on the path, but it needs to be slowed down.
+ for (int j = onPath; j >= 0; --j)
+ {
+ p1.pop_front();
+ }
+ int r = p1_length - p1_dist[onPath]; // remaining length
+ assert(r > 0);
+ setPath(p1, p1_length * 1024 / r);
+ delete[] p1_dist;
+ return;
+ }
+
+ Path bestPath;
+ int bestRating = -1, bestStart = 0, bestLength = 0;
+ int j = 0;
+
+ for (Path::iterator i = p1.begin(), i_end = p1.end(); i != i_end; ++i)
+ {
+ // Look if it is worth passing by tile i.
+ Path p2 = mMap->findPath(mX / 32, mY / 32, i->x / 32, i->y / 32, getWalkMask());
+ if (!p2.empty())
+ {
+ int l1 = mMap->getMetaTile(i->x / 32, i->y / 32)->Gcost;
+ int l2 = p1_length - p1_dist[j];
+ int r = l1 + l2 / 2; // TODO: tune rating formula
+ assert(r > 0);
+ if (bestRating < 0 || r < bestRating)
+ {
+ bestPath.swap(p2);
+ bestRating = r;
+ bestStart = j;
+ bestLength = l1 + l2;
+ }
+ }
+ ++j;
+ }
+
+ if (bestRating < 0)
+ {
+ // Unable to reach the path? Still, don't teleport since it could be
+ // user input instead of server command.
+ setPath(p1);
+ delete[] p1_dist;
+ return;
+ }
+
+ bestPath.pop_back();
+ for (Path::iterator i = bestPath.begin(), i_end = bestPath.end(); i != i_end; ++i)
+ {
+ i->x = i->x * 32;
+ i->y = i->y * 32;
+ }
+
+ // Concatenate paths.
+ for (int j = bestStart; j > 0; --j)
+ {
+ p1.pop_front();
+ }
+ p1.splice(p1.begin(), bestPath);
+
+ assert(bestLength > 0);
+ setPath(p1, p1_length * 1024 / bestLength);
+ delete[] p1_dist;
+ */
+}
+
+void Being::adjustCourse(int srcX, int srcY)
+{
+ if (debug_movement)
+ printf("%p adjustCourse(%d, %d)\n", (void*) this, srcX, srcY);
+
+ if (!mPath.empty())
+ adjustCourse(srcX, srcY, mPath.back().x * 32, mPath.back().y * 32);
}
+void Being::setDestination(int destX, int destY)
+{
+ if (debug_movement)
+ printf("%p setDestination(%d, %d)\n", (void*) this, destX, destY);
+
+ adjustCourse((int) mPos.x, (int) mPos.y, destX, destY);
+}
+#endif // TMWSERV_SUPPORT
+
void Being::clearPath()
{
mPath.clear();
@@ -135,12 +330,15 @@ void Being::clearPath()
void Being::setPath(const Path &path)
{
mPath = path;
-
+#ifdef TMWSERV_SUPPORT
+ std::cout << this << " New path: " << path << std::endl;
+#else
if (mAction != WALK && mAction != DEAD)
{
nextStep();
mWalkTime = tick_time;
}
+#endif
}
void Being::setHairStyle(int style, int color)
@@ -149,7 +347,7 @@ void Being::setHairStyle(int style, int color)
mHairColor = color < 0 ? mHairColor : color % ColorDB::size();
}
-void Being::setSprite(int slot, int id, std::string color)
+void Being::setSprite(int slot, int id, const std::string &color)
{
assert(slot >= BASE_SPRITE && slot < VECTOREND_SPRITE);
mSpriteIDs[slot] = id;
@@ -235,7 +433,13 @@ void Being::takeDamage(Being *attacker, int amount, AttackType type)
}
// Show damage number
- particleEngine->addTextSplashEffect(damage, mPx + 16, mPy + 16,
+ particleEngine->addTextSplashEffect(damage,
+#ifdef TMWSERV_SUPPORT
+ (int) mPos.x + 16,
+ (int) mPos.y + 16,
+#else
+ mPx + 16, mPy + 16,
+#endif
color, font, true);
if (amount > 0)
@@ -251,11 +455,17 @@ void Being::takeDamage(Being *attacker, int amount, AttackType type)
}
}
+#ifdef TMWSERV_SUPPORT
+void Being::handleAttack()
+#else
void Being::handleAttack(Being *victim, int damage, AttackType type)
+#endif
{
setAction(Being::ATTACK);
+#ifdef EATHENA_SUPPORT
mFrame = 0;
mWalkTime = tick_time;
+#endif
}
void Being::setMap(Map *map)
@@ -280,7 +490,7 @@ void Being::controlParticle(Particle *particle)
mChildParticleEffects.addLocally(particle);
}
-void Being::setAction(Action action)
+void Being::setAction(Action action, int attackType)
{
SpriteAction currentAction = ACTION_INVALID;
@@ -333,8 +543,27 @@ void Being::setDirection(Uint8 direction)
if (mDirection == direction)
return;
+#ifdef TMWSERV_SUPPORT
+ // if the direction does not change much, keep the common component
+ int mFaceDirection = mDirection & direction;
+ if (!mFaceDirection)
+ mFaceDirection = direction;
+ mDirection = direction;
+
+ SpriteDirection dir;
+ if (mFaceDirection & UP)
+ dir = DIRECTION_UP;
+ else if (mFaceDirection & DOWN)
+ dir = DIRECTION_DOWN;
+ else if (mFaceDirection & RIGHT)
+ dir = DIRECTION_RIGHT;
+ else
+ dir = DIRECTION_LEFT;
+ mSpriteDirection = dir;
+#else
mDirection = direction;
SpriteDirection dir = getSpriteDirection();
+#endif
for (int i = 0; i < VECTOREND_SPRITE; i++)
{
@@ -343,6 +572,7 @@ void Being::setDirection(Uint8 direction)
}
}
+#ifdef EATHENA_SUPPORT
SpriteDirection Being::getSpriteDirection() const
{
SpriteDirection dir;
@@ -382,7 +612,7 @@ void Being::nextStep()
setDirection(dir);
- if (mMap->tileCollides(pos.x, pos.y))
+ if (!mMap->getWalk(pos.x, pos.y, getWalkMask()))
{
setAction(STAND);
return;
@@ -393,12 +623,13 @@ void Being::nextStep()
setAction(WALK);
mWalkTime += mWalkSpeed / 10;
}
+#endif
void Being::logic()
{
// Reduce the time that speech is still displayed
if (mSpeechTime > 0)
- mSpeechTime--;
+ mSpeechTime--;
// Remove text and speechbubbles if speech boxes aren't being used
if (mSpeechTime == 0 && mText)
@@ -407,6 +638,42 @@ void Being::logic()
mText = 0;
}
+#ifdef TMWSERV_SUPPORT
+ const Vector dest = (mPath.empty()) ?
+ mDest : Vector(mPath.front().x * 32 + 16,
+ mPath.front().y * 32 + 16);
+
+ Vector dir = dest - mPos;
+ const float length = dir.length();
+
+ // When we're over 2 pixels from our destination, move to it
+ // TODO: Should be possible to make it even pixel exact, but this solves
+ // the jigger caused by moving too far.
+ if (length > 2.0f) {
+ const float speed = mWalkSpeed / 100.0f;
+ mPos += dir / (length / speed);
+
+ if (mAction != WALK)
+ setAction(WALK);
+
+ // Update the player sprite direction
+ int direction = 0;
+ const float dx = std::abs(dir.x);
+ const float dy = std::abs(dir.y);
+ if (dx > dy)
+ direction |= (dir.x > 0) ? RIGHT : LEFT;
+ else
+ direction |= (dir.y > 0) ? DOWN : UP;
+ setDirection(direction);
+ }
+ else if (!mPath.empty()) {
+ // TODO: Pop as soon as there is a direct unblocked line to the next
+ // point on the path.
+ mPath.pop_front();
+ } else if (mAction == WALK) {
+ setAction(STAND);
+ }
+#else
int oldPx = mPx;
int oldPy = mPy;
@@ -418,6 +685,7 @@ void Being::logic()
{
updateCoords();
}
+#endif
if (mEmotion != 0)
{
@@ -448,13 +716,23 @@ void Being::logic()
}
// Update particle effects
- mChildParticleEffects.moveTo((float) mPx + 16.0f, (float) mPy + 32.0f);
+#ifdef TMWSERV_SUPPORT
+ mChildParticleEffects.moveTo((float) mPx + 16.0f,
+ (float) mPy + 32.0f);
+#else
+ mChildParticleEffects.moveTo(mPos.x, mPos.y);
+#endif
}
void Being::draw(Graphics *graphics, int offsetX, int offsetY) const
{
+#ifdef TMWSERV_SUPPORT
+ int px = (int) mPos.x + offsetX;
+ int py = (int) mPos.y + offsetY;
+#else
int px = mPx + offsetX;
int py = mPy + offsetY;
+#endif
if (mUsedTargetCursor)
mUsedTargetCursor->draw(graphics, px, py);
@@ -462,7 +740,15 @@ void Being::draw(Graphics *graphics, int offsetX, int offsetY) const
for (int i = 0; i < VECTOREND_SPRITE; i++)
{
if (mSprites[i])
+ {
+#ifdef TMWSERV_SUPPORT
+ // TODO: Eventually, we probably should fix all sprite offsets so
+ // that this translation isn't necessary anymore.
+ mSprites[i]->draw(graphics, px - 16, py - 32);
+#else
mSprites[i]->draw(graphics, px, py);
+#endif
+ }
}
}
@@ -471,8 +757,13 @@ void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY)
if (!mEmotion)
return;
+#ifdef TMWSERV_SUPPORT
+ const int px = (int) mPos.x + offsetX;
+ const int py = (int) mPos.y + offsetY - 64;
+#else
const int px = mPx - offsetX;
const int py = mPy - offsetY - 64;
+#endif
const int emotionIndex = mEmotion - 1;
if (emotionIndex >= 0 && emotionIndex <= EmoteDB::getLast())
@@ -481,8 +772,13 @@ void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY)
void Being::drawSpeech(int offsetX, int offsetY)
{
+#ifdef TMWSERV_SUPPORT
+ int px = (int) mPos.x - offsetX;
+ int py = (int) mPos.y - offsetY;
+#else
const int px = mPx - offsetX;
const int py = mPy - offsetY;
+#endif
const int speech = (int) config.getValue("speech", NAME_IN_BUBBLE);
// Draw speech above this being
@@ -596,6 +892,7 @@ void Being::setStatusEffect(int index, bool active)
}
}
+#ifdef EATHENA_SUPPORT
int Being::getOffset(char pos, char neg) const
{
// Check whether we're walking in the requested direction
@@ -615,6 +912,7 @@ int Being::getOffset(char pos, char neg) const
return offset;
}
+#endif
int Being::getWidth() const
{