コード例 #1
0
ファイル: Pathfinding.cpp プロジェクト: Munkeylord/OpenXcom
/*
* 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;
}
コード例 #2
0
ファイル: Pathfinding.cpp プロジェクト: Munkeylord/OpenXcom
/**
 * 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 &currentPos = 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;
}
コード例 #3
0
ファイル: Pathfinding.cpp プロジェクト: Munkeylord/OpenXcom
/**
 * 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 &currentPos = 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;
}
コード例 #4
0
ファイル: Pathfinding.cpp プロジェクト: Munkeylord/OpenXcom
// 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;
}
コード例 #5
0
ファイル: Pathfinding.cpp プロジェクト: Devanon/OpenXcom
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();
    }

}
コード例 #6
0
ファイル: Pathfinding.cpp プロジェクト: yoyko/OpenXcom
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();
	}

}