From 3adb0710b9b0262b7d7a03aa687e78c232f04d06 Mon Sep 17 00:00:00 2001 From: Bertram Date: Tue, 23 Feb 2010 22:32:18 +0100 Subject: Sanitized ManaServ movement protocol, by mainly moving code from LocalPlayer to Being. This fixes some movement glitches under ManaServ and make the code much cleaner even if it's not perfect enough yet. First of all, many checks have been gathered in the Being::setDestination() calls. Also, now all path nodes including destination are checked against surrounding tiles to correct the path when necessary. The LocalPlayer::nextTile() still needs to be reviewed and some checks are missing but it's almost done :) --- src/being.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++--- src/being.h | 9 ++++++++ src/localplayer.cpp | 60 ++++++------------------------------------------- 3 files changed, 77 insertions(+), 56 deletions(-) diff --git a/src/being.cpp b/src/being.cpp index 247e193a..ce6c9e1b 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -127,6 +127,48 @@ void Being::setPosition(const Vector &pos) (int)pos.y - getHeight() - mText->getHeight() - 6); } +Position Being::checkNodeOffsets(Position position) +{ + // Pre-computing character's position in tiles + const int tx = position.x / 32; + const int ty = position.y / 32; + + // Pre-computing character's position offsets. + int fx = position.x % 32; + int fy = position.y % 32; + + // Compute the being radius: + // FIXME: the beings' radius should be obtained from xml values + // and stored into the Being ojects. + int radius = getWidth() / 2; + // FIXME: Hande beings with more than 1/2 tile radius by not letting them + // go or spawn in too narrow places. The server will have to be aware + // of being's radius value (in tiles) to handle this gracefully. + if (radius > 32 / 2) radius = 32 / 2; + // set a default value if no value returned. + if (radius < 1) radius = 32 / 3; + + // Fix coordinates so that the player does not seem to dig into walls. + if (fx > (32 - radius) && !mMap->getWalk(tx + 1, ty, getWalkMask())) + fx = 32 - radius; + else if (fx < radius && !mMap->getWalk(tx - 1, ty, getWalkMask())) + fx = radius; + else if (fy > (32 - radius) && !mMap->getWalk(tx, ty + 1, getWalkMask())) + fy = 32 - radius; + else if (fy < radius && !mMap->getWalk(tx, ty - 1, getWalkMask())) + fy = radius; + + // FIXME: Check also diagonal positions. + + // Test also the current character's position, to avoid the corner case + // where a player can approach an obstacle by walking from slightly + // under, diagonally. First part to the walk on water bug. + //if (offsetY < 16 && !mMap->getWalk(posX, posY - 1, getWalkMask())) + //fy = 16; + + return Position(tx * 32 + fx, ty * 32 + fy); +} + void Being::setDestination(int dstX, int dstY) { if (Net::getNetworkType() == ServerInfo::EATHENA) @@ -136,12 +178,22 @@ void Being::setDestination(int dstX, int dstY) return; } - mDest.x = dstX; - mDest.y = dstY; + // Check the walkability of the destination: + // If the destination is unwalkable, + // don't bother finding a path or set a destination. + if (!mMap->getWalk(dstX / 32, dstY / 32)) + return; + + // We check the destination in order to handle + // surrounding blocking tiles gracefully... + Position dest = checkNodeOffsets(dstX, dstY); + mDest.x = dest.x; + mDest.y = dest.y; int srcX = mPos.x; int srcY = mPos.y; - Path thisPath; + // We initialize an empty path... + Path thisPath = Path(); if (mMap) { @@ -175,6 +227,12 @@ void Being::setDestination(int dstX, int dstY) { it->x = (it->x * 32) + startX + (changeX * i); it->y = (it->y * 32) + startY + (changeY * i); + + // We check each path node and correct the + // tile position's offsets whenever needed. + Position pos = checkNodeOffsets(*it); + it->x = pos.x; + it->y = pos.y; i++; it++; } diff --git a/src/being.h b/src/being.h index 3b63b02d..5140717c 100644 --- a/src/being.h +++ b/src/being.h @@ -647,6 +647,15 @@ class Being : public Sprite, public ConfigListener Vector mDest; /**< destination coordinates. */ + /** + * Check the current position against surrounding + * blocking tiles, and correct the position offset within + * tile when needed. + */ + Position checkNodeOffsets(Position position); + Position checkNodeOffsets(int x, int y) + { return checkNodeOffsets(Position(x, y)); } + private: /** diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 6f63d799..d7f64113 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -462,63 +462,17 @@ void LocalPlayer::setTarget(Being *target) void LocalPlayer::setDestination(int x, int y) { - if (Net::getNetworkType() == ServerInfo::MANASERV) - { - // Pre-computing character's destination in tiles - const int tx = x / 32; - const int ty = y / 32; - - // Check the walkability of the destination - // If the destination is a wall, don't go there! - if (!mMap->getWalk(tx, ty)) - return; - - // Pre-computing character's position useful variables. - Vector playerPosition = getPosition(); - const int posX = (int)(playerPosition.x / 32); - const int posY = (int)(playerPosition.y / 32); - const int offsetY = (int)playerPosition.y % 32; - - // check if we're finding a path to the seeked destination - // If the path is empty... and isn't on the same tile, - // then, it's an unvalid one. - if (posX != tx || posY != ty) - { - Path evaluatedPath = mMap->findPath(posX, posY, tx, ty, - getWalkMask()); - if (evaluatedPath.empty()) - return; - } - - // Pre-computing character's destination offsets. - int fx = x % 32; - int fy = y % 32; - - // Fix coordinates so that the player does not seem to dig into walls. - if (fx > 16 && !mMap->getWalk(tx + 1, ty, getWalkMask())) - fx = 16; - else if (fx < 16 && !mMap->getWalk(tx - 1, ty, getWalkMask())) - fx = 16; - else if (fy > 16 && !mMap->getWalk(tx, ty + 1, getWalkMask())) - fy = 16; - else if (fy < 16 && !mMap->getWalk(tx, ty - 1, getWalkMask())) - fy = 16; - - // Test also the current character's position, to avoid the corner case - // where a player can approach an obstacle by walking from slightly - // under, diagonally. First part to the walk on water bug. - if (offsetY < 16 && !mMap->getWalk(posX, posY - 1, getWalkMask())) - fy = 16; - - x = tx * 32 + fx; - y = ty * 32 + fy; - } - // Only send a new message to the server when destination changes if (x != mDest.x || y != mDest.y) { Being::setDestination(x, y); - Net::getPlayerHandler()->setDestination(x, y, mDirection); + + // Manaserv: + // If the destination given to being class is accepted, + // we inform the Server. + if ((x == mDest.x && y == mDest.y) + || Net::getNetworkType() == ServerInfo::EATHENA) + Net::getPlayerHandler()->setDestination(x, y, mDirection); } mPickUpTarget = NULL; -- cgit v1.2.3-70-g09d2