void Pathfinding::calculate(BattleUnit *unit, Position endPosition) { Position startPosition = unit->getPosition(); _movementType = unit->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(); // look for a possible fast and accurate bresenham path and skip A* if (startPosition.z == endPosition.z && bresenhamPath(startPosition,endPosition)) { std::reverse(_path.begin(), _path.end()); //paths are stored in reverse order return; } // Now try through A*. aStarPath(startPosition, endPosition); }
/** * Get's the TU cost to move from 1 tile to the other(ONE STEP ONLY). But also updates the endPosition, because it is possible * the unit goes upstairs or falls down while walking. * @param startPosition * @param direction * @param endPosition pointer * @param unit * @return TU cost - 255 if movement impossible */ int Pathfinding::getTUCost(const Position &startPosition, int direction, Position *endPosition, BattleUnit *unit) { _unit = unit; directionToVector(direction, endPosition); *endPosition += startPosition; bool fellDown = false; bool triedStairs = false; int size = _unit->getArmor()->getSize() - 1; int cost = 0; int numberOfPartsChangingLevel = 0; for (int x = size; x >= 0; x--) { for (int y = size; y >= 0; y--) { Tile *startTile = _save->getTile(startPosition + Position(x,y,0)); Tile *destinationTile = _save->getTile(*endPosition + Position(x,y,0)); cost = 0; // this means the destination is probably outside the map if (!destinationTile) return 255; // check if the destination tile can be walked over if (isBlocked(destinationTile, MapData::O_FLOOR) || isBlocked(destinationTile, MapData::O_OBJECT)) return 255; // can't walk on top of other units if (_save->getTile(*endPosition + Position(x,y,-1)) && _save->getTile(*endPosition + Position(x,y,-1))->getUnit() && _save->getTile(*endPosition + Position(x,y,-1))->getUnit() != _unit && _movementType != MT_FLY) return 255; if (direction < DIR_UP) { // check if we can go this way if (isBlocked(startTile, destinationTile, direction)) return 255; if (startTile->getTerrainLevel() - destinationTile->getTerrainLevel() > 8 && x==0 && y==0) return 255; } else { // check if we can go up or down through gravlift or fly if (validateUpDown(unit, startPosition, direction)) { cost = 8; // vertical movement by flying suit or grav lift } else { return 255; } } // if we are on a stairs try to go up a level if (direction < DIR_UP && startTile->getTerrainLevel() < -12 && x==0 && y==0) { endPosition->z++; destinationTile = _save->getTile(*endPosition); triedStairs = true; } // this means the destination is probably outside the map if (!destinationTile) return 255; int wallcost = 0; // walking through rubble walls if (direction == 7 || direction == 0 || direction == 1) wallcost += startTile->getTUCost(MapData::O_NORTHWALL, _movementType); if (direction == 1 || direction == 2 || direction == 3) wallcost += destinationTile->getTUCost(MapData::O_WESTWALL, _movementType); if (direction == 3 || direction == 4 || direction == 5) wallcost += destinationTile->getTUCost(MapData::O_NORTHWALL, _movementType); if (direction == 5 || direction == 6 || direction == 7) wallcost += startTile->getTUCost(MapData::O_WESTWALL, _movementType); // check if we have floor, else fall down while (canFallDown(destinationTile) && (_movementType != MT_FLY || triedStairs) && x==0 && y==0) { endPosition->z--; destinationTile = _save->getTile(*endPosition); fellDown = true; numberOfPartsChangingLevel++; } // if we don't want to fall down and there is no floor, we can't know the TUs so it's default to 4 if (direction < DIR_UP && !fellDown && destinationTile->hasNoFloor() && x==0 && y==0) { cost = 4; } // check if the destination tile can be walked over if ((isBlocked(destinationTile, MapData::O_FLOOR) || isBlocked(destinationTile, MapData::O_OBJECT)) && !fellDown) { return 255; } // calculate the cost by adding floor walk cost and object walk cost if (direction < DIR_UP) { cost += destinationTile->getTUCost(MapData::O_FLOOR, _movementType); if (!fellDown) { cost += destinationTile->getTUCost(MapData::O_OBJECT, _movementType); } } // diagonal walking (uneven directions) costs 50% more tu's if (direction < DIR_UP && direction & 1) { wallcost /= 2; cost = (int)((double)cost * 1.5); } cost += wallcost; if (startTile->getTerrainLevel() != destinationTile->getTerrainLevel()) { numberOfPartsChangingLevel++; } } } // for bigger sized units, check the path between part 1,1 and part 0,0 at end position if (size) { Tile *startTile = _save->getTile(*endPosition + Position(1,1,0)); Tile *destinationTile = _save->getTile(*endPosition); int tmpDirection = 7; if (isBlocked(startTile, destinationTile, tmpDirection)) return 255; // also check if we change level, that there are two parts changing level, // so a big sized unit can not go up a small sized stairs if (numberOfPartsChangingLevel == 1) return 255; } return cost; }
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(); } }
/** * Get's the TU cost to move from 1 tile to the other(ONE STEP ONLY). But also updates the endPosition, because it is possible * the unit goes upstairs or falls down while walking. * @param startPosition * @param direction * @param endPosition pointer * @param unit * @return TU cost - 255 if movement impossible */ int Pathfinding::getTUCost(const Position &startPosition, int direction, Position *endPosition, BattleUnit *unit) { _unit = unit; directionToVector(direction, endPosition); *endPosition += startPosition; bool fellDown = false; Tile *startTile = _save->getTile(startPosition); Tile *destinationTile = _save->getTile(*endPosition); // this means the destination is probably outside the map if (!destinationTile) return 255; // check if the destination tile can be walked over if (isBlocked(destinationTile, MapData::O_FLOOR) || isBlocked(destinationTile, MapData::O_OBJECT)) return 255; // check if we can go this way if (isBlocked(startTile, destinationTile, direction)) return 255; // if we are on a stairs try to go up a level if (startTile->getTerrainLevel() < -12) { endPosition->z++; destinationTile = _save->getTile(*endPosition); } // this means the destination is probably outside the map if (!destinationTile) return 255; // check if we have floor, else fall down while (canFallDown(destinationTile)) { endPosition->z--; destinationTile = _save->getTile(*endPosition); fellDown = true; } // if we don't want to fall down and there is no floor, it ends here if (!fellDown && destinationTile->hasNoFloor()) { return 255; } // check if the destination tile can be walked over if ((isBlocked(destinationTile, MapData::O_FLOOR) || isBlocked(destinationTile, MapData::O_OBJECT)) && !fellDown) { return 255; } // calculate the cost by adding floor walk cost and object walk cost int cost = destinationTile->getTUCost(MapData::O_FLOOR, _movementType); if (!fellDown) { cost += destinationTile->getTUCost(MapData::O_OBJECT, _movementType); } // diagonal walking (uneven directions) costs 50% more tu's if (direction & 1) { cost = (int)((double)cost * 1.5); } return cost; }
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(); } }
/** * Get's the TU cost to move from 1 tile to the other(ONE STEP ONLY). But also updates the endPosition, because it is possible * the unit goes upstairs or falls down while walking. * @param startPosition * @param direction * @param endPosition pointer * @param unit * @return TU cost - 255 if movement impossible */ int Pathfinding::getTUCost(const Position &startPosition, int direction, Position *endPosition, BattleUnit *unit) { _unit = unit; directionToVector(direction, endPosition); *endPosition += startPosition; bool fellDown = false; bool triedStairs = false; int size = _unit->getUnit()->getArmor()->getSize() - 1; int cost = 0; for (int x = size; x >= 0; x--) { for (int y = size; y >= 0; y--) { Tile *startTile = _save->getTile(startPosition + Position(x,y,0)); Tile *destinationTile = _save->getTile(*endPosition + Position(x,y,0)); cost = 0; // this means the destination is probably outside the map if (!destinationTile) return 255; // check if the destination tile can be walked over if (isBlocked(destinationTile, MapData::O_FLOOR) || isBlocked(destinationTile, MapData::O_OBJECT)) return 255; if (direction < DIR_UP) { // check if we can go this way if (isBlocked(startTile, destinationTile, direction)) return 255; if (startTile->getTerrainLevel() - destinationTile->getTerrainLevel() > 8 && x==0 && y==0) return 255; } else { // check if we can go up or down through gravlift or fly if (validateUpDown(unit, startPosition, direction)) { cost += 4; // vertical movement } else { return 255; } } // if we are on a stairs try to go up a level if (startTile->getTerrainLevel() < -12 && x==0 && y==0) { endPosition->z++; destinationTile = _save->getTile(*endPosition); triedStairs = true; } // this means the destination is probably outside the map if (!destinationTile) return 255; // check if we have floor, else fall down while (canFallDown(destinationTile) && (_movementType != MT_FLY || triedStairs) && x==0 && y==0) { endPosition->z--; destinationTile = _save->getTile(*endPosition); fellDown = true; } // if we don't want to fall down and there is no floor, it ends here if (!fellDown && destinationTile->hasNoFloor() && x==0 && y==0) { if (_movementType != MT_FLY) return 255; else cost = 4; } // check if the destination tile can be walked over if ((isBlocked(destinationTile, MapData::O_FLOOR) || isBlocked(destinationTile, MapData::O_OBJECT)) && !fellDown) { return 255; } // calculate the cost by adding floor walk cost and object walk cost cost += destinationTile->getTUCost(MapData::O_FLOOR, _movementType); if (!fellDown) { cost += destinationTile->getTUCost(MapData::O_OBJECT, _movementType); } // diagonal walking (uneven directions) costs 50% more tu's if (direction & 1) { cost = (int)((double)cost * 1.5); } } } // for bigger sized units, check the path between part 1,1 and part 0,0 at end position if (size) { Tile *startTile = _save->getTile(*endPosition + Position(1,1,0)); Tile *destinationTile = _save->getTile(*endPosition); int tmpDirection = 7; if (isBlocked(startTile, destinationTile, tmpDirection)) return 255; } return cost; }