Пример #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;
}
Пример #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
/**
 * 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;
	}
}
Пример #4
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;
}
Пример #5
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;

    PathSprites slope = GetImplodedPathSlope(inst_data);
    if (slope < PATH_FLAT_COUNT) { // At a flat path tile.
        return (_path_expand[slope] >> PATHBIT_NE) & 0xF;
    }

    switch (slope) {
    case PATH_RAMP_NE:
        return (1 << EDGE_NE) | (0x10 << EDGE_SW);
    case PATH_RAMP_NW:
        return (1 << EDGE_NW) | (0x10 << EDGE_SE);
    case PATH_RAMP_SE:
        return (1 << EDGE_SE) | (0x10 << EDGE_NW);
    case PATH_RAMP_SW:
        return (1 << EDGE_SW) | (0x10 << EDGE_NE);
    default:
        NOT_REACHED();
    }
}
Пример #6
0
/**
 * Can the user press the 'remove' button at the path GUI?
 * @return Button is enabled.
 */
bool PathBuildManager::GetRemoveIsEnabled() const
{
	if (this->state == PBS_IDLE || this->state == PBS_WAIT_VOXEL) return false;
	/* If current tile has a path, it can be removed. */
	const Voxel *v = _world.GetVoxel(this->pos);
	if (v != nullptr && HasValidPath(v)) return true;
	return this->state == PBS_WAIT_BUY;
}
Пример #7
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);
}
Пример #8
0
/**
 * (Try to) change the path type of the current path at the given voxel.
 * @param voxel_pos Coordinate of the voxel.
 * @param path_type For changing (ie not \a test_only), the type of path to change to.
 * @param test_only Only test whether it could be changed.
 * @return Whether the path's type could be changed.
 */
static bool ChangePath(const XYZPoint16 &voxel_pos, PathType path_type, bool test_only)
{
	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) ChangePathAtTile(voxel_pos, path_type, ps);
	return true;
}
Пример #9
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];
}
Пример #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();
	}
}
Пример #11
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;
}
Пример #12
0
/**
 * User clicked somewhere. Check whether it is a good tile or path, and if so, move the building process forward.
 * @param click_pos Coordinate of the voxel.
 */
void PathBuildManager::TileClicked(const XYZPoint16 &click_pos)
{
	if (this->state == PBS_IDLE || this->state > PBS_WAIT_BUY) return;

	if (this->state == PBS_SINGLE) {
		this->pos = click_pos;
		const Voxel *v = _world.GetCreateVoxel(this->pos, false);

		if (v != nullptr && HasValidPath(v)) {
			ChangePath(this->pos, this->path_type, false);
		} else {
			TileSlope ts = ExpandTileSlope(v->GetGroundSlope());

			if (ts == SL_FLAT) {
				BuildFlatPath(this->pos, this->path_type, false);
			} else {
				TileEdge edge = INVALID_EDGE;

				switch (ts) {
					case TSB_NORTHEAST:
						edge = EDGE_SW;
						break;
					case TSB_NORTHWEST:
						edge = EDGE_SE;
						break;
					case TSB_SOUTHEAST:
						edge = EDGE_NW;
						break;
					case TSB_SOUTHWEST:
						edge = EDGE_NE;
						break;
					default:
						return;
				}

				BuildUpwardPath(this->pos, edge, this->path_type, false);
			}
		}
		_additions.Commit();
	} else {
		uint8 dirs = GetPathAttachPoints(click_pos);
		if (dirs == 0) return;

		this->pos = click_pos;
		this->allowed_arrows = dirs;
		this->SetState(PBS_WAIT_ARROW);
	}
}
Пример #13
0
/**
 * Examine, and perhaps modify a neighbouring path edge or ride connection, to make it connect (or not if not \a add_edges)
 * to the centre examined tile.
 * @param voxel_pos Coordinate of the neighbouring voxel.
 * @param edge Edge to examine, and/or connected to.
 * @param add_edges If set, add edges (else, remove them).
 * @param at_bottom Whether a path connection is expected at the bottom (if \c false, it should be at the top).
 * @param dest_voxel [out] %Voxel containing the neighbouring path, or \c nullptr.
 * @param dest_inst_data [out] New instance of the voxel. Only valid if \a dest_voxel is not \c nullptr.
 * @param dest_status [out] Status of the neighbouring path.
 * @return Neighbouring voxel was (logically) connected to the centre tile.
 */
