Ejemplo n.º 1
0
/**
 * Try to move the tile cursor to a new tile.
 * @param edge Direction of movement.
 * @param move_up Current tile seems to move up.
 */
void PathBuildManager::MoveCursor(TileEdge edge, bool move_up)
{
	if (this->state <= PBS_WAIT_ARROW || this->state > PBS_WAIT_BUY || edge == INVALID_EDGE) return;

	/* Test whether we can move in the indicated direction. */
	Point16 dxy = _tile_dxy[edge];
	if ((dxy.x < 0 && this->pos.x == 0) || (dxy.x > 0 && this->pos.x == _world.GetXSize() - 1)) return;
	if ((dxy.y < 0 && this->pos.y == 0) || (dxy.y > 0 && this->pos.y == _world.GetYSize() - 1)) return;
	if (_game_mode_mgr.InPlayMode() && _world.GetTileOwner(this->pos.x + dxy.x, this->pos.y + dxy.y) != OWN_PARK) return;

	const Voxel *v_top, *v_bot;
	if (move_up) {
		/* Exit of current tile is at the top. */
		v_top = (this->pos.z > WORLD_Z_SIZE - 2) ? nullptr : _world.GetVoxel(this->pos + XYZPoint16(dxy.x, dxy.y, 1));
		v_bot = _world.GetVoxel(this->pos + XYZPoint16(dxy.x, dxy.y, 0));
	} else {
		/* Exit of current tile is at the bottom. */
		v_top = _world.GetVoxel(this->pos + XYZPoint16(dxy.x, dxy.y, 0));
		v_bot = (this->pos.z == 0) ? nullptr : _world.GetVoxel(this->pos + XYZPoint16(dxy.x, dxy.y, -1));
	}

	/* Try to find a voxel with a path. */
	if (v_top != nullptr && HasValidPath(v_top)) {
		this->pos.x += dxy.x;
		this->pos.y += dxy.y;
		if (move_up) this->pos.z++;
		this->SetState(PBS_WAIT_ARROW);
		return;
	}
	if (v_bot != nullptr && HasValidPath(v_bot)) {
		this->pos.x += dxy.x;
		this->pos.y += dxy.y;
		if (!move_up) this->pos.z--;
		this->SetState(PBS_WAIT_ARROW);
		return;
	}

	/* Try to find a voxel with surface. */
	if (v_top != nullptr && v_top->GetGroundType() != GTP_INVALID && !IsImplodedSteepSlope(v_top->GetGroundSlope())) {
		this->pos.x += dxy.x;
		this->pos.y += dxy.y;
		if (move_up) this->pos.z++;
		this->SetState(PBS_WAIT_ARROW);
		return;
	}
	if (v_bot != nullptr && v_bot->GetGroundType() != GTP_INVALID && !IsImplodedSteepSlope(v_bot->GetGroundSlope())) {
		this->pos.x += dxy.x;
		this->pos.y += dxy.y;
		if (!move_up) this->pos.z--;
		this->SetState(PBS_WAIT_ARROW);
		return;
	}
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
0
/**
 * Can a shop be placed at the given voxel?
 * @param selected_shop Shop to place.
 * @param pos Coordinate of the voxel.
 * @param vp_orient Orientation of the viewport.
 * @pre voxel coordinate must be valid in the world.
 * @pre \a selected_shop may not be \c nullptr.
 * @return Shop can be placed at the given position.
 */
bool RideBuildWindow::CanPlaceShop(const ShopType *selected_shop, const XYZPoint16 &pos, ViewOrientation vp_orient)
{
	/* 1. Can the position itself be used to build a shop? */
	if (_world.GetTileOwner(pos.x, pos.y) != OWN_PARK) return false;
	const Voxel *vx = _world.GetVoxel(pos);
	if (vx != nullptr) {
		if (!vx->CanPlaceInstance()) return false; // Cannot build on a path or other ride.
		return vx->GetGroundType() != GTP_INVALID && vx->GetGroundSlope() == SL_FLAT; // Can build at a flat surface.
	}

	/* 2. Is the shop just above non-flat ground? */
	if (pos.z > 0) {
		vx = _world.GetVoxel(pos + XYZPoint16(0, 0, -1));
		if (vx != nullptr && vx->GetInstance() == SRI_FREE &&
				vx->GetGroundType() != GTP_INVALID && vx->GetGroundSlope() != SL_FLAT) return true;
	}

	/* 3. Is there a path at the right place? */
	for (TileEdge entrance = EDGE_BEGIN; entrance < EDGE_COUNT; entrance++) { // Loop over the 4 unrotated directions.
		if ((selected_shop->flags & (1 << entrance)) == 0) continue; // No entrance here.
		TileEdge entr = static_cast<TileEdge>((entrance + vp_orient + this->orientation) & 3); // Perform rotation specified by the user in the GUI.
		if (PathExistsAtBottomEdge(pos, entr)) return true;
	}
	return false;
}
Ejemplo n.º 5
0
/**
 * Check that the voxel stack at the given coordinate is a good spot to use as entry point for new guests.
 * @param x X position at the edge.
 * @param y Y position at the edge.
 * @return The given position is a good starting point.
 */
static bool IsGoodEdgeRoad(int16 x, int16 y)
{
	if (x < 0 || y < 0) return false;
	int16 z = _world.GetBaseGroundHeight(x, y);
	const Voxel *vs = _world.GetVoxel(XYZPoint16(x, y, z));
	return vs && HasValidPath(vs) && GetImplodedPathSlope(vs) < PATH_FLAT_COUNT;
}
Ejemplo n.º 6
0
/**
 * Mark the additions in the display as outdated, so they get repainted.
 * @param vp %Viewport displaying the world.
 */
void WorldAdditions::MarkDirty(Viewport *vp)
{
	for (const auto &iter : this->modified_stacks) {
		const Point32 pt = iter.first;
		const VoxelStack *vstack = iter.second;
		if (vstack != nullptr) vp->MarkVoxelDirty(XYZPoint16(pt.x, pt.y, vstack->base), vstack->height);
	}
}
Ejemplo n.º 7
0
/**
 * Add foundation bits from the bottom up to the given voxel.
 * @param world %Voxel storage.
 * @param xpos X position of the voxel stack.
 * @param ypos Y position of the voxel stack.
 * @param z Height of the ground.
 * @param bits Foundation bits to add.
 */
static void AddFoundations(VoxelWorld *world, uint16 xpos, uint16 ypos, int16 z, uint8 bits)
{
	for (int16 zpos = 0; zpos < z; zpos++) {
		Voxel *v = world->GetCreateVoxel(XYZPoint16(xpos, ypos, zpos), true);
		if (v->GetFoundationType() == FDT_INVALID) {
			v->SetFoundationType(FDT_GROUND);
			v->SetFoundationSlope(bits);
			continue;
		}
		v->SetFoundationSlope(v->GetFoundationSlope() | bits);
	}
}
Ejemplo n.º 8
0
/**
 * Compute the voxel to display the arrow cursor.
 * @return Computed position of the voxel that should contain the arrow cursor.
 */
XYZPoint16 PathBuildManager::ComputeArrowCursorPosition()
{
	assert(this->state > PBS_WAIT_ARROW && this->state <= PBS_WAIT_BUY);
	assert(this->selected_arrow != INVALID_EDGE);

	Point16 dxy = _tile_dxy[this->selected_arrow];
	XYZPoint16 arr_pos = this->pos + XYZPoint16(dxy.x, dxy.y, 0);

	uint8 bit = 1 << this->selected_arrow;
	if ((bit & this->allowed_arrows) == 0) { // Build direction is not at the bottom of the voxel.
		assert(((bit << 4) &  this->allowed_arrows) != 0); // Should be available at the top of the voxel.
		arr_pos.z++;
	}

	/* Do some paranoia checking. */
	assert(IsVoxelInsideWorld(arr_pos));
	return arr_pos;
}
Ejemplo n.º 9
0
/**
 * Creates a world of flat tiles.
 * @param z Height of the tiles.
 */
void VoxelWorld::MakeFlatWorld(int16 z)
{
	for (uint16 xpos = 0; xpos < this->x_size; xpos++) {
		for (uint16 ypos = 0; ypos < this->y_size; ypos++) {
			Voxel *v = this->GetCreateVoxel(XYZPoint16(xpos, ypos, z), true);
			v->SetFoundationType(FDT_INVALID);
			v->SetGroundType(GTP_GRASS0);
			v->SetGroundSlope(ImplodeTileSlope(SL_FLAT));
			v->ClearInstances();
		}
	}
	for (uint16 xpos = 0; xpos < this->x_size; xpos++) {
		AddFoundations(this, xpos, 0, z, 0xC0);
		AddFoundations(this, xpos, this->y_size - 1, z, 0x0C);
	}
	for (uint16 ypos = 0; ypos < this->y_size; ypos++) {
		AddFoundations(this, 0, ypos, z, 0x03);
		AddFoundations(this, this->x_size - 1, ypos, z, 0x30);
	}
}
Ejemplo n.º 10
0
/** Compute the new contents of the voxel where the path should be added from the #_world. */
void PathBuildManager::ComputeWorldAdditions()
{
	assert(this->state == PBS_WAIT_BUY); // It needs selected_arrow and selected_slope.
	assert(this->selected_slope != TSL_INVALID);

	if (((1 << this->selected_slope) & this->allowed_slopes) == 0) return;

	XYZPoint16 arrow_pos = this->ComputeArrowCursorPosition();

	const Voxel *v = _world.GetVoxel(arrow_pos);
	if (v != nullptr && HasValidPath(v)) {
		ChangePath(arrow_pos, this->path_type, false);
		return;
	}

	TileEdge edge = (TileEdge)((this->selected_arrow + 2) % 4);
	switch (this->selected_slope) {
		case TSL_DOWN:
			v = _world.GetVoxel(arrow_pos + XYZPoint16(0, 0, -1));

			if (v != nullptr && HasValidPath(v)) {
				arrow_pos.z--;
				ChangePath(arrow_pos, this->path_type, false);
			} else {
				BuildDownwardPath(arrow_pos, edge, this->path_type, false);
			}
			break;

		case TSL_FLAT:
			BuildFlatPath(arrow_pos, this->path_type, false);
			break;

		case TSL_UP:
			BuildUpwardPath(arrow_pos, edge, this->path_type, false);
			break;

		default:
			NOT_REACHED();
	}
}
Ejemplo n.º 11
0
/**
 * Build a path from #_path_builder xpos/ypos to the mouse cursor position.
 * @param mousexy Mouse position.
 */
void PathBuildManager::ComputeNewLongPath(const Point32 &mousexy)
{
	static const TrackSlope slope_prios_down[] = {TSL_DOWN, TSL_FLAT, TSL_UP,   TSL_INVALID}; // Order of preference when going down.
	static const TrackSlope slope_prios_flat[] = {TSL_FLAT, TSL_UP,   TSL_DOWN, TSL_INVALID}; // Order of preference when at the right height.
	static const TrackSlope slope_prios_up[]   = {TSL_UP,   TSL_FLAT, TSL_DOWN, TSL_INVALID}; // Order of preference when going up.

	Viewport *vp = GetViewport();
	if (vp == nullptr) return;

	int c1, c2, c3;
	switch (vp->orientation) {
		case VOR_NORTH: c1 =  1; c2 =  2; c3 =  2; break;
		case VOR_EAST:  c1 = -1; c2 = -2; c3 =  2; break;
		case VOR_SOUTH: c1 =  1; c2 = -2; c3 = -2; break;
		case VOR_WEST:  c1 = -1; c2 =  2; c3 = -2; break;
		default: NOT_REACHED();
	}

	XYZPoint16 path_pos(0, 0, 0);
	path_pos.y = this->pos.y * 256 + 128;
	int32 lambda_y = path_pos.y - mousexy.y; // Distance to constant Y plane at current tile cursor.
	path_pos.x = this->pos.x * 256 + 128;
	int32 lambda_x = path_pos.x - mousexy.x; // Distance to constant X plane at current tile cursor.

	if (abs(lambda_x) < abs(lambda_y)) {
		/* X constant. */
		path_pos.x /= 256;
		path_pos.y = Clamp<int32>(mousexy.y + c1 * lambda_x, 0, _world.GetYSize() * 256 - 1) / 256;
		path_pos.z = Clamp<int32>(vp->view_pos.z + c3 * lambda_x, 0, WORLD_Z_SIZE * 256 - 1) / 256;
	} else {
		/* Y constant. */
		path_pos.x = Clamp<int32>(mousexy.x + c1 * lambda_y, 0, _world.GetXSize() * 256 - 1) / 256;
		path_pos.y /= 256;
		path_pos.z = Clamp<int32>(vp->view_pos.z + c2 * lambda_y, 0, WORLD_Z_SIZE * 256 - 1) / 256;
	}

	if (this->long_pos != path_pos) {
		this->long_pos = path_pos;

		_additions.Clear();
		path_pos = this->pos;
		/* Find the right direction from the selected tile to the current cursor location. */
		TileEdge direction;
		Point16 dxy;
		for (direction = EDGE_BEGIN; direction < EDGE_COUNT; direction++) {
			dxy = _tile_dxy[direction];
			if (!GoodDirection(dxy.x, path_pos.x, this->long_pos.x) || !GoodDirection(dxy.y, path_pos.y, this->long_pos.y)) continue;
			break;
		}
		if (direction == EDGE_COUNT) return;

		/* 'Walk' to the cursor as long as possible. */
		while (path_pos.x != this->long_pos.x || path_pos.y != this->long_pos.y) {
			uint8 slopes = CanBuildPathFromEdge(path_pos, direction);
			const TrackSlope *slope_prio;
			/* Get order of slope preference. */
			if (path_pos.z > this->long_pos.z) {
				slope_prio = slope_prios_down;
			} else if (path_pos.z == this->long_pos.z) {
				slope_prio = slope_prios_flat;
			} else {
				slope_prio = slope_prios_up;
			}
			/* Find best slope, and take it. */
			while (*slope_prio != TSL_INVALID && (slopes & (1 << *slope_prio)) == 0) slope_prio++;
			if (*slope_prio == TSL_INVALID) break;

			path_pos.x += dxy.x;
			path_pos.y += dxy.y;

			const Voxel *v = _world.GetVoxel(path_pos);
			if (v != nullptr && HasValidPath(v)) {
				if (!ChangePath(path_pos, this->path_type, false)) break;

				if (*slope_prio == TSL_UP) path_pos.z++;
			} else {
				if (*slope_prio == TSL_UP) {
					if (!BuildUpwardPath(path_pos, static_cast<TileEdge>((direction + 2) & 3), this->path_type, false)) break;
					path_pos.z++;
				} else if (*slope_prio == TSL_DOWN) {
					v = _world.GetVoxel(path_pos + XYZPoint16(0, 0, -1));

					if (v != nullptr && HasValidPath(v)) {
						if (!ChangePath(path_pos + XYZPoint16(0, 0, -1), this->path_type, false)) break;
					} else {
						if (!BuildDownwardPath(path_pos, static_cast<TileEdge>((direction + 2) & 3), this->path_type, false)) break;
					}
					path_pos.z--;
				} else {
					if (!BuildFlatPath(path_pos, this->path_type, false)) break;
				}
			}
		}
		vp->EnableWorldAdditions();
		vp->EnsureAdditionsAreVisible();
	}
}