Example #1
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();
	}
}
Example #2
0
/** Update the state of the path build process. */
void PathBuildManager::UpdateState()
{
	Viewport *vp = GetViewport();

	if (this->state == PBS_IDLE || this->state == PBS_SINGLE) {
		DisableWorldAdditions();
		this->selected_arrow = INVALID_EDGE;
		this->selected_slope = this->state == PBS_IDLE ? TSL_INVALID : TSL_FLAT;
	}

	/* The tile cursor is controlled by the viewport if waiting for a voxel or earlier. */
	if (vp != nullptr && this->state > PBS_WAIT_VOXEL && this->state <= PBS_WAIT_BUY) {
		vp->tile_cursor.SetCursor(this->pos, CUR_TYPE_TILE);
	}

	/* See whether the PBS_WAIT_ARROW state can be left automatically. */
	if (this->state == PBS_WAIT_ARROW) {
		this->allowed_arrows = GetPathAttachPoints(this->pos);

		/* If a valid selection has been made, or if only one choice exists, take it. */
		if (this->selected_arrow != INVALID_EDGE && ((0x11 << this->selected_arrow) & this->allowed_arrows) != 0) {
			this->state = PBS_WAIT_SLOPE;
		} else if (this->allowed_arrows == (1 << EDGE_NE) || this->allowed_arrows == (0x10 << EDGE_NE)) {
			this->selected_arrow = EDGE_NE;
			this->state = PBS_WAIT_SLOPE;
		} else if (this->allowed_arrows == (1 << EDGE_NW) || this->allowed_arrows == (0x10 << EDGE_NW)) {
			this->selected_arrow = EDGE_NW;
			this->state = PBS_WAIT_SLOPE;
		} else if (this->allowed_arrows == (1 << EDGE_SE) || this->allowed_arrows == (0x10 << EDGE_SE)) {
			this->selected_arrow = EDGE_SE;
			this->state = PBS_WAIT_SLOPE;
		} else if (this->allowed_arrows == (1 << EDGE_SW) || this->allowed_arrows == (0x10 << EDGE_SW)) {
			this->selected_arrow = EDGE_SW;
			this->state = PBS_WAIT_SLOPE;
		}
	}

	/* Set the arrow cursor. Note that display is controlled later. */
	if (vp != nullptr) {
		if (this->state > PBS_WAIT_ARROW && this->state <= PBS_WAIT_BUY) {
			XYZPoint16 arrow_pos = this->ComputeArrowCursorPosition();
			vp->arrow_cursor.SetCursor(arrow_pos, (CursorType)(CUR_TYPE_ARROW_NE + this->selected_arrow));
		} else {
			vp->arrow_cursor.SetInvalid();
		}
	}

	/* See whether the PBS_WAIT_SLOPE state can be left automatically. */
	if (this->state == PBS_WAIT_SLOPE) {
		/* Compute allowed slopes. */
		XYZPoint16 arrow_pos = this->ComputeArrowCursorPosition();
		this->allowed_slopes = CanBuildPathFromEdge(arrow_pos, (TileEdge)((this->selected_arrow + 2) % 4));

		/* If a valid selection has been made, or if only one choice exists, take it. */
		if (this->selected_slope != TSL_INVALID && ((1 << this->selected_slope) & this->allowed_slopes) != 0) {
			this->state = PBS_WAIT_BUY;
		} else if (this->allowed_slopes == (1 << TSL_DOWN)) {
			this->selected_slope = TSL_DOWN;
			this->state = PBS_WAIT_BUY;
		} else if (this->allowed_slopes == (1 << TSL_FLAT)) {
			this->selected_slope = TSL_FLAT;
			this->state = PBS_WAIT_BUY;
		} else if (this->allowed_slopes == (1 << TSL_UP)) {
			this->selected_slope = TSL_UP;
			this->state = PBS_WAIT_BUY;
		}
	}

	/* Handle _additions display. */
	if (vp != nullptr) {
		if (this->state == PBS_SINGLE || this->state == PBS_WAIT_SLOPE) {
			_additions.Clear();
			vp->EnableWorldAdditions();
		} else if (this->state == PBS_WAIT_BUY) {
			_additions.Clear();

			this->ComputeWorldAdditions();
			vp->EnableWorldAdditions();
			vp->EnsureAdditionsAreVisible();
		} else {
			if (this->state != PBS_LONG_BUILD && this->state != PBS_LONG_BUY) vp->DisableWorldAdditions();
		}
	}

	NotifyChange(WC_PATH_BUILDER, ALL_WINDOWS_OF_TYPE, CHG_UPDATE_BUTTONS, 0);
}