From 21e8d502d07c6cae9580a34dde7587d58e6d3a28 Mon Sep 17 00:00:00 2001 From: Yohann Ferreira Date: Tue, 15 Mar 2011 17:28:16 +0100 Subject: Basically merged the two movement algorithms into one. This was made in favour of the manaserv way of doing things. I also added a way to keep the original server speed value so the pixel value can be recomputed at each map change, as this was necessary since the speed is given before the first map is loaded. The code is much more simpler now about movement handling, and we can already see improvements on other characters movements in The Mana World with this. Everything can't be perfect the first time; here are bugs identified so far: - Monsters direction isn't updated on TmwAthena for obscure reasons. - Remote players walking animation is sometimes reset on each steps. - When changing map, the local player sometimes walks randomly until the player reacts. Stay tuned! --- src/gui/viewport.cpp | 119 +++++++++++++++------------------------------------ 1 file changed, 35 insertions(+), 84 deletions(-) (limited to 'src/gui/viewport.cpp') diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index ac910d5f..459edab5 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -261,43 +261,27 @@ void Viewport::_drawDebugPath(Graphics *graphics) Path debugPath; - if (Net::getNetworkType() == ServerInfo::TMWATHENA) - { - const int mouseTileX = (mMouseX + (int) mPixelViewX) / 32; - const int mouseTileY = (mMouseY + (int) mPixelViewY) / 32; - const Vector &playerPos = player_node->getPosition(); - - debugPath = mMap->findPath( - (int) (playerPos.x - 16) / 32, - (int) (playerPos.y - 32) / 32, - mouseTileX, mouseTileY, 0xFF); - - _drawPath(graphics, debugPath); - } - else if (Net::getNetworkType() == ServerInfo::MANASERV) - { - const Vector &playerPos = player_node->getPosition(); - const int playerRadius = player_node->getCollisionRadius(); - // Draw player collision rectangle - graphics->setColor(gcn::Color(128, 128, 0, 120)); - graphics->fillRectangle( - gcn::Rectangle((int) playerPos.x - (int) mPixelViewX - playerRadius, - (int) playerPos.y - (int) mPixelViewY - playerRadius, - playerRadius * 2, playerRadius * 2)); - - debugPath = mMap->findPixelPath( - (int) playerPos.x, - (int) playerPos.y, - mMouseX + (int) mPixelViewX, - mMouseY + (int) mPixelViewY, - playerRadius, 0xFF); - - // We draw the path proposed by mouse - _drawPath(graphics, debugPath, gcn::Color(128, 0, 128)); - - // But also the one currently walked on. - _drawPath(graphics, player_node->getPath(), gcn::Color(0, 0, 255)); - } + const Vector &playerPos = player_node->getPosition(); + const int playerRadius = player_node->getCollisionRadius(); + // Draw player collision rectangle + graphics->setColor(gcn::Color(128, 128, 0, 120)); + graphics->fillRectangle( + gcn::Rectangle((int) playerPos.x - (int) mPixelViewX - playerRadius, + (int) playerPos.y - (int) mPixelViewY - playerRadius, + playerRadius * 2, playerRadius * 2)); + + debugPath = mMap->findPixelPath( + (int) playerPos.x, + (int) playerPos.y, + mMouseX + (int) mPixelViewX, + mMouseY + (int) mPixelViewY, + playerRadius, 0xFF); + + // We draw the path proposed by mouse + _drawPath(graphics, debugPath, gcn::Color(128, 0, 128)); + + // But also the one currently walked on. + _drawPath(graphics, player_node->getPath(), gcn::Color(0, 0, 255)); } void Viewport::_drawPath(Graphics *graphics, const Path &path, @@ -305,33 +289,16 @@ void Viewport::_drawPath(Graphics *graphics, const Path &path, { graphics->setColor(color); - if (Net::getNetworkType() == ServerInfo::TMWATHENA) + for (Path::const_iterator i = path.begin(); i != path.end(); ++i) { - for (Path::const_iterator i = path.begin(); i != path.end(); ++i) - { - int squareX = i->x * 32 - (int) mPixelViewX + 12; - int squareY = i->y * 32 - (int) mPixelViewY + 12; - - graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); - graphics->drawText( - toString(mMap->getMetaTile(i->x, i->y)->Gcost), - squareX + 4, squareY + 12, gcn::Graphics::CENTER); - } - } - else if (Net::getNetworkType() == ServerInfo::MANASERV) - { - for (Path::const_iterator i = path.begin(); i != path.end(); ++i) - { - int squareX = i->x - (int) mPixelViewX; - int squareY = i->y - (int) mPixelViewY; - - graphics->fillRectangle(gcn::Rectangle(squareX - 4, squareY - 4, - 8, 8)); - graphics->drawText( - toString(mMap->getMetaTile(i->x / 32, i->y / 32)->Gcost), - squareX + 4, squareY + 12, gcn::Graphics::CENTER); - } - + int squareX = i->x - (int) mPixelViewX; + int squareY = i->y - (int) mPixelViewY; + + graphics->fillRectangle(gcn::Rectangle(squareX - 4, squareY - 4, + 8, 8)); + graphics->drawText( + toString(mMap->getMetaTile(i->x / 32, i->y / 32)->Gcost), + squareX + 4, squareY + 12, gcn::Graphics::CENTER); } } @@ -441,25 +408,12 @@ void Viewport::mouseDragged(gcn::MouseEvent &event) if (mPlayerFollowMouse && !event.isShiftPressed()) { - if (Net::getNetworkType() == ServerInfo::MANASERV) + if (get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) { - if (get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) - { - mLocalWalkTime = tick_time; - player_node->setDestination(event.getX() + (int) mPixelViewX, - event.getY() + (int) mPixelViewY); - player_node->pathSetByMouse(); - } - } - else - { - if (mLocalWalkTime != player_node->getActionTime()) - { - mLocalWalkTime = player_node->getActionTime(); - int destX = (event.getX() + mPixelViewX) / mMap->getTileWidth(); - int destY = (event.getY() + mPixelViewY) / mMap->getTileHeight(); - player_node->setDestination(destX, destY); - } + mLocalWalkTime = tick_time; + player_node->setDestination(event.getX() + (int) mPixelViewX, + event.getY() + (int) mPixelViewY); + player_node->pathSetByMouse(); } } } @@ -467,9 +421,6 @@ void Viewport::mouseDragged(gcn::MouseEvent &event) void Viewport::mouseReleased(gcn::MouseEvent &event) { mPlayerFollowMouse = false; - - // Only useful for eAthena but doesn't hurt under ManaServ - mLocalWalkTime = -1; } void Viewport::showPopup(Window *parent, int x, int y, Item *item, -- cgit v1.2.3-70-g09d2 From 452dc7f163749de0b5d698af2f022ca22c1aabb0 Mon Sep 17 00:00:00 2001 From: Yohann Ferreira Date: Thu, 17 Mar 2011 00:02:23 +0100 Subject: Now the client centers the pixel positions when using tA. I made it so that the behaviour can be changed with only a boolean setting in the playerhandler. --- src/being.cpp | 32 ++++++++++++++++++++++++-------- src/gui/viewport.cpp | 39 +++++++++++++++++++++++++++++++++------ src/map.cpp | 34 ++++++++++++++++++++++++++++++++++ src/map.h | 8 ++++++++ src/net/manaserv/playerhandler.h | 3 +++ src/net/playerhandler.h | 6 ++++++ src/net/tmwa/playerhandler.h | 3 +++ 7 files changed, 111 insertions(+), 14 deletions(-) (limited to 'src/gui/viewport.cpp') diff --git a/src/being.cpp b/src/being.cpp index 2c8414d8..6b971dbf 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -198,21 +198,37 @@ void Being::setDestination(int dstX, int dstY) 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(dstX, dstY); + 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; diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index 459edab5..19bed735 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -37,6 +37,7 @@ #include "gui/beingpopup.h" #include "net/net.h" +#include "net/playerhandler.h" #include "resources/resourcemanager.h" @@ -270,12 +271,38 @@ void Viewport::_drawDebugPath(Graphics *graphics) (int) playerPos.y - (int) mPixelViewY - playerRadius, playerRadius * 2, playerRadius * 2)); - debugPath = mMap->findPixelPath( - (int) playerPos.x, - (int) playerPos.y, - mMouseX + (int) mPixelViewX, - mMouseY + (int) mPixelViewY, - playerRadius, 0xFF); + // Prepare the walkmask corresponding to the protocol + unsigned char walkMask = 0; + switch (Net::getNetworkType()) + { + case ServerInfo::TMWATHENA: + walkMask = Map::BLOCKMASK_WALL | Map::BLOCKMASK_CHARACTER; + break; + case ServerInfo::MANASERV: + default: + walkMask = Map::BLOCKMASK_WALL; + break; + } + + // Adapt the path finding to the precision requested + if (Net::getPlayerHandler()->usePixelPrecision()) + { + debugPath = mMap->findPixelPath( + (int) playerPos.x, + (int) playerPos.y, + mMouseX + (int) mPixelViewX, + mMouseY + (int) mPixelViewY, + playerRadius, walkMask); + } + else + { + debugPath = mMap->findTilePath( + (int) playerPos.x, + (int) playerPos.y, + mMouseX + (int) mPixelViewX, + mMouseY + (int) mPixelViewY, + walkMask); + } // We draw the path proposed by mouse _drawPath(graphics, debugPath, gcn::Color(128, 0, 128)); diff --git a/src/map.cpp b/src/map.cpp index 87c902f6..69793299 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -710,6 +710,40 @@ Position Map::checkNodeOffsets(int radius, unsigned char walkMask, return Position(tx * 32 + fx, ty * 32 + fy); } +Path Map::findTilePath(int startPixelX, int startPixelY, int endPixelX, + int endPixelY, unsigned char walkMask, int maxCost) +{ + Path myPath = findPath(startPixelX / mTileWidth, startPixelY / mTileHeight, + endPixelX / mTileWidth, endPixelY / mTileHeight, + walkMask, maxCost); + + // Don't compute empty coordinates. + if (myPath.empty()) + return myPath; + + // Convert the map path to pixels over tiles + // And add interpolation between the starting and ending offsets + Path::iterator it = myPath.begin(); + while (it != myPath.end()) + { + // A position that is valid on the start and end tile is not + // necessarily valid on all the tiles in between, so check the offsets. + *it = Position(it->x * mTileWidth + mTileWidth / 2, + it->y * mTileHeight + mTileHeight / 2); + it++; + } + + // Remove the last path node, as it's more clever to go to the destination. + // It also permit to avoid zigzag at the end of the path, + // especially with mouse. + Position destination((endPixelX / mTileWidth) * mTileWidth + mTileWidth / 2, + (endPixelY / mTileHeight) * mTileHeight + mTileHeight / 2); + myPath.pop_back(); + myPath.push_back(destination); + + return myPath; +} + Path Map::findPixelPath(int startPixelX, int startPixelY, int endPixelX, int endPixelY, int radius, unsigned char walkMask, int maxCost) diff --git a/src/map.h b/src/map.h index 22ca68c4..fae8af50 100644 --- a/src/map.h +++ b/src/map.h @@ -285,6 +285,14 @@ class Map : public Properties int x, int y) const { return checkNodeOffsets(radius, walkMask, Position(x, y)); } + /** + * Find a pixel path from one location to the next. + * Path node are centered on their corresponding tiles in that case. + */ + Path findTilePath(int startPixelX, int startPixelY, int endPixelX, + int endPixelY, unsigned char walkMask, + int maxCost = 20); + /** * Find a pixel path from one location to the next. */ diff --git a/src/net/manaserv/playerhandler.h b/src/net/manaserv/playerhandler.h index 6069c6da..8bba1580 100644 --- a/src/net/manaserv/playerhandler.h +++ b/src/net/manaserv/playerhandler.h @@ -69,6 +69,9 @@ class PlayerHandler : public MessageHandler, public Net::PlayerHandler Vector getPixelsPerTickMoveSpeed(Vector speed, Map *map = 0); + bool usePixelPrecision() + { return true; } + private: void handleMapChangeMessage(Net::MessageIn &msg); }; diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h index 23277062..5d94a093 100644 --- a/src/net/playerhandler.h +++ b/src/net/playerhandler.h @@ -74,6 +74,12 @@ class PlayerHandler * Convert the original speed in pixel per tick for internal use. */ virtual Vector getPixelsPerTickMoveSpeed(Vector speed, Map *map = 0) = 0; + + /** + * Tells whether the client has to use pixel paths. + * Return false when tiles-center positions only are to be used. + */ + virtual bool usePixelPrecision() = 0; }; } // namespace Net diff --git a/src/net/tmwa/playerhandler.h b/src/net/tmwa/playerhandler.h index 4df74350..42eb85f2 100644 --- a/src/net/tmwa/playerhandler.h +++ b/src/net/tmwa/playerhandler.h @@ -61,6 +61,9 @@ class PlayerHandler : public MessageHandler, public Net::PlayerHandler Vector getDefaultMoveSpeed(); Vector getPixelsPerTickMoveSpeed(Vector speed, Map *map = 0); + + bool usePixelPrecision() + { return false; } }; } // namespace TmwAthena -- cgit v1.2.3-70-g09d2