Exemplo n.º 1
0
/**
 * In the given voxel, can a path be build in the voxel from the bottom at the given edge?
 * @param voxel_pos Coordinate of the voxel.
 * @param edge Entry edge.
 * @return Bit-set of track slopes, indicating the directions of building paths.
 */
static uint8 CanBuildPathFromEdge(const XYZPoint16 &voxel_pos, TileEdge edge)
{
	if (!IsVoxelstackInsideWorld(voxel_pos.x, voxel_pos.y)) return 0;
	if (voxel_pos.z < 0 || voxel_pos.z >= WORLD_Z_SIZE - 1) return 0;

	/* If other side of the edge is not on-world or not owned, don't compute path options. */
	Point16 dxy = _tile_dxy[edge];
	if (!IsVoxelstackInsideWorld(voxel_pos.x + dxy.x, voxel_pos.y + dxy.y) ||
			(_game_mode_mgr.InPlayMode() && _world.GetTileOwner(voxel_pos.x + dxy.x, voxel_pos.y + dxy.y) != OWN_PARK)) return 0;

	const Voxel *v = _world.GetVoxel(voxel_pos);
	if (v != nullptr && HasValidPath(v)) {
		PathSprites ps = GetImplodedPathSlope(v);
		if (ps < PATH_FLAT_COUNT) return 1 << TSL_FLAT;
		if (ps == _path_up_from_edge[edge]) return 1 << TSL_UP;
	}
	if (voxel_pos.z > 0) {
		v = _world.GetVoxel(voxel_pos + XYZPoint16(0, 0, -1));
		if (v != nullptr &&  HasValidPath(v) && GetImplodedPathSlope(v) == _path_down_from_edge[edge]) return 1 << TSL_DOWN;
	}

	uint8 slopes = 0;
	// XXX Check for already existing paths.
	if (BuildDownwardPath(voxel_pos, edge, PAT_INVALID, true)) slopes |= 1 << TSL_DOWN;
	if (BuildFlatPath(voxel_pos, PAT_INVALID, true)) slopes |= 1 << TSL_FLAT;
	if (BuildUpwardPath(voxel_pos, edge, PAT_INVALID, true)) slopes |= 1 << TSL_UP;
	return slopes;
}
Exemplo n.º 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;
	}
}
Exemplo n.º 3
0
/**
 * In the given voxel, can a flat path be build in the voxel?
 * @param voxel_pos Coordinate of the voxel.
 * @param path_type For building (ie not \a test_only), the type of path to build.
 * @param test_only Only test whether it could be created.
 * @return Whether the path is or could be built.
 */
static bool BuildFlatPath(const XYZPoint16 &voxel_pos, PathType path_type, bool test_only)
{
	/* xy position should be valid, and allow path building. */
	if (!IsVoxelstackInsideWorld(voxel_pos.x, voxel_pos.y)) return false;
	if (_game_mode_mgr.InPlayMode() && _world.GetTileOwner(voxel_pos.x, voxel_pos.y) != OWN_PARK) return false;

	if (voxel_pos.z < 0 || voxel_pos.z > WORLD_Z_SIZE - 2) return false; // Z range should be valid.

	const VoxelStack *vs = _world.GetStack(voxel_pos.x, voxel_pos.y);
	const Voxel *v = vs->Get(voxel_pos.z + 1); // Voxel above the path should be empty.
	if (v != nullptr && !v->IsEmpty()) return false;
	v = vs->Get(voxel_pos.z);
	if (v != nullptr) {
		if (v->GetInstance() != SRI_FREE) return false; // Voxel should have no other rides.
		if (v->GetGroundType() != GTP_INVALID) {
			if (v->GetGroundSlope() >= PATH_FLAT_COUNT) return false; // Non-flat surface.
		} else {
			/* No surface, but a foundation suggests a nearby hill.
			 * Currently simply deny building here, in the future, consider making a tunnel.
			 */
			if (v->GetFoundationType() != FDT_INVALID) return false;
		}
	}

	if (!test_only) BuildPathAtTile(voxel_pos, path_type, PATH_EMPTY);
	return true;
}
Exemplo n.º 4
0
/**
 * In the given voxel, can an upward path be build in the voxel from the bottom at the given edge?
 * @param voxel_pos Coordinate of the voxel.
 * @param edge Entry edge.
 * @param path_type For building (ie not \a test_only), the type of path to build.
 * @param test_only Only test whether it could be created.
 * @return Whether the path is or could be built.
 */
