Пример #1
0
/**
 * Search for a path to the destination.
 * @return Whether a path has been found.
 */
bool PathSearcher::Search()
{
	this->dest_pos = nullptr;
	while (!open_points.empty()) {
		WalkedDistance wd = *open_points.begin();
		open_points.erase(open_points.begin());

		if (wd.traveled != wd.pos->traveled || wd.estimate != wd.pos->estimate) continue; // Invalid open point.

		/* Reached the destination? */
		const WalkedPosition *wp = wd.pos;
		if (wp->cur_vox == this->dest_vox) {
			this->dest_pos = wp;
			return true;
		}

		/* Add new open points. */
		const Voxel *v = _world.GetVoxel(wp->cur_vox);
		if (v == nullptr) continue; // No voxel at the expected point, don't bother.

		uint8 exits = GetPathExits(v);
		for (TileEdge edge = EDGE_BEGIN; edge < EDGE_COUNT; edge++) {
			if ((exits & (0x11 << edge)) == 0) continue;

			/* There is an outgoing connection, is it also on the world? */
			Point16 dxy = _tile_dxy[edge];
			if (dxy.x < 0 && wp->cur_vox.x == 0) continue;
			if (dxy.x > 0 && wp->cur_vox.x + 1 == _world.GetXSize()) continue;
			if (dxy.y < 0 && wp->cur_vox.y == 0) continue;
			if (dxy.y > 0 && wp->cur_vox.y + 1 == _world.GetYSize()) continue;

			int extra_z = ((exits & (0x10 << edge)) != 0);
			if (wp->cur_vox.z + extra_z < 0 || wp->cur_vox.z + extra_z >= WORLD_Z_SIZE) continue;

			/* Now check the other side, new_z is the voxel where the path should be at the bottom. */
			const Voxel *v2 = _world.GetVoxel(wp->cur_vox + XYZPoint16(dxy.x, dxy.y, extra_z));
			if (v2 == nullptr) continue;

			uint8 other_exits = GetPathExits(v2);
			if ((other_exits & (1 << ((edge + 2) % 4))) == 0) { // No path here, try one voxel below
				extra_z--;
				if (wp->cur_vox.z + extra_z < 0) continue;
				v2 = _world.GetVoxel(wp->cur_vox + XYZPoint16(dxy.x, dxy.y, extra_z));
				if (v2 == nullptr) continue;
				other_exits = GetPathExits(v2);
				if ((other_exits & (0x10 << ((edge + 2) % 4))) == 0) continue;
			}
			/* Add new open point to the path finder. */
			this->AddOpen(wp->cur_vox + XYZPoint16(dxy.x, dxy.y, extra_z), wp->traveled + 1, wp);
		}
	}
	return false;
}
Пример #2
0
/**
 * Walk over a queue path from the given entry edge at the given position.
 * If it leads to a new voxel edge, the provided position and edge is update with the exit point.
 * @param voxel_pos [inout] Start voxel position before the queue path, updated to last voxel position.
 * @param entry Direction used for entry to the path, updated to last edge exit direction.
 * @return Whether a (possibly) new last voxel could be found, \c false means the path leads to nowhere.
 * @note Parameter values may get changed during the call, do not rely on their values except when \c true is returned.
 */
bool TravelQueuePath(XYZPoint16 *voxel_pos, TileEdge *entry)
{
	XYZPoint16 new_pos = *voxel_pos;
	TileEdge edge = *entry;

	/* Check that entry voxel actually exists. */
	if (!IsVoxelstackInsideWorld(new_pos.x, new_pos.y)) return false;

	for (;;) {
		new_pos.x += _tile_dxy[edge].x;
		new_pos.y += _tile_dxy[edge].y;
		if (!IsVoxelstackInsideWorld(new_pos.x, new_pos.y)) return false;

		const Voxel *vx = _world.GetVoxel(new_pos);
		if (vx == nullptr || !HasValidPath(vx)) {
			/* No path here, check the voxel below. */
			if (new_pos.z == 0) return true; // Path ends here.
			new_pos.z--;
			vx = _world.GetVoxel(new_pos);
			if (vx == nullptr || !HasValidPath(vx)) return true; // Path ends here.
		}

		if (new_pos == *voxel_pos) return false; // Cycle detected.

		/* Stop if we found a non-queue path. */
		if (_sprite_manager.GetPathStatus(GetPathType(vx->GetInstanceData())) != PAS_QUEUE_PATH) return true;

		/* At this point:
		 * voxel_pos, edge (and *entry) contain the last valid voxel edge.
		 * new_pos, vx is the next queue path tile position.
		 */

		uint8 exits = GetPathExits(vx);

		/* Check that the new tile can go back to our last tile. */
		uint8 rev_edge = (edge + 2) % 4;
		if (!((exits & (0x01 << rev_edge)) != 0 && new_pos.z == voxel_pos->z) &&
				!((exits & (0x10 << rev_edge)) != 0 && new_pos.z == voxel_pos->z - 1)) {
			return false;
		}

		/* Find exit to the next path tile. */
		for (edge = EDGE_BEGIN; edge < EDGE_COUNT; edge++) {
			if (edge == rev_edge) continue; // Skip the direction we came from.
			if ((exits & (0x01 << edge)) != 0) break;
			if ((exits & (0x10 << edge)) != 0) {
				new_pos.z++;
				break;
			}
		}

		if (edge == EDGE_COUNT) return false; // Queue path doesn't have a second exit.

		*voxel_pos = new_pos;
		*entry = edge;
	}
}
Пример #3
0
/**
 * Find all edges that are an exit for a path in the given voxel. No investigation is performed whether the exits connect to anything.
 * @param v %Voxel to investigate.
 * @return Exits for a path in the queried voxel. Lower 4 bits are exits at the bottom; upper 4 bits are exits at the top.
 */
uint8 GetPathExits(const Voxel *v)
{
	SmallRideInstance instance = v->GetInstance();
	if (instance != SRI_PATH) return 0;
	uint16 inst_data = v->GetInstanceData();
	if (!HasValidPath(inst_data)) return 0;

	return GetPathExits(GetImplodedPathSlope(inst_data), true);
}