diff options
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | src/being.cpp | 157 | ||||
-rw-r--r-- | src/being.h | 15 | ||||
-rw-r--r-- | src/localplayer.h | 2 | ||||
-rw-r--r-- | src/net/beinghandler.cpp | 15 |
5 files changed, 169 insertions, 22 deletions
@@ -2,6 +2,8 @@ * src/map.cpp: Removed being collisions. Fixed wrong heuristic cost of the pathfinder. + * src/localplayer.h, src/being.cpp, src/net/beinghandler.cpp, + src/being.h: Fitted being trajectories to synchronization messages. 2006-09-02 Bjørn Lindeijer <bjorn@lindeijer.nl> diff --git a/src/being.cpp b/src/being.cpp index a4bee426..982b0ebf 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -23,6 +23,7 @@ #include "being.h" #include <algorithm> +#include <cassert> #include <cmath> #include "animatedsprite.h" @@ -58,6 +59,7 @@ Being::Being(Uint16 id, Uint16 job, Map *map): mSex(2), mWeapon(0), mWalkSpeed(150), + mSpeedModifier(1024), mMap(NULL), mHairStyle(0), mHairColor(0), mSpeechTime(0), @@ -75,34 +77,157 @@ Being::~Being() setMap(NULL); } -void -Being::setDestination(Uint16 destX, Uint16 destY) +void Being::adjustCourse(Uint16 srcX, Uint16 srcY, Uint16 dstX, Uint16 dstY) { - if (!mMap || (mX == destX && mY == destY)) + if (!mMap || (mX == dstX && mY == dstY)) { + setPath(Path()); return; } - Path p; - if (mX / 32 != destX / 32 || mY / 32 != destY / 32) + if (mX / 32 == dstX / 32 && mY / 32 == dstY / 32) { - p = mMap->findPath(mX / 32, mY / 32, destX / 32, destY / 32); - if (p.empty()) + // The being is already on the last tile of the path. + Path p; + p.push_back(PATH_NODE(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); + if (p1.empty()) { - setPath(p); + // No path? Better teleport. + mX = dstX; + mY = dstY; + 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. - p.pop_back(); - for (Path::iterator i = p.begin(), i_end = p.end(); i != i_end; ++i) + p1.pop_back(); + for (Path::iterator i = p1.begin(), i_end = p1.end(); i != i_end; ++i) { - // Set intermediate step to tile centers. + // 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; + } + // Set intermediate steps to tile centers. i->x = i->x * 32 + 16; i->y = i->y * 32 + 16; + ++j; } + p1_length = mMap->getMetaTile(dstX / 32, dstY / 32)->Gcost; + p1_dist[p1_size - 1] = p1_length; } - p.push_back(PATH_NODE(destX, destY)); - setPath(p); + p1.push_back(PATH_NODE(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); + 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? Better teleport. + mX = srcX; + mY = srcY; + 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 + 16; + i->y = i->y * 32 + 16; + } + + // 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(Uint16 srcX, Uint16 srcY) +{ + if (!mPath.empty()) + { + adjustCourse(srcX, srcY, mPath.back().x, mPath.back().y); + } +} + +void +Being::setDestination(Uint16 destX, Uint16 destY) +{ + adjustCourse(mX, mY, destX, destY); } void @@ -112,9 +237,10 @@ Being::clearPath() } void -Being::setPath(const Path &path) +Being::setPath(const Path &path, int mod) { mPath = path; + mSpeedModifier = mod >= 512 ? (mod <= 2048 ? mod : 2048) : 512; // TODO: tune bounds if (mAction != WALK && mAction != DEAD) { @@ -305,7 +431,8 @@ Being::nextStep() mY = node.y; setAction(WALK); mWalkTime += mStepTime / 10; - mStepTime = mWalkSpeed * (int)std::sqrt((double)mStepX * mStepX + (double)mStepY * mStepY) / 32; + mStepTime = mWalkSpeed * (int)std::sqrt((double)mStepX * mStepX + (double)mStepY * mStepY) * + mSpeedModifier / (32 * 1024); } void diff --git a/src/being.h b/src/being.h index b2495a29..d8db9375 100644 --- a/src/being.h +++ b/src/being.h @@ -127,7 +127,17 @@ class Being : public Sprite /** * Sets a new destination for this being to walk to. */ - virtual void setDestination(Uint16 destX, Uint16 destY); + void setDestination(Uint16 destX, Uint16 destY); + + /** + * Adjusts course to expected stat point. + */ + void adjustCourse(Uint16, Uint16); + + /** + * Adjusts course to expected start and end points. + */ + void adjustCourse(Uint16, Uint16, Uint16, Uint16); /** * Puts a "speech balloon" above this being for the specified amount @@ -340,7 +350,7 @@ class Being : public Sprite * Sets the new path for this being. */ void - setPath(const Path &path); + setPath(const Path &path, int mod = 1024); /** * Returns the sprite direction of this being. @@ -352,6 +362,7 @@ class Being : public Sprite Uint8 mSex; /**< Character's gender */ Uint16 mWeapon; /**< Weapon picture id */ Uint16 mWalkSpeed; /**< Walking speed */ + Uint16 mSpeedModifier; /**< Modifier to keep course on sync (1024 = normal speed) */ Map *mMap; /**< Map on which this being resides */ SpriteIterator mSpriteIterator; diff --git a/src/localplayer.h b/src/localplayer.h index 886c3d11..7d5aef87 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -107,7 +107,7 @@ class LocalPlayer : public Player /** * Sets a new destination for this being to walk to. */ - virtual void setDestination(Uint16 x, Uint16 y); + void setDestination(Uint16 x, Uint16 y); void raiseAttribute(Attribute attr); void raiseSkill(Uint16 skillId); diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp index eb5ccb57..2d68dd28 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -469,17 +469,24 @@ void BeingHandler::handleBeingsMoveMessage(MessageIn &msg) { continue; } - bool update = being != player_node; // the local player already knows where he wants to go if (abs(being->mX - sx) + abs(being->mY - sy) > 4 * 32) { - // crude handling of synchronization messages + // Too large a desynchronization. being->mX = sx; being->mY = sy; - update = true; + being->setDestination(dx, dy); } - if (update && (flags & MOVING_DESTINATION)) + else if (!(flags & MOVING_POSITION)) { being->setDestination(dx, dy); } + else if (!(flags & MOVING_DESTINATION)) + { + being->adjustCourse(sx, sy); + } + else + { + being->adjustCourse(sx, sy, dx, dy); + } } } |