static bool BuildUpwardPath(const XYZPoint16 &voxel_pos, TileEdge edge, PathType path_type, bool test_only)
{
	/* xy position should be valid, and allow path building. */
	if (!IsVoxelstackInsideWorld(voxel_pos.x, voxel_pos.y)) return false;
	if (_game_mode_mgr.InPlayMode() && _world.GetTileOwner(voxel_pos.x, voxel_pos.y) != OWN_PARK) return false;

	if (voxel_pos.z < 0 || voxel_pos.z > WORLD_Z_SIZE - 3) return false; // Z range should be valid.

	const VoxelStack *vs = _world.GetStack(voxel_pos.x, voxel_pos.y);
	const Voxel *v = vs->Get(voxel_pos.z + 1); // Voxel above the path should be empty.
	if (v != nullptr && !v->IsEmpty()) return false;

	v = vs->Get(voxel_pos.z + 2); // 2 voxels higher should also be empty.
	if (v != nullptr && !v->IsEmpty()) return false;

	v = vs->Get(voxel_pos.z);
	if (v != nullptr) {
		if (v->GetInstance() != SRI_FREE) return false; // Voxel should have no other rides.
		if (v->GetGroundType() != GTP_INVALID) {
			TileSlope slope = ExpandTileSlope(v->GetGroundSlope());
			if ((slope & (TSB_STEEP | TSB_TOP)) == TSB_STEEP) return false;
			if ((slope & _corners_at_edge[edge]) != 0) return false; // A raised corner at 'edge'.
		} else {
			/* No surface, but a foundation suggests a nearby hill.
			 * Currently simply deny building here, in the future, consider making a tunnel.
			 */
			if (v->GetFoundationType() != FDT_INVALID) return false;
		}
	}

	if (!test_only) BuildPathAtTile(voxel_pos, path_type, _path_up_from_edge[edge]);
	return true;
}
Exemplo n.º 5
0
/**
 * Compute the attach points of a path in a voxel.
 * @param voxel_pos Coordinate of the voxel.
 * @return Attach points for paths starting from the given voxel coordinates.
 *         Upper 4 bits are the edges at the top of the voxel, lower 4 bits are the attach points for the bottom of the voxel.
 */
