summaryrefslogtreecommitdiff
path: root/src/being.cpp
diff options
context:
space:
mode:
authorYohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer>2011-04-26 23:40:37 +0200
committerYohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer>2011-04-26 23:40:37 +0200
commit2d0e84449b14615bdacb6f897791628662bbfd39 (patch)
tree08019ae6c24c6da25e25da027d04ad008c25d6a2 /src/being.cpp
parent42605753159d7b63276351311e0fd43874a3366b (diff)
parent0056412ed33b941d72a175dcd3f025abcd8fc02b (diff)
downloadmana-2d0e84449b14615bdacb6f897791628662bbfd39.tar.gz
mana-2d0e84449b14615bdacb6f897791628662bbfd39.tar.bz2
mana-2d0e84449b14615bdacb6f897791628662bbfd39.tar.xz
mana-2d0e84449b14615bdacb6f897791628662bbfd39.zip
Made the client use a unique kind of movement code.
This is fixng many issues and (hopefully) will make the movement rendering much smoother. Merge branch 'master' of gitorious.org:~bertram/mana/mana-movement-code-merge Conflicts: src/being.cpp src/net/manaserv/beinghandler.cpp Resolves: TMW-Mantis #946. Reviewed-by: Thorbjorn.
Diffstat (limited to 'src/being.cpp')
-rw-r--r--src/being.cpp368
1 files changed, 162 insertions, 206 deletions
diff --git a/src/being.cpp b/src/being.cpp
index 788422fb..5911b03c 100644
--- a/src/being.cpp
+++ b/src/being.cpp
@@ -71,7 +71,6 @@ static const int DEFAULT_BEING_WIDTH = 32;
static const int DEFAULT_BEING_HEIGHT = 32;
int Being::mNumberOfHairstyles = 1;
-// TODO: mWalkTime used by eAthena only
Being::Being(int id, Type type, int subtype, Map *map):
ActorSprite(id),
mInfo(BeingInfo::Unknown),
@@ -91,7 +90,7 @@ Being::Being(int id, Type type, int subtype, Map *map):
mParty(NULL),
mIsGM(false),
mType(type),
- mX(0), mY(0),
+ mSpeedPixelsPerTick(Vector(0.0f, 0.0f, 0.0f)),
mDamageTaken(0),
mIp(0)
{
@@ -100,7 +99,7 @@ Being::Being(int id, Type type, int subtype, Map *map):
mSpeechBubble = new SpeechBubble;
- mWalkSpeed = Net::getPlayerHandler()->getDefaultWalkSpeed();
+ mMoveSpeed = Net::getPlayerHandler()->getDefaultMoveSpeed();
if (getType() == PLAYER)
mShowName = config.getBoolValue("visiblenames");
@@ -168,6 +167,15 @@ Map::BlockType Being::getBlockType() const
return mInfo->getBlockType();
}
+void Being::setMoveSpeed(const Vector &speed)
+{
+ mMoveSpeed = speed;
+ // If we already can, recalculate the system speed right away.
+ if (mMap)
+ mSpeedPixelsPerTick =
+ Net::getPlayerHandler()->getPixelsPerTickMoveSpeed(speed);
+}
+
void Being::setPosition(const Vector &pos)
{
Actor::setPosition(pos);
@@ -181,39 +189,46 @@ void Being::setPosition(const Vector &pos)
void Being::setDestination(int dstX, int dstY)
{
- if (Net::getNetworkType() == ServerInfo::TMWATHENA)
- {
- if (mMap)
- setPath(mMap->findPath(mX, mY, dstX, dstY, getWalkMask()));
- return;
- }
-
- // Manaserv's part:
-
// We can't calculate anything without a map anyway.
if (!mMap)
return;
// Don't handle flawed destinations from server...
- if (dstX == 0 || dstY == 0)
+ if (dstX <= 0 || dstY <= 0)
return;
// If the destination is unwalkable, don't bother trying to get there
- if (!mMap->getWalk(dstX / 32, dstY / 32))
+ int tileWidth = mMap->getTileWidth();
+ int tileHeight = mMap->getTileHeight();
+ if (!mMap->getWalk(dstX / tileWidth, dstY / tileHeight))
return;
- Position dest = mMap->checkNodeOffsets(getCollisionRadius(), getWalkMask(),
- dstX, dstY);
- Path thisPath = mMap->findPixelPath((int) mPos.x, (int) mPos.y,
- dest.x, dest.y,
- getCollisionRadius(), getWalkMask());
+ Position dest(0, 0);
+ Path thisPath;
+ if (Net::getPlayerHandler()->usePixelPrecision())
+ {
+ dest = mMap->checkNodeOffsets(getCollisionRadius(), getWalkMask(),
+ dstX, dstY);
+ thisPath = mMap->findPixelPath((int) mPos.x, (int) mPos.y,
+ dest.x, dest.y,
+ getCollisionRadius(), getWalkMask());
+ }
+ else
+ {
+ // We center the destination.
+ dest.x = (dstX / tileWidth) * tileWidth + tileWidth / 2;
+ dest.y = (dstY / tileHeight) * tileHeight + tileHeight / 2;
+ // and find a tile centered pixel path
+ thisPath = mMap->findTilePath((int) mPos.x, (int) mPos.y,
+ dest.x, dest.y, getWalkMask());
+ }
if (thisPath.empty())
{
// If there is no path but the destination is on the same walkable tile,
// we accept it.
- if ((int)mPos.x / 32 == dest.x / 32
- && (int)mPos.y / 32 == dest.y / 32)
+ if ((int)mPos.x / tileWidth == dest.x / tileWidth
+ && (int)mPos.y / tileHeight == dest.y / tileHeight)
{
mDest.x = dest.x;
mDest.y = dest.y;
@@ -237,13 +252,6 @@ void Being::clearPath()
void Being::setPath(const Path &path)
{
mPath = path;
-
- if ((Net::getNetworkType() == ServerInfo::TMWATHENA) &&
- mAction != MOVE && mAction != DEAD)
- {
- nextTile();
- mActionTime = tick_time;
- }
}
void Being::setSpeech(const std::string &text, int time)
@@ -393,19 +401,17 @@ void Being::handleAttack(Being *victim, int damage, AttackType type)
if (this != player_node)
setAction(Being::ATTACK, 1);
+ if (victim)
+ lookAt(victim->getPosition());
+
if (getType() == PLAYER && victim && mEquippedWeapon)
fireMissile(victim, mEquippedWeapon->getMissileParticle());
else
fireMissile(victim, mInfo->getAttack(mAttackType)->missileParticle);
- if (Net::getNetworkType() == ServerInfo::TMWATHENA)
- {
- reset();
- mActionTime = tick_time;
- }
-
sound.playSfx(mInfo->getSound((damage > 0) ?
- SOUND_EVENT_HIT : SOUND_EVENT_MISS), mX, mY);
+ SOUND_EVENT_HIT : SOUND_EVENT_MISS),
+ getPixelX(), getPixelY());
}
void Being::setName(const std::string &name)
@@ -597,28 +603,26 @@ void Being::setAction(Action action, int attackType)
currentAction = mInfo->getAttack(attackType)->action;
reset();
- if (Net::getNetworkType() == ServerInfo::MANASERV)
+ int rotation = 0;
+ //attack particle effect
+ std::string particleEffect = mInfo->getAttack(attackType)
+ ->particleEffect;
+ if (!particleEffect.empty() && Particle::enabled)
{
- int rotation = 0;
- //attack particle effect
- std::string particleEffect = mInfo->getAttack(attackType)
- ->particleEffect;
- if (!particleEffect.empty() && Particle::enabled)
+ switch (mSpriteDirection)
{
- switch (mSpriteDirection)
- {
- case DIRECTION_DOWN: rotation = 0; break;
- case DIRECTION_LEFT: rotation = 90; break;
- case DIRECTION_UP: rotation = 180; break;
- case DIRECTION_RIGHT: rotation = 270; break;
- default: break;
- }
- Particle *p;
- p = particleEngine->addEffect(particleEffect, 0, 0,
- rotation);
- controlParticle(p);
+ case DIRECTION_DOWN: rotation = 0; break;
+ case DIRECTION_LEFT: rotation = 90; break;
+ case DIRECTION_UP: rotation = 180; break;
+ case DIRECTION_RIGHT: rotation = 270; break;
+ default: break;
}
+ Particle *p;
+ p = particleEngine->addEffect(particleEffect, 0, 0,
+ rotation);
+ controlParticle(p);
}
+
}
break;
@@ -630,7 +634,8 @@ void Being::setAction(Action action, int attackType)
break;
case DEAD:
currentAction = SpriteAction::DEAD;
- sound.playSfx(mInfo->getSound(SOUND_EVENT_DIE), mX, mY);
+ sound.playSfx(mInfo->getSound(SOUND_EVENT_DIE),
+ getPixelX(), getPixelY());
break;
case STAND:
currentAction = SpriteAction::STAND;
@@ -647,13 +652,90 @@ void Being::setAction(Action action, int attackType)
mActionTime = tick_time;
}
-void Being::setDirection(Uint8 direction)
+void Being::lookAt(const Vector &destPos)
{
- if (Net::getNetworkType() == ServerInfo::MANASERV)
+ // We first handle simple cases
+
+ // If the two positions are the same,
+ // don't update the direction since it's only a matter of keeping
+ // the previous one.
+ if (mPos.x == destPos.x && mPos.y == destPos.y)
+ return;
+
+ if (mPos.x == destPos.x)
+ {
+ if (mPos.y > destPos.y)
+ setDirection(UP);
+ else
+ setDirection(DOWN);
+ return;
+ }
+
+ if (mPos.y == destPos.y)
+ {
+ if (mPos.x > destPos.x)
+ setDirection(LEFT);
+ else
+ setDirection(RIGHT);
+ return;
+ }
+
+ // Now let's handle diagonal cases
+ // First, find the lower angle:
+ if (mPos.x < destPos.x)
+ {
+ // Up-right direction
+ if (mPos.y > destPos.y)
+ {
+ // Compute tan of the angle
+ if ((mPos.y - destPos.y) / (destPos.x - mPos.x) < 1)
+ // The angle is less than 45°, we look to the right
+ setDirection(RIGHT);
+ else
+ setDirection(UP);
+ return;
+ }
+ else // Down-right
+ {
+ // Compute tan of the angle
+ if ((destPos.y - mPos.y) / (destPos.x - mPos.x) < 1)
+ // The angle is less than 45°, we look to the right
+ setDirection(RIGHT);
+ else
+ setDirection(DOWN);
+ return;
+ }
+ }
+ else
{
- if (mDirection == direction)
+ // Up-left direction
+ if (mPos.y > destPos.y)
+ {
+ // Compute tan of the angle
+ if ((mPos.y - destPos.y) / (mPos.x - destPos.x) < 1)
+ // The angle is less than 45°, we look to the left
+ setDirection(LEFT);
+ else
+ setDirection(UP);
+ return;
+ }
+ else // Down-left
+ {
+ // Compute tan of the angle
+ if ((destPos.y - mPos.y) / (mPos.x - destPos.x) < 1)
+ // The angle is less than 45°, we look to the left
+ setDirection(LEFT);
+ else
+ setDirection(DOWN);
return;
+ }
}
+}
+
+void Being::setDirection(Uint8 direction)
+{
+ if (!direction || mDirection == direction)
+ return;
mDirection = direction;
@@ -671,45 +753,9 @@ void Being::setDirection(Uint8 direction)
CompoundSprite::setDirection(dir);
}
-/** Note: Used by Tmw-Athena only */
-void Being::nextTile()
-{
- if (mPath.empty())
- {
- setAction(STAND);
- return;
- }
-
- Position pos = mPath.front();
- mPath.pop_front();
-
- int dir = 0;
- if (pos.x > mX)
- dir |= RIGHT;
- else if (pos.x < mX)
- dir |= LEFT;
- if (pos.y > mY)
- dir |= DOWN;
- else if (pos.y < mY)
- dir |= UP;
-
- setDirection(dir);
-
- if (!mMap->getWalk(pos.x, pos.y, getWalkMask()))
- {
- setAction(STAND);
- return;
- }
-
- mX = pos.x;
- mY = pos.y;
- setAction(MOVE);
- mActionTime += (int)(mWalkSpeed.x / 10);
-}
-
int Being::getCollisionRadius() const
{
- // FIXME: Get this from XML file
+ // FIXME: Get this from XML file once a better pathfinding algorithm is up.
return 16;
}
@@ -726,15 +772,14 @@ void Being::logic()
mText = 0;
}
- if ((Net::getNetworkType() == ServerInfo::MANASERV) && (mAction != DEAD)
- && !mWalkSpeed.isNull())
+ if ((mAction != DEAD) && !mSpeedPixelsPerTick.isNull())
{
const Vector dest = (mPath.empty()) ?
mDest : Vector(mPath.front().x,
mPath.front().y);
// Avoid going to flawed destinations
- if (mDest.x <= 0 && mDest.y <= 0)
+ if (dest.x <= 0 || dest.y <= 0)
{
// We make the being stop move in that case.
mDest = mPos;
@@ -758,8 +803,8 @@ void Being::logic()
// â = a / ||a|| (||a|| is the a length.)
// Then, diff = (dir/||dir||) * speed.
const Vector normalizedDir = dir.normalized();
- Vector diff(normalizedDir.x * mWalkSpeed.x,
- normalizedDir.y * mWalkSpeed.y);
+ Vector diff(normalizedDir.x * mSpeedPixelsPerTick.x,
+ normalizedDir.y * mSpeedPixelsPerTick.y);
// Test if we don't miss the destination by a move too far:
if (diff.length() > distance)
@@ -784,8 +829,10 @@ void Being::logic()
// Update the player sprite direction.
// N.B.: We only change this if the distance is more than one pixel
- // to avoid flawing the ending direction.
- if (distance > 1.0f)
+ // to avoid flawing the ending direction for players,
+ // but always for very slow beings.
+ float maxDistance = mSpeedPixelsPerTick.length();
+ if (distance > ((maxDistance > 1.0f) ? 1.0f : 0.0f))
{
// The player direction is handled for keyboard
// by LocalPlayer::startWalking(), we shouldn't get
@@ -821,85 +868,12 @@ void Being::logic()
setAction(STAND);
}
}
- else if (Net::getNetworkType() == ServerInfo::TMWATHENA)
- {
- int frameCount = getFrameCount();
-
- switch (mAction)
- {
- case STAND:
- case SIT:
- case DEAD:
- case HURT:
- break;
-
- case MOVE:
- if ((int) ((get_elapsed_time(mActionTime) * frameCount)
- / getWalkSpeed().x) >= frameCount)
- nextTile();
- break;
-
- case ATTACK:
- int rotation = 0;
- std::string particleEffect = "";
-
- int curFrame = (get_elapsed_time(mActionTime) * frameCount)
- / mAttackSpeed;
-
- //attack particle effect
- if (mEquippedWeapon)
- {
- particleEffect = mEquippedWeapon->getParticleEffect();
-
- if (!particleEffect.empty() &&
- findSameSubstring(particleEffect,
- paths.getStringValue("particles")).empty())
- particleEffect = paths.getStringValue("particles")
- + particleEffect;
- }
- else
- {
- particleEffect = mInfo->getAttack(mAttackType)
- ->particleEffect;
- }
-
- if (!particleEffect.empty() && Particle::enabled
- && curFrame == 1)
- {
- switch (mDirection)
- {
- case DOWN: rotation = 0; break;
- case LEFT: rotation = 90; break;
- case UP: rotation = 180; break;
- case RIGHT: rotation = 270; break;
- default: break;
- }
- Particle *p;
- p = particleEngine->addEffect(particleEffect, 0, 0,
- rotation);
- controlParticle(p);
- }
-
- if (curFrame >= frameCount)
- nextTile();
-
- break;
- }
-
- // Update pixel coordinates
- setPosition(mX * 32 + 16 + getXOffset(),
- mY * 32 + 32 + getYOffset());
- }
ActorSprite::logic();
- int frameCount = getFrameCount();
- if (frameCount < 10)
- frameCount = 10;
-
+ // Remove it after 3 secs. TODO: Just play the dead animation before removing
if (!isAlive() && Net::getGameHandler()->removeDeadBeings() &&
- (int) ((get_elapsed_time(mActionTime)
- / getWalkSpeed().x) >= frameCount))
+ get_elapsed_time(mActionTime) > 3000)
{
if (getType() != PLAYER)
actorSpriteManager->destroy(this);
@@ -960,36 +934,6 @@ void Being::drawSpeech(int offsetX, int offsetY)
}
}
-/** Note: Used by Tmw-Athena only */
-int Being::getOffset(char pos, char neg) const
-{
- // Check whether we're walking in the requested direction
- if (mAction != MOVE || !(mDirection & (pos | neg)))
- return 0;
-
- int offset = 0;
-
- if (mMap)
- {
- offset = (pos == LEFT && neg == RIGHT) ?
- (int)((get_elapsed_time(mActionTime)
- * mMap->getTileWidth()) / mWalkSpeed.x) :
- (int)((get_elapsed_time(mActionTime)
- * mMap->getTileHeight()) / mWalkSpeed.y);
- }
-
- // We calculate the offset _from_ the _target_ location
- offset -= 32;
- if (offset > 0)
- offset = 0;
-
- // Going into negative direction? Invert the offset.
- if (mDirection & pos)
- offset = -offset;
-
- return offset;
-}
-
int Being::getWidth() const
{
return std::max(CompoundSprite::getWidth(), DEFAULT_BEING_WIDTH);
@@ -1253,3 +1197,15 @@ void Being::event(Event::Channel channel, const Event &event)
}
}
+
+void Being::setMap(Map *map)
+{
+ Actor::setMap(map);
+
+ // Recalculate pixel/tick speed
+ if (map && !mMoveSpeed.isNull())
+ {
+ mSpeedPixelsPerTick =
+ Net::getPlayerHandler()->getPixelsPerTickMoveSpeed(mMoveSpeed, map);
+ }
+}