summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/being.cpp157
-rw-r--r--src/being.h15
-rw-r--r--src/localplayer.h2
-rw-r--r--src/net/beinghandler.cpp15
4 files changed, 167 insertions, 22 deletions
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);
+ }
}
}