static uint8 GetPathAttachPoints(const XYZPoint16 &voxel_pos)
{
	if (!IsVoxelstackInsideWorld(voxel_pos.x, voxel_pos.y)) return 0;
	if (voxel_pos.z >= WORLD_Z_SIZE - 1) return 0; // The voxel containing the flat path, and one above it.

	const Voxel *v = _world.GetVoxel(voxel_pos);
	if (v == nullptr) return 0;

	uint8 edges = 0;
	for (TileEdge edge = EDGE_BEGIN; edge < EDGE_COUNT; edge++) {
		uint16 x = voxel_pos.x + _tile_dxy[edge].x;
		uint16 y = voxel_pos.y + _tile_dxy[edge].y;
		if (!IsVoxelstackInsideWorld(x, y)) continue;

		const XYZPoint16 new_pos(x, y, voxel_pos.z);
		if (HasValidPath(v)) {
			PathSprites ps = GetImplodedPathSlope(v);
			if (ps < PATH_FLAT_COUNT) {
				if (CanBuildPathFromEdge(new_pos, (TileEdge)((edge + 2) % 4)) != 0) edges |= 1 << edge;
			} else {
				const XYZPoint16 new_above(x, y, voxel_pos.z + 1);
				if (_path_up_from_edge[edge] == ps
						&& CanBuildPathFromEdge(new_pos, (TileEdge)((edge + 2) % 4)) != 0) edges |= 1 << edge;
				if (_path_down_from_edge[edge] == ps
						&& CanBuildPathFromEdge(new_above, (TileEdge)((edge + 2) % 4)) != 0) edges |= (1 << edge) << 4;
			}
			continue;
		}
		if (v->GetGroundType() != GTP_INVALID) {
			TileSlope ts = ExpandTileSlope(v->GetGroundSlope());
			if ((ts & TSB_STEEP) != 0) continue;
			if ((ts & _corners_at_edge[edge]) == 0) {
				if (CanBuildPathFromEdge(new_pos, (TileEdge)((edge + 2) % 4)) != 0) edges |= 1 << edge;
			} else {
				const XYZPoint16 new_above(x, y, voxel_pos.z + 1);
				if (CanBuildPathFromEdge(new_above, (TileEdge)((edge + 2) % 4)) != 0) edges |= (1 << edge) << 4;
			}
			continue;
		}
		/* No path and no ground -> Invalid, do next edge. */
	}
	return edges;
}
Exemplo n.º 6
0
/**
 * Decide at which voxel to place a shop. It should be placed at a voxel intersecting with the view line through the given point in the world.
 * @param world_pos Coordinate of the point.
 * @param vp_orient Orientation of the viewport.
 * @return Result of the placement process.
 */
RidePlacementResult RideBuildWindow::ComputeShopVoxel(XYZPoint32 world_pos, ViewOrientation vp_orient)
{
	ShopInstance *si = static_cast<ShopInstance *>(this->instance);
	assert(si != nullptr && si->GetKind() == RTK_SHOP); // It should be possible to set the position of a shop.
	const ShopType *st = si->GetShopType();
	assert(st != nullptr);

	int dx, dy; // Change of xworld and yworld for every (zworld / 2) change.
	switch (vp_orient) {
		case VOR_NORTH: dx =  1; dy =  1; break;
		case VOR_WEST:  dx = -1; dy =  1; break;
		case VOR_SOUTH: dx = -1; dy = -1; break;
		case VOR_EAST:  dx =  1; dy = -1; break;
		default: NOT_REACHED();
	}

	XYZPoint16 vox_pos;
	/* Move to the top voxel of the world. */
	vox_pos.z = WORLD_Z_SIZE - 1;
	int dz = vox_pos.z * 256 - world_pos.z;
	world_pos.x += dx * dz / 2;
	world_pos.y += dy * dz / 2;

	while (vox_pos.z >= 0) {
		vox_pos.x = world_pos.x / 256;
		vox_pos.y = world_pos.y / 256;
		if (IsVoxelstackInsideWorld(vox_pos.x, vox_pos.y) && this->CanPlaceShop(st, vox_pos, vp_orient)) {
			/* Position of the shop the same as previously? */
			if (si->vox_pos != vox_pos || si->orientation != this->orientation) {
				si->SetRide((this->orientation + vp_orient) & 3, vox_pos);
				return RPR_CHANGED;
			}
			return RPR_SAMEPOS;
		} else {
			/* Since z gets smaller, we subtract dx and dy, thus the checks reverse. */
			if (vox_pos.x < 0 && dx > 0) break;
			if (vox_pos.x >= _world.GetXSize() && dx < 0) break;
			if (vox_pos.y < 0 && dy > 0) break;
			if (vox_pos.y >= _world.GetYSize() && dy < 0) break;
		}
		world_pos.x -= 128 * dx;
		world_pos.y -= 128 * dy;
		vox_pos.z--;
	}
	return RPR_FAIL;
}
Exemplo n.º 7
0
/**
 * Does a path run at/to the bottom the given voxel in the neighbouring voxel?
 * @param voxel_pos Coordinate of the voxel.
 * @param edge Direction to move to get the neighbouring voxel.
 * @return Whether a path exists at the bottom of the neighbouring voxel.
 * @pre voxel coordinate must be valid in the world.
 * @todo Merge with path computations in the path placement.
 */
