diff options
author | Yohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer> | 2011-04-26 23:40:37 +0200 |
---|---|---|
committer | Yohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer> | 2011-04-26 23:40:37 +0200 |
commit | 2d0e84449b14615bdacb6f897791628662bbfd39 (patch) | |
tree | 08019ae6c24c6da25e25da027d04ad008c25d6a2 /src/being.cpp | |
parent | 42605753159d7b63276351311e0fd43874a3366b (diff) | |
parent | 0056412ed33b941d72a175dcd3f025abcd8fc02b (diff) | |
download | mana-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.cpp | 368 |
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); + } +} |