static bool ExamineNeighbourPathEdge(const XYZPoint16 &voxel_pos, TileEdge edge, bool add_edges, bool at_bottom,
		Voxel **dest_voxel, uint16 *dest_inst_data, PathStatus *dest_status)
{
	Voxel *v;

	*dest_voxel = nullptr;
	*dest_status = PAS_UNUSED;
	*dest_inst_data = PATH_INVALID;

	v = _world.GetCreateVoxel(voxel_pos, false);
	if (v == nullptr) return false;

	uint16 fences = v->GetFences();
	FenceType fence_type = GetFenceType(fences, edge);
	if (fence_type != FENCE_TYPE_INVALID) return false;

	uint16 number = v->GetInstance();
	if (number == SRI_PATH) {
		uint16 instance_data = v->GetInstanceData();
		if (!HasValidPath(instance_data)) return false;

		uint8 slope = GetImplodedPathSlope(instance_data);
		if (at_bottom) {
			if (slope >= PATH_FLAT_COUNT && slope != _path_up_from_edge[edge]) return false;
		} else {
			if (slope != _path_down_from_edge[edge]) return false;
		}

		PathStatus status = _sprite_manager.GetPathStatus(GetPathType(instance_data));
		if (add_edges && status == PAS_QUEUE_PATH) { // Only try to connect to queue paths if they are not yet connected to 2 (or more) neighbours.
			if (GetQuePathEdgeConnectCount(slope) > 1) return false;
		}

		slope = SetPathEdge(slope, edge, add_edges);
		*dest_status = status;
		*dest_voxel = v;
		*dest_inst_data = SetImplodedPathSlope(instance_data, slope);
		return true;

	} else if (number >= SRI_FULL_RIDES) { // A ride instance. Does it have an entrance here?
		if ((v->GetInstanceData() & (1 << edge)) != 0) {
			*dest_status = PAS_QUEUE_PATH;
			return true;
		}
	}
	return false;
}
Пример #14
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;
}
Пример #15
0
/**
 * User clicked 'forward' or 'back'. Figure out which direction to move in.
 * @param move_forward Move forward (if \c false, move back).
 */
void PathBuildManager::SelectMovement(bool move_forward)
{
	if (this->state <= PBS_WAIT_ARROW || this->state > PBS_WAIT_BUY) return;

	TileEdge edge = (move_forward) ? this->selected_arrow : (TileEdge)((this->selected_arrow + 2) % 4);

	bool move_up;
	const Voxel *v = _world.GetVoxel(this->pos);
	if (v == nullptr) return;
	if (HasValidPath(v)) {
		move_up = (GetImplodedPathSlope(v) == _path_down_from_edge[edge]);
	} else if (v->GetGroundType() != GTP_INVALID) {
		TileSlope ts = ExpandTileSlope(v->GetGroundSlope());
		if ((ts & TSB_STEEP) != 0) return;
		move_up = ((ts & _corners_at_edge[edge]) != 0);
	} else {
		return; // Surface type but no valid ground/path surface.
	}

	this->MoveCursor(edge, move_up);
}
Пример #16
0
/**
 * User selected 'buy' or 'remove'. Perform the action, and update the path build state.
 * @param buying If \c true, user selected 'buy'.
 */
void PathBuildManager::SelectBuyRemove(bool buying)
{
	if (buying) {
		// Buy a long path.
		if (this->state == PBS_LONG_BUY) {
			_additions.Commit();
			this->pos = this->long_pos;
			this->SetState(PBS_WAIT_ARROW);
			return;
		}
		// Buying a path tile.
		if (this->state != PBS_WAIT_BUY) return;
		_additions.Commit();
		this->SelectMovement(true);
	} else {
		// Removing a path tile.
		if (this->state <= PBS_WAIT_VOXEL || this->state > PBS_WAIT_BUY) return;
		TileEdge edge = (TileEdge)((this->selected_arrow + 2) % 4);
		const Voxel *v = _world.GetVoxel(this->pos);
		if (v == nullptr || !HasValidPath(v)) {
			this->MoveCursor(edge, false);
			this->UpdateState();
			return;
		}
		PathSprites ps = GetImplodedPathSlope(v);

		_additions.Clear();
		if (RemovePath(this->pos, false)) {
			_additions.Commit();
		}

		/* Short-cut version of this->SelectMovement(false), as that function fails after removing the path. */
		bool move_up = (ps == _path_down_from_edge[edge]);
		this->MoveCursor(edge, move_up);
		this->UpdateState();
	}
}
Пример #17
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();
	}
}