/* * Preview path, marks tiles. */ bool Pathfinding::previewPath(bool bRemove) { if (_path.empty()) return false; if (!bRemove && _pathPreviewed) return false; _pathPreviewed = !bRemove; Position pos = _unit->getPosition(); Position destination; int tus = _unit->getTimeUnits(); int size = _unit->getArmor()->getSize() - 1; for (std::vector<int>::reverse_iterator i = _path.rbegin(); i != _path.rend(); ++i) { int dir = *i; int tu = getTUCost(pos, dir, &destination, _unit); // gets tu cost, but also gets the destination position. tus -= tu; pos = destination; for (int x = size; x >= 0; x--) { for (int y = size; y >= 0; y--) { _save->getTile(pos + Position(x,y,0))->setMarkerColor(bRemove?0:(tus>0?4:3)); } } } return true; }
/** * Calculate the shortest path using a simple A-Star algorithm. * The unit information and movement type must have already been set. * The path information is set only if a valid path is found. * @param startPosition The position to start from. * @param endPosition The position we want to reach. * @return True if a path exists, false otherwise. */ bool Pathfinding::aStarPath(const Position &startPosition, const Position &endPosition) { // reset every node, so we have to check them all for (std::vector<PathfindingNode>::iterator it = _nodes.begin(); it != _nodes.end(); ++it) it->reset(); // start position is the first one in our "open" list PathfindingNode *start = getNode(startPosition); start->connect(0, 0, 0, endPosition); PathfindingOpenSet openList; openList.push(start); // if the open list is empty, we've reached the end while(!openList.empty()) { PathfindingNode *currentNode = openList.pop(); Position const ¤tPos = currentNode->getPosition(); currentNode->setChecked(); if (currentPos == endPosition) // We found our target. { _path.clear(); PathfindingNode *pf = currentNode; while (pf->getPrevNode()) { _path.push_back(pf->getPrevDir()); pf = pf->getPrevNode(); } return true; } // Try all reachable neighbours. for (int direction = 0; direction < 10; direction++) { Position nextPos; int tuCost = getTUCost(currentPos, direction, &nextPos, _unit); if (tuCost == 255) // Skip unreachable / blocked continue; PathfindingNode *nextNode = getNode(nextPos); if (nextNode->isChecked()) // Our algorithm means this node is already at minimum cost. continue; int totalTuCost = currentNode->getTUCost() + tuCost; // If this node is unvisited or visited from a better path. if (!nextNode->inOpenSet() || nextNode->getTUCost() > totalTuCost) { nextNode->connect(totalTuCost, currentNode, direction, endPosition); openList.push(nextNode); } } } // Unble to reach the target return false; }
/** * Use Dijkstra's algorithm to locate all tiles reachable to @a *unit with a TU cost no more than @a tuMax. * @param unit Pointer to the unit. * @param tuMax The maximum cost of the path to each tile. * @return An array of reachable tiles, sorted in ascending order of cost. The first tile is the start location. */ std::vector<int> Pathfinding::findReachable(BattleUnit *unit, int tuMax) { const Position &start = unit->getPosition(); for (std::vector<PathfindingNode>::iterator it = _nodes.begin(); it != _nodes.end(); ++it) { it->reset(); } PathfindingNode *startNode = getNode(start); startNode->connect(0, 0, 0); PathfindingOpenSet unvisited; unvisited.push(startNode); std::vector<PathfindingNode*> reachable; while (!unvisited.empty()) { PathfindingNode *currentNode = unvisited.pop(); Position const ¤tPos = currentNode->getPosition(); // Try all reachable neighbours. for (int direction = 0; direction < 10; direction++) { Position nextPos; int tuCost = getTUCost(currentPos, direction, &nextPos, unit); if (tuCost == 255) // Skip unreachable / blocked continue; if (tuCost > tuMax) // Run out of TUs continue; PathfindingNode *nextNode = getNode(nextPos); if (nextNode->isChecked()) // Our algorithm means this node is already at minimum cost. continue; int totalTuCost = currentNode->getTUCost() + tuCost; // If this node is unvisited or visited from a better path. if (!nextNode->inOpenSet() || nextNode->getTUCost() > totalTuCost) { nextNode->connect(totalTuCost, currentNode, direction); unvisited.push(nextNode); } } currentNode->setChecked(); reachable.push_back(currentNode); } std::sort(reachable.begin(), reachable.end(), MinNodeCosts()); std::vector<int> tiles; tiles.reserve(reachable.size()); for (std::vector<PathfindingNode*>::const_iterator it = reachable.begin(); it != reachable.end(); ++it) { tiles.push_back(_save->getTileIndex((*it)->getPosition())); } return tiles; }
// this works in only x/y plane bool Pathfinding::bresenhamPath(const Position& origin, const Position& target) { int xd[8] = {0, 1, 1, 1, 0, -1, -1, -1}; int yd[8] = {-1, -1, 0, 1, 1, 1, 0, -1}; int x, x0, x1, delta_x, step_x; int y, y0, y1, delta_y, step_y; int z, z0, z1, delta_z, step_z; int swap_xy, swap_xz; int drift_xy, drift_xz; int cx, cy, cz; Position lastPoint(origin); int dir; int lastTUCost = -1; Position nextPoint; //start and end points x0 = origin.x; x1 = target.x; y0 = origin.y; y1 = target.y; z0 = origin.z; z1 = target.z; //'steep' xy Line, make longest delta x plane swap_xy = abs(y1 - y0) > abs(x1 - x0); if (swap_xy) { std::swap(x0, y0); std::swap(x1, y1); } //do same for xz swap_xz = abs(z1 - z0) > abs(x1 - x0); if (swap_xz) { std::swap(x0, z0); std::swap(x1, z1); } //delta is Length in each plane delta_x = abs(x1 - x0); delta_y = abs(y1 - y0); delta_z = abs(z1 - z0); //drift controls when to step in 'shallow' planes //starting value keeps Line centred drift_xy = (delta_x / 2); drift_xz = (delta_x / 2); //direction of line step_x = 1; if (x0 > x1) { step_x = -1; } step_y = 1; if (y0 > y1) { step_y = -1; } step_z = 1; if (z0 > z1) { step_z = -1; } //starting point y = y0; z = z0; //step through longest delta (which we have swapped to x) for (x = x0; x != (x1+step_x); x += step_x) { //copy position cx = x; cy = y; cz = z; //unswap (in reverse) if (swap_xz) std::swap(cx, cz); if (swap_xy) std::swap(cx, cy); if (x != x0 || y != y0 || z != z0) { Position realNextPoint = Position(cx, cy, cz); nextPoint = realNextPoint; // get direction for (dir = 0; dir < 8; ++dir) { if (xd[dir] == cx-lastPoint.x && yd[dir] == cy-lastPoint.y) break; } int tuCost = getTUCost(lastPoint, dir, &nextPoint, _unit); if (nextPoint == realNextPoint && tuCost < 255 && (tuCost == lastTUCost || (dir&1 && tuCost == lastTUCost*1.5) || (!(dir&1) && tuCost*1.5 == lastTUCost) || lastTUCost == -1) && !isBlocked(_save->getTile(lastPoint), _save->getTile(nextPoint), dir)) { _path.push_back(dir); } else { return false; } lastTUCost = tuCost; lastPoint = Position(cx, cy, cz); } //update progress in other planes drift_xy = drift_xy - delta_y; drift_xz = drift_xz - delta_z; //step in y plane if (drift_xy < 0) { y = y + step_y; drift_xy = drift_xy + delta_x; } //same in z if (drift_xz < 0) { z = z + step_z; drift_xz = drift_xz + delta_x; } } return true; }
void Pathfinding::calculate(BattleUnit *unit, Position &endPosition) { std::list<PathfindingNode*> openList; Position currentPos, nextPos, startPosition = unit->getPosition(); int tuCost; _movementType = MT_WALK; // should be parameter _unit = unit; Tile *destinationTile = _save->getTile(endPosition); // check if destination is not blocked if (isBlocked(destinationTile, MapData::O_FLOOR) || isBlocked(destinationTile, MapData::O_OBJECT)) return; // the following check avoids that the unit walks behind the stairs if we click behind the stairs to make it go up the stairs. // it only works if the unit is on one of the 2 tiles on the stairs, or on the tile right in front of the stairs. if (isOnStairs(startPosition, endPosition)) { endPosition.z++; destinationTile = _save->getTile(endPosition); } // check if we have floor, else lower destination (for non flying units only, because otherwise they never reached this place) while (canFallDown(destinationTile)) { endPosition.z--; destinationTile = _save->getTile(endPosition); } _path.clear(); // reset every node, so we have to check them all for (int i = 0; i < _size; ++i) _nodes[i]->reset(); // start position is the first one in our "open" list openList.push_back(getNode(startPosition)); openList.front()->check(0, 0, 0, 0); // if the open list is empty, we've reached the end while(!openList.empty()) { // this algorithm expands in all directions for (int direction = 0; direction < 8; direction++) { currentPos = openList.front()->getPosition(); tuCost = getTUCost(currentPos, direction, &nextPos, unit); if(tuCost < 255) { if( (!getNode(nextPos)->isChecked() || getNode(nextPos)->getTUCost() > getNode(currentPos)->getTUCost() + tuCost) && (!getNode(endPosition)->isChecked() || getNode(endPosition)->getTUCost() > getNode(currentPos)->getTUCost() + tuCost) ) { getNode(nextPos)->check(getNode(currentPos)->getTUCost() + tuCost, getNode(currentPos)->getStepsNum() + 1, getNode(currentPos), direction); openList.push_back(getNode(nextPos)); } } } openList.pop_front(); } if(!getNode(endPosition)->isChecked()) return; //Backward tracking of the path PathfindingNode* pf = getNode(endPosition); for (int i = getNode(endPosition)->getStepsNum(); i > 0; i--) { _path.push_back(pf->getPrevDir()); pf=pf->getPrevNode(); } }
void Pathfinding::calculate(BattleUnit *unit, Position endPosition) { std::list<PathfindingNode*> openList; PathfindingNode *currentNode, *nextNode; Position currentPos, nextPos, startPosition = unit->getPosition(); int tuCost, totalTuCost = 0; _movementType = unit->getUnit()->getArmor()->getMovementType(); _unit = unit; Tile *destinationTile = _save->getTile(endPosition); // check if destination is not blocked if (isBlocked(destinationTile, MapData::O_FLOOR) || isBlocked(destinationTile, MapData::O_OBJECT)) return; // the following check avoids that the unit walks behind the stairs if we click behind the stairs to make it go up the stairs. // it only works if the unit is on one of the 2 tiles on the stairs, or on the tile right in front of the stairs. if (isOnStairs(startPosition, endPosition)) { endPosition.z++; destinationTile = _save->getTile(endPosition); } // check if we have floor, else lower destination (for non flying units only, because otherwise they never reached this place) while (canFallDown(destinationTile) && _movementType != MT_FLY) { endPosition.z--; destinationTile = _save->getTile(endPosition); } _path.clear(); if (startPosition.z == endPosition.z && bresenhamPath(startPosition, endPosition)) return; _path.clear(); // reset every node, so we have to check them all for (int i = 0; i < _size; ++i) _nodes[i]->reset(); // start position is the first one in our "open" list openList.push_back(getNode(startPosition)); openList.front()->check(0, 0, 0, 0); // if the open list is empty, we've reached the end while(!openList.empty()) { currentPos = openList.front()->getPosition(); currentNode = getNode(currentPos); // this algorithm expands in all directions for (int direction = 0; direction < 10; direction++) { tuCost = getTUCost(currentPos, direction, &nextPos, unit); if(tuCost < 255) // check if we can go to this node (ie is not blocked) { nextNode = getNode(nextPos); totalTuCost = currentNode->getTUCost() + tuCost; // if we haven't checked this node, or the current cost tu cost is lower than our previous path, push this node in the open list to visit later. if( (!nextNode->isChecked() || nextNode->getTUCost() > totalTuCost) && // this will keep pushing back nodes, as long as we did not reach the end position or there are still possible shorter paths (!getNode(endPosition)->isChecked() || getNode(endPosition)->getTUCost() > totalTuCost) ) { nextNode->check(totalTuCost, currentNode->getStepsNum() + 1, currentNode, direction); openList.push_back(nextNode); } } } openList.pop_front(); } if(!getNode(endPosition)->isChecked()) return; //Backward tracking of the path PathfindingNode* pf = getNode(endPosition); for (int i = getNode(endPosition)->getStepsNum(); i > 0; i--) { _path.push_back(pf->getPrevDir()); pf=pf->getPrevNode(); } }