real autowalking
* smart autowalking using A* path finding algorithm
This commit is contained in:
parent
8bc63e25df
commit
fe86dc8050
|
@ -63,32 +63,11 @@ function UIGameMap:onMouseRelease(mousePosition, mouseButton)
|
|||
if GameInterface.processMouseAction(mousePosition, mouseButton, nil, tile:getTopLookThing(), tile:getTopUseThing(), tile:getTopCreature(), tile:getTopMultiUseThing()) then
|
||||
return true
|
||||
elseif mouseButton == MouseLeftButton then
|
||||
local fromPos = g_game.getLocalPlayer():getPosition()
|
||||
local toPos = tile:getPosition()
|
||||
if fromPos.z ~= toPos.z then
|
||||
local dirs = g_map.findPath(g_game.getLocalPlayer():getPosition(), tile:getPosition(), 255)
|
||||
if #dirs == 0 then
|
||||
TextMessage.displayStatus('There is no way.')
|
||||
return true
|
||||
end
|
||||
|
||||
-- simple and stupid pathfinding algorithm
|
||||
local dirs = {}
|
||||
local pathPos = fromPos
|
||||
while pathPos.x ~= toPos.x or pathPos.y ~= toPos.y do
|
||||
if pathPos.x < toPos.x then
|
||||
pathPos.x = pathPos.x + 1
|
||||
table.insert(dirs, East)
|
||||
elseif pathPos.x > toPos.x then
|
||||
pathPos.x = pathPos.x - 1
|
||||
table.insert(dirs, West)
|
||||
elseif pathPos.y < toPos.y then
|
||||
pathPos.y = pathPos.y + 1
|
||||
table.insert(dirs, South)
|
||||
else --if pathPos.y > toPos.y then
|
||||
pathPos.y = pathPos.y - 1
|
||||
table.insert(dirs, North)
|
||||
end
|
||||
end
|
||||
|
||||
g_game.autoWalk(dirs)
|
||||
return true
|
||||
end
|
||||
|
|
|
@ -372,15 +372,18 @@ void Game::walk(Otc::Direction direction)
|
|||
forceWalk(direction);
|
||||
}
|
||||
|
||||
void Game::autoWalk(const std::vector<Otc::Direction>& dir)
|
||||
void Game::autoWalk(const std::vector<Otc::Direction>& dirs)
|
||||
{
|
||||
if(!canPerformGameAction())
|
||||
return;
|
||||
|
||||
if(dirs.size() >= 255)
|
||||
return;
|
||||
|
||||
if(isFollowing())
|
||||
cancelFollow();
|
||||
|
||||
m_protocolGame->sendAutoWalk(dir);
|
||||
m_protocolGame->sendAutoWalk(dirs);
|
||||
}
|
||||
|
||||
void Game::forceWalk(Otc::Direction direction)
|
||||
|
|
|
@ -123,7 +123,7 @@ public:
|
|||
|
||||
// walk related
|
||||
void walk(Otc::Direction direction);
|
||||
void autoWalk(const std::vector<Otc::Direction>& dir);
|
||||
void autoWalk(const std::vector<Otc::Direction>& dirs);
|
||||
void forceWalk(Otc::Direction direction);
|
||||
void turn(Otc::Direction direction);
|
||||
void stop();
|
||||
|
|
|
@ -411,3 +411,111 @@ int Map::getLastAwareFloor()
|
|||
else
|
||||
return Otc::SEA_FLOOR;
|
||||
}
|
||||
|
||||
// pathfinding using A* search algorithm
|
||||
// as described in http://en.wikipedia.org/wiki/A*_search_algorithm
|
||||
std::vector<Otc::Direction> Map::findPath(const Position& startPos, const Position& goalPos, int maxSteps)
|
||||
{
|
||||
struct Node {
|
||||
Node(const Position& pos) : cost(0), totalCost(0), steps(0), pos(pos), prev(nullptr), dir(Otc::InvalidDirection), evaluated(false) { }
|
||||
bool operator<(const Node& other) const { return totalCost < other.totalCost; }
|
||||
float cost;
|
||||
float totalCost;
|
||||
int steps;
|
||||
Position pos;
|
||||
Node *prev;
|
||||
Otc::Direction dir;
|
||||
bool evaluated;
|
||||
};
|
||||
|
||||
struct LessNode : std::binary_function<Node*, Node*, bool> {
|
||||
bool operator()(Node* a, Node* b) const {
|
||||
return b->totalCost < a->totalCost;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<Otc::Direction> dirs;
|
||||
|
||||
if(startPos == goalPos || startPos.z != goalPos.z || startPos.distance(goalPos) > maxSteps) {
|
||||
return dirs;
|
||||
}
|
||||
|
||||
auto estimateCost = [=](const Position& pos) { return pos.distance(goalPos); };
|
||||
std::unordered_map<Position, Node*, PositionHasher> nodes;
|
||||
std::priority_queue<Node*, std::vector<Node*>, LessNode> searchList;
|
||||
|
||||
Node *currentNode = new Node(startPos);
|
||||
currentNode->pos = startPos;
|
||||
Node *foundNode = nullptr;
|
||||
while(currentNode && currentNode->steps < maxSteps) {
|
||||
if(currentNode->pos == goalPos && (!foundNode || currentNode->cost < foundNode->cost))
|
||||
foundNode = currentNode;
|
||||
|
||||
if(foundNode && currentNode->totalCost >= foundNode->cost)
|
||||
break;
|
||||
|
||||
for(int i=-1;i<=1;++i) {
|
||||
for(int j=-1;j<=1;++j) {
|
||||
if(i == 0 && j == 0)
|
||||
continue;
|
||||
|
||||
Position neighborPos = currentNode->pos.translated(i, j);
|
||||
const TilePtr& tile = getTile(neighborPos);
|
||||
if(!tile || !tile->isWalkable())
|
||||
continue;
|
||||
|
||||
float walkFactor;
|
||||
Otc::Direction walkDir = currentNode->pos.getDirectionFromPosition(neighborPos);
|
||||
if(walkDir >= Otc::NorthEast)
|
||||
walkFactor = 3.0f;
|
||||
else
|
||||
walkFactor = 1.0f;
|
||||
|
||||
float cost = currentNode->cost + (tile->getGroundSpeed() * walkFactor) / 100.0f;
|
||||
|
||||
Node *neighborNode;
|
||||
if(nodes.find(neighborPos) == nodes.end()) {
|
||||
neighborNode = new Node(neighborPos);
|
||||
nodes[neighborPos] = neighborNode;
|
||||
} else {
|
||||
neighborNode = nodes[neighborPos];
|
||||
if(neighborNode->cost < cost)
|
||||
continue;
|
||||
}
|
||||
|
||||
neighborNode->prev = currentNode;
|
||||
neighborNode->cost = cost;
|
||||
neighborNode->steps = currentNode->steps + 1;
|
||||
neighborNode->totalCost = neighborNode->cost + estimateCost(neighborPos);
|
||||
neighborNode->dir = walkDir;
|
||||
neighborNode->evaluated = false;
|
||||
searchList.push(neighborNode);
|
||||
}
|
||||
}
|
||||
|
||||
currentNode->evaluated = true;
|
||||
currentNode = nullptr;
|
||||
while(searchList.size() > 0 && !currentNode) {
|
||||
Node *node = searchList.top();
|
||||
searchList.pop();
|
||||
|
||||
if(!node->evaluated)
|
||||
currentNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
if(foundNode) {
|
||||
currentNode = foundNode;
|
||||
while(currentNode) {
|
||||
dirs.push_back(currentNode->dir);
|
||||
currentNode = currentNode->prev;
|
||||
}
|
||||
dirs.pop_back();
|
||||
std::reverse(dirs.begin(), dirs.end());
|
||||
}
|
||||
|
||||
for(auto it : nodes)
|
||||
delete it.second;
|
||||
|
||||
return dirs;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,8 @@ public:
|
|||
std::vector<AnimatedTextPtr> getAnimatedTexts() { return m_animatedTexts; }
|
||||
std::vector<StaticTextPtr> getStaticTexts() { return m_staticTexts; }
|
||||
|
||||
std::vector<Otc::Direction> findPath(const Position& start, const Position& goal, int maxSteps);
|
||||
|
||||
private:
|
||||
std::unordered_map<Position, TilePtr, PositionHasher> m_tiles;
|
||||
std::map<uint32, CreaturePtr> m_knownCreatures;
|
||||
|
|
|
@ -425,6 +425,7 @@ void MapView::setVisibleDimension(const Size& visibleDimension)
|
|||
}
|
||||
|
||||
Point virtualCenterOffset = (drawDimension/2 - Size(1,1)).toPoint();
|
||||
Point visibleCenterOffset = virtualCenterOffset;
|
||||
|
||||
ViewRange viewRange;
|
||||
if(tileSize >= 32 && visibleDimension.area() <= NEAR_VIEW_AREA)
|
||||
|
@ -446,6 +447,7 @@ void MapView::setVisibleDimension(const Size& visibleDimension)
|
|||
bool mustUpdate = (m_drawDimension != drawDimension ||
|
||||
m_viewRange != viewRange ||
|
||||
m_virtualCenterOffset != virtualCenterOffset ||
|
||||
m_visibleCenterOffset != visibleCenterOffset ||
|
||||
m_tileSize != tileSize);
|
||||
|
||||
m_visibleDimension = visibleDimension;
|
||||
|
@ -453,6 +455,7 @@ void MapView::setVisibleDimension(const Size& visibleDimension)
|
|||
m_tileSize = tileSize;
|
||||
m_viewRange = viewRange;
|
||||
m_virtualCenterOffset = virtualCenterOffset;
|
||||
m_visibleCenterOffset = visibleCenterOffset;
|
||||
|
||||
if(mustUpdate)
|
||||
requestVisibleTilesCacheUpdate();
|
||||
|
@ -550,7 +553,7 @@ TilePtr MapView::getTile(const Point& mousePos, const Rect& mapRect)
|
|||
tilePos2D += m_followingCreature->getWalkOffset() * scaleFactor;
|
||||
tilePos2D /= m_tileSize;
|
||||
|
||||
Position tilePos = Position(1 + (int)tilePos2D.x - m_virtualCenterOffset.x, 1 + (int)tilePos2D.y - m_virtualCenterOffset.y, 0) + cameraPosition;
|
||||
Position tilePos = Position(1 + (int)tilePos2D.x - m_visibleCenterOffset.x, 1 + (int)tilePos2D.y - m_visibleCenterOffset.y, 0) + cameraPosition;
|
||||
if(!tilePos.isValid())
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ private:
|
|||
Size m_drawDimension;
|
||||
Size m_visibleDimension;
|
||||
Point m_virtualCenterOffset;
|
||||
Point m_visibleCenterOffset;
|
||||
Position m_customCameraPosition;
|
||||
Boolean<true> m_mustUpdateVisibleTilesCache;
|
||||
Boolean<true> m_mustDrawVisibleTilesCache;
|
||||
|
|
|
@ -75,6 +75,7 @@ void OTClient::registerLuaFunctions()
|
|||
g_lua.bindClassStaticFunction("g_map", "getCreatureById", std::bind(&Map::getCreatureById, &g_map, _1));
|
||||
g_lua.bindClassStaticFunction("g_map", "removeCreatureById", std::bind(&Map::removeCreatureById, &g_map, _1));
|
||||
g_lua.bindClassStaticFunction("g_map", "getSpectators", std::bind(&Map::getSpectators, &g_map, _1, _2));
|
||||
g_lua.bindClassStaticFunction("g_map", "findPath", std::bind(&Map::findPath, &g_map, _1, _2, _3));
|
||||
|
||||
g_lua.registerStaticClass("g_game");
|
||||
g_lua.bindClassStaticFunction("g_game", "loginWorld", std::bind(&Game::loginWorld, &g_game, _1, _2, _3, _4, _5));
|
||||
|
|
|
@ -151,6 +151,8 @@ public:
|
|||
}
|
||||
|
||||
bool isValid() const { return !(x == 65535 && y == 65535 && z == 255); }
|
||||
float distance(const Position& pos) const { return sqrt(pow((pos.x - x), 2) + pow((pos.y - y), 2)); }
|
||||
int manhattanDistance(const Position& pos) const { return std::abs(pos.x - x) + std::abs(pos.y - y); }
|
||||
|
||||
void translate(int dx, int dy, short dz = 0) { x += dx; y += dy; z += dz; }
|
||||
Position translated(int dx, int dy, short dz = 0) const { Position pos = *this; pos.x += dx; pos.y += dy; pos.z += dz; return pos; }
|
||||
|
|
Loading…
Reference in New Issue