bool PathExistsAtBottomEdge(XYZPoint16 voxel_pos, TileEdge edge)
{
	voxel_pos.x += _tile_dxy[edge].x;
	voxel_pos.y += _tile_dxy[edge].y;
	if (!IsVoxelstackInsideWorld(voxel_pos.x, voxel_pos.y)) return false;

	const Voxel *vx = _world.GetVoxel(voxel_pos);
	if (vx == nullptr || !HasValidPath(vx)) {
		/* No path here, check the voxel below. */
		if (voxel_pos.z == 0) return false;
		voxel_pos.z--;
		vx = _world.GetVoxel(voxel_pos);
		if (vx == nullptr || !HasValidPath(vx)) return false;
		/* Path must end at the top of the voxel. */
		return GetImplodedPathSlope(vx) == _path_up_from_edge[edge];
	}
	/* Path must end at the bottom of the voxel. */
	return GetImplodedPathSlope(vx) < PATH_FLAT_COUNT || GetImplodedPathSlope(vx) == _path_down_from_edge[edge];
}
Exemplo n.º 8
0
/**
 * (Try to) remove a path from the given voxel.
 * @param voxel_pos Coordinate of the voxel.
 * @param test_only Only test whether it could be removed.
 * @return Whether the path is or could be removed.
 */
static bool RemovePath(const XYZPoint16 &voxel_pos, bool test_only)
{
	/* xy position should be valid, and allow path building. */
	if (!IsVoxelstackInsideWorld(voxel_pos.x, voxel_pos.y)) return false;
	if (_game_mode_mgr.InPlayMode() && _world.GetTileOwner(voxel_pos.x, voxel_pos.y) != OWN_PARK) return false;

	if (voxel_pos.z <= 0 || voxel_pos.z > WORLD_Z_SIZE - 2) return false; // Z range should be valid.

	const VoxelStack *vs = _world.GetStack(voxel_pos.x, voxel_pos.y);
	const Voxel *v = vs->Get(voxel_pos.z);
	if (v == nullptr || !HasValidPath(v)) return false;
	PathSprites ps = GetImplodedPathSlope(v);
	assert(ps < PATH_COUNT);

	v = vs->Get(voxel_pos.z + 1);
	assert(v->GetInstance() == SRI_PATH && !HasValidPath(v->GetInstanceData()));
	if (ps >= PATH_FLAT_COUNT) {
		v = vs->Get(voxel_pos.z + 2);
		assert(v->GetInstance() == SRI_PATH && !HasValidPath(v->GetInstanceData()));
	}

	if (!test_only) RemovePathAtTile(voxel_pos, ps);
	return true;
}
Exemplo n.º 9
0
/**
 * Add edges of the neighbouring path tiles.
 * @param voxel_pos Coordinate of the central voxel with a path tile.
 * @param slope Imploded path slope of the central voxel.
 * @param dirs Edge directions to change (bitset of #TileEdge), usually #EDGE_ALL.
 * @param status Status of the path. #PAS_UNUSED means to remove the edges.
 * @return Updated (imploded) slope at the central voxel.
 */
uint8 AddRemovePathEdges(const XYZPoint16 &voxel_pos, uint8 slope, uint8 dirs, PathStatus status)
{
	PathStatus ngb_status[4];    // Neighbour path status, #PAS_UNUSED means do not connect.
	Voxel *ngb_voxel[4];         // Neighbour voxels with path, may be null if it doesn't need changing.
	uint16 ngb_instance_data[4]; // New instance data, if the voxel exists.
	XYZPoint16 ngb_pos[4];       // Coordinate of the neighbouring voxel.

	Voxel *v = _world.GetCreateVoxel(voxel_pos, false);
	uint16 fences = v->GetFences();

	std::fill_n(ngb_status, lengthof(ngb_status), PAS_UNUSED); // Clear path all statuses to prevent connecting to it if an edge is skipped.
	for (TileEdge edge = EDGE_BEGIN; edge < EDGE_COUNT; edge++) {
		if ((dirs & (1 << edge)) == 0) continue; // Skip directions that should not be updated.
		int delta_z = 0;
		if (slope >= PATH_FLAT_COUNT) {
			if (_path_down_from_edge[edge] == slope) {
				delta_z = 1;
			} else if (_path_up_from_edge[edge] != slope) {
				continue;
			}
		}
		Point16 dxy = _tile_dxy[edge];
		ngb_pos[edge].x = voxel_pos.x + dxy.x;
		ngb_pos[edge].y = voxel_pos.y + dxy.y;
		if (!IsVoxelstackInsideWorld(ngb_pos[edge].x, ngb_pos[edge].y)) continue;

		FenceType fence_type = GetFenceType(fences, edge);
		if (fence_type != FENCE_TYPE_INVALID) continue;

		TileEdge edge2 = (TileEdge)((edge + 2) % 4);
		bool modified = false;
		if (delta_z <= 0 || voxel_pos.z < WORLD_Z_SIZE - 1) {
			ngb_pos[edge].z = voxel_pos.z + delta_z;
			modified = ExamineNeighbourPathEdge(ngb_pos[edge], edge2, status != PAS_UNUSED, true,
					&ngb_voxel[edge], &ngb_instance_data[edge], &ngb_status[edge]);
		}
		delta_z--;
		if (!modified && (delta_z >= 0 || voxel_pos.z > 0)) {
			ngb_pos[edge].z = voxel_pos.z + delta_z;
			ExamineNeighbourPathEdge(ngb_pos[edge], edge2, status != PAS_UNUSED, false,
					&ngb_voxel[edge], &ngb_instance_data[edge], &ngb_status[edge]);
		}
	}

	switch (status) {
		case PAS_UNUSED: // All edges get removed.
		case PAS_NORMAL_PATH: // All edges get added.
			for (TileEdge edge = EDGE_BEGIN; edge < EDGE_COUNT; edge++) {
				if (ngb_status[edge] != PAS_UNUSED) {
					if (slope < PATH_FLAT_COUNT) slope = SetPathEdge(slope, edge, status != PAS_UNUSED);
					if (ngb_voxel[edge] != nullptr) {
						ngb_voxel[edge]->SetInstanceData(ngb_instance_data[edge]);
						MarkVoxelDirty(ngb_pos[edge]);
					}
				}
			}
			return slope;

		case PAS_QUEUE_PATH:
			/* Pass 1: Try to add other queue paths. */
			for (TileEdge edge = EDGE_BEGIN; edge < EDGE_COUNT; edge++) {
				if (ngb_status[edge] == PAS_QUEUE_PATH) {
					if (GetQuePathEdgeConnectCount(slope) > 1) break;

					if (slope < PATH_FLAT_COUNT) slope = SetPathEdge(slope, edge, true);
					if (ngb_voxel[edge] != nullptr) {
						ngb_voxel[edge]->SetInstanceData(ngb_instance_data[edge]);
						MarkVoxelDirty(ngb_pos[edge]);
					}
				}
			}
			/* Pass 2: Add normal paths. */
			for (TileEdge edge = EDGE_BEGIN; edge < EDGE_COUNT; edge++) {
				if (ngb_status[edge] == PAS_NORMAL_PATH) {
					if (GetQuePathEdgeConnectCount(slope) > 1) break;

					if (slope < PATH_FLAT_COUNT) slope = SetPathEdge(slope, edge, true);
					if (ngb_voxel[edge] != nullptr) {
						ngb_voxel[edge]->SetInstanceData(ngb_instance_data[edge]);
						MarkVoxelDirty(ngb_pos[edge]);
					}
				}
			}
			return slope;

		default: NOT_REACHED();
	}
}