Example #1
0
static uint NPFSlopeCost(AyStarNode *current)
{
	TileIndex next = current->tile + TileOffsByDiagDir(TrackdirToExitdir(current->direction));

	/* Get center of tiles */
	int x1 = TileX(current->tile) * TILE_SIZE + TILE_SIZE / 2;
	int y1 = TileY(current->tile) * TILE_SIZE + TILE_SIZE / 2;
	int x2 = TileX(next) * TILE_SIZE + TILE_SIZE / 2;
	int y2 = TileY(next) * TILE_SIZE + TILE_SIZE / 2;

	int dx4 = (x2 - x1) / 4;
	int dy4 = (y2 - y1) / 4;

	/* Get the height on both sides of the tile edge.
	 * Avoid testing the height on the tile-center. This will fail for halftile-foundations.
	 */
	int z1 = GetSlopeZ(x1 + dx4, y1 + dy4);
	int z2 = GetSlopeZ(x2 - dx4, y2 - dy4);

	if (z2 - z1 > 1) {
		/* Slope up */
		return _settings_game.pf.npf.npf_rail_slope_penalty;
	}
	return 0;
	/* Should we give a bonus for slope down? Probably not, we
	 * could just substract that bonus from the penalty, because
	 * there is only one level of steepness... */
}
Example #2
0
static void SetDisasterVehiclePos(DisasterVehicle *v, int x, int y, byte z)
{
	v->x_pos = x;
	v->y_pos = y;
	v->z_pos = z;
	v->tile = TileVirtXY(x, y);

	DisasterVehicleUpdateImage(v);
	VehicleMove(v, true);

	DisasterVehicle *u = v->Next();
	if (u != NULL) {
		int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
		int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);

		u->x_pos = x;
		u->y_pos = y - 1 - (max(z - GetSlopeZ(safe_x, safe_y), 0U) >> 3);
		safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
		u->z_pos = GetSlopeZ(safe_x, safe_y);
		u->direction = v->direction;

		DisasterVehicleUpdateImage(u);
		VehicleMove(u, true);

		if ((u = u->Next()) != NULL) {
			u->x_pos = x;
			u->y_pos = y;
			u->z_pos = z + 5;
			VehicleMove(u, true);
		}
	}
Example #3
0
void SndPlayTileFx(SoundID sound, TileIndex tile)
{
	/* emits sound from center of the tile */
	int x = min(MapMaxX() - 1, TileX(tile)) * TILE_SIZE + TILE_SIZE / 2;
	int y = min(MapMaxY() - 1, TileY(tile)) * TILE_SIZE - TILE_SIZE / 2;
	uint z = (y < 0 ? 0 : GetSlopeZ(x, y));
	Point pt = RemapCoords(x, y, z);
	y += 2 * TILE_SIZE;
	Point pt2 = RemapCoords(x, y, GetSlopeZ(x, y));
	SndPlayScreenCoordFx(sound, pt.x, pt2.x, pt.y, pt2.y);
}
Example #4
0
/**
 * Place a sign at the given coordinates. Ownership of sign has
 * no effect whatsoever except for the colour the sign gets for easy recognition,
 * but everybody is able to rename/remove it.
 * @param tile tile to place sign at
 * @param flags type of operation
 * @param p1 unused
 * @param p2 unused
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdPlaceSign(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	/* Try to locate a new sign */
	if (!Sign::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_SIGNS);

	/* Check sign text length if any */
	if (!StrEmpty(text) && Utf8StringLength(text) >= MAX_LENGTH_SIGN_NAME_CHARS) return CMD_ERROR;

	/* When we execute, really make the sign */
	if (flags & DC_EXEC) {
		Sign *si = new Sign(_current_company);
		int x = TileX(tile) * TILE_SIZE;
		int y = TileY(tile) * TILE_SIZE;

		si->x = x;
		si->y = y;
		si->z = GetSlopeZ(x, y);
		if (!StrEmpty(text)) {
			si->name = strdup(text);
		}
		si->UpdateVirtCoord();
		InvalidateWindowData(WC_SIGN_LIST, 0, 0);
		_new_sign_id = si->index;
	}

	return CommandCost();
}
Example #5
0
/**
 * Build a ship.
 * @param tile     tile of the depot where ship is built.
 * @param flags    type of operation.
 * @param e        the engine to build.
 * @param data     unused.
 * @param ret[out] the vehicle that has been built.
 * @return the cost of this operation or an error.
 */
CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
{
	tile = GetShipDepotNorthTile(tile);
	if (flags & DC_EXEC) {
		int x;
		int y;

		const ShipVehicleInfo *svi = &e->u.ship;

		Ship *v = new Ship();
		*ret = v;

		v->owner = _current_company;
		v->tile = tile;
		x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
		y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
		v->x_pos = x;
		v->y_pos = y;
		v->z_pos = GetSlopeZ(x, y);

		v->UpdateDeltaXY(v->direction);
		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;

		v->spritenum = svi->image_index;
		v->cargo_type = e->GetDefaultCargoType();
		v->cargo_cap = svi->capacity;

		v->last_station_visited = INVALID_STATION;
		v->engine_type = e->index;

		v->reliability = e->reliability;
		v->reliability_spd_dec = e->reliability_spd_dec;
		v->max_age = e->GetLifeLengthInDays();
		_new_vehicle_id = v->index;

		v->state = TRACK_BIT_DEPOT;

		v->service_interval = Company::Get(_current_company)->settings.vehicle.servint_ships;
		v->date_of_last_service = _date;
		v->build_year = _cur_year;
		v->cur_image = SPR_IMG_QUERY;
		v->random_bits = VehicleRandomBits();

		v->UpdateCache();

		if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);

		v->InvalidateNewGRFCacheOfChain();

		v->cargo_cap = GetVehicleCapacity(v);

		v->InvalidateNewGRFCacheOfChain();

		VehicleMove(v, false);
	}

	return CommandCost();
}
Example #6
0
	int SlopeCost(TileIndex tile, TileIndex next_tile, Trackdir trackdir)
	{
		/* height of the center of the current tile */
		int x1 = TileX(tile) * TILE_SIZE;
		int y1 = TileY(tile) * TILE_SIZE;
		int z1 = GetSlopeZ(x1 + TILE_SIZE / 2, y1 + TILE_SIZE / 2);

		/* height of the center of the next tile */
		int x2 = TileX(next_tile) * TILE_SIZE;
		int y2 = TileY(next_tile) * TILE_SIZE;
		int z2 = GetSlopeZ(x2 + TILE_SIZE / 2, y2 + TILE_SIZE / 2);

		if (z2 - z1 > 1) {
			/* Slope up */
			return Yapf().PfGetSettings().road_slope_penalty;
		}
		return 0;
	}
	/**
	 * Updates vehicle's Z position.
	 * Inclination can't change in the middle of a tile.
	 * The faster code is used for trains and road vehicles unless they are
	 * reversing on a sloped tile.
	 */
	FORCEINLINE void UpdateZPosition()
	{
#if 0
		/* The following code does this: */

		if (HasBit(this->gv_flags, GVF_GOINGUP_BIT)) {
			switch (this->direction) {
				case DIR_NE:
					this->z_pos += (this->x_pos & 1); break;
				case DIR_SW:
					this->z_pos += (this->x_pos & 1) ^ 1; break;
				case DIR_NW:
					this->z_pos += (this->y_pos & 1); break;
				case DIR_SE:
					this->z_pos += (this->y_pos & 1) ^ 1; break;
				default: break;
			}
		} else if (HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
			switch (this->direction) {
				case DIR_NE:
					this->z_pos -= (this->x_pos & 1); break;
				case DIR_SW:
					this->z_pos -= (this->x_pos & 1) ^ 1; break;
				case DIR_NW:
					this->z_pos -= (this->y_pos & 1); break;
				case DIR_SE:
					this->z_pos -= (this->y_pos & 1) ^ 1; break;
				default: break;
			}
		}

		/* But gcc 4.4.5 isn't able to nicely optimise it, and the resulting
		 * code is full of conditional jumps. */
#endif

		/* Vehicle's Z position can change only if it has GVF_GOINGUP_BIT or GVF_GOINGDOWN_BIT set.
		 * Furthermore, if this function is called once every time the vehicle's position changes,
		 * we know the Z position changes by +/-1 at certain moments - when x_pos, y_pos is odd/even,
		 * depending on orientation of the slope and vehicle's direction */

		if (HasBit(this->gv_flags, GVF_GOINGUP_BIT) || HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
			if (T::From(this)->HasToUseGetSlopeZ()) {
				/* In some cases, we have to use GetSlopeZ() */
				this->z_pos = GetSlopeZ(this->x_pos, this->y_pos);
				return;
			}
			/* DirToDiagDir() is a simple right shift */
			DiagDirection dir = DirToDiagDir(this->direction);
			/* Read variables, so the compiler knows the access doesn't trap */
			int8 x_pos = this->x_pos;
			int8 y_pos = this->y_pos;
			/* DiagDirToAxis() is a simple mask */
			int8 d = DiagDirToAxis(dir) == AXIS_X ? x_pos : y_pos;
			/* We need only the least significant bit */
			d &= 1;
			/* Conditional "^ 1". Optimised to "(dir - 1) <= 1". */
			d ^= (int8)(dir == DIAGDIR_SW || dir == DIAGDIR_SE);
			/* Subtraction instead of addition because we are testing for GVF_GOINGUP_BIT.
			 * GVF_GOINGUP_BIT is used because it's bit 0, so simple AND can be used,
			 * without any shift */
			this->z_pos += HasBit(this->gv_flags, GVF_GOINGUP_BIT) ? d : -d;
		}

		assert(this->z_pos == GetSlopeZ(this->x_pos, this->y_pos));
	}
Example #8
0
/*!
 * Toplevel network safe docommand function for the current company. Must not be called recursively.
 * The callback is called when the command succeeded or failed. The parameters
 * tile, p1 and p2 are from the #CommandProc function. The paramater cmd is the command to execute.
 * The parameter my_cmd is used to indicate if the command is from a company or the server.
 *
 * @param tile The tile to perform a command on (see #CommandProc)
 * @param p1 Additional data for the command (see #CommandProc)
 * @param p2 Additional data for the command (see #CommandProc)
 * @param cmd The command to execute (a CMD_* value)
 * @param callback A callback function to call after the command is finished
 * @param text The text to pass
 * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
 * @return true if the command succeeded, else false
 */
bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd)
{
	assert(_docommand_recursive == 0);

	CommandCost res, res2;

	int x = TileX(tile) * TILE_SIZE;
	int y = TileY(tile) * TILE_SIZE;

	_error_message = INVALID_STRING_ID;
	StringID error_part1 = GB(cmd, 16, 16);
	_additional_cash_required = 0;

	/* get pointer to command handler */
	byte cmd_id = cmd & CMD_ID_MASK;
	assert(cmd_id < lengthof(_command_proc_table));

	CommandProc *proc = _command_proc_table[cmd_id].proc;
	if (proc == NULL) return false;

	/* Command flags are used internally */
	uint cmd_flags = GetCommandFlags(cmd);
	/* Flags get send to the DoCommand */
	DoCommandFlag flags = CommandFlagsToDCFlags(cmd_flags);

	/* Do not even think about executing out-of-bounds tile-commands */
	if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (cmd_flags & CMD_ALL_TILES) == 0))) return false;

	/* Always execute server and spectator commands as spectator */
	if (cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) _current_company = COMPANY_SPECTATOR;

	CompanyID old_company = _current_company;

	/* If the company isn't valid it may only do server command or start a new company!
	 * The server will ditch any server commands a client sends to it, so effectively
	 * this guards the server from executing functions for an invalid company. */
	if (_game_mode == GM_NORMAL && (cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) == 0 && !Company::IsValidID(_current_company)) {
		if (my_cmd) ShowErrorMessage(error_part1, _error_message, x, y);
		return false;
	}

	bool notest = (cmd_flags & CMD_NO_TEST) != 0;

	_docommand_recursive = 1;

	/* cost estimation only? */
	if (!IsGeneratingWorld() &&
			_shift_pressed &&
			IsLocalCompany() &&
			!(cmd & CMD_NETWORK_COMMAND) &&
			cmd_id != CMD_PAUSE) {
		/* estimate the cost. */
		SetTownRatingTestMode(true);
		res = proc(tile, flags, p1, p2, text);
		SetTownRatingTestMode(false);
		if (CmdFailed(res)) {
			res.SetGlobalErrorMessage();
			ShowErrorMessage(error_part1, _error_message, x, y);
		} else {
			ShowEstimatedCostOrIncome(res.GetCost(), x, y);
		}

		_docommand_recursive = 0;
		ClearStorageChanges(false);
		return false;
	}


	if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
		/* first test if the command can be executed. */
		SetTownRatingTestMode(true);
		res = proc(tile, flags, p1, p2, text);
		SetTownRatingTestMode(false);
		assert(cmd_id == CMD_COMPANY_CTRL || old_company == _current_company);
		if (CmdFailed(res)) {
			res.SetGlobalErrorMessage();
			goto show_error;
		}
		/* no money? Only check if notest is off */
		if (!notest && res.GetCost() != 0 && !CheckCompanyHasMoney(res)) goto show_error;
	}

#ifdef ENABLE_NETWORK
	/*
	 * If we are in network, and the command is not from the network
	 * send it to the command-queue and abort execution
	 */
	if (_networking && !(cmd & CMD_NETWORK_COMMAND)) {
		NetworkSend_Command(tile, p1, p2, cmd & ~CMD_FLAGS_MASK, callback, text);
		_docommand_recursive = 0;
		ClearStorageChanges(false);
		return true;
	}
#endif /* ENABLE_NETWORK */
	DEBUG(desync, 1, "cmd: %08x; %08x; %1x; %06x; %08x; %08x; %04x; %s\n", _date, _date_fract, (int)_current_company, tile, p1, p2, cmd & ~CMD_NETWORK_COMMAND, text);

	/* update last build coordinate of company. */
	if (tile != 0) {
		Company *c = Company::GetIfValid(_current_company);
		if (c != NULL) c->last_build_coordinate = tile;
	}

	/* Actually try and execute the command. If no cost-type is given
	 * use the construction one */
	res2 = proc(tile, flags | DC_EXEC, p1, p2, text);

	assert(cmd_id == CMD_COMPANY_CTRL || old_company == _current_company);

	/* If notest is on, it means the result of the test can be different than
	 *  the real command.. so ignore the test */
	if (!notest && !((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
		assert(res.GetCost() == res2.GetCost() && CmdFailed(res) == CmdFailed(res2)); // sanity check
	} else {
		if (CmdFailed(res2)) {
			res2.SetGlobalErrorMessage();
			goto show_error;
		}
	}

	SubtractMoneyFromCompany(res2);

	/* update signals if needed */
	UpdateSignalsInBuffer();

	if (IsLocalCompany() && _game_mode != GM_EDITOR) {
		if (res2.GetCost() != 0 && tile != 0) ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2.GetCost());
		if (_additional_cash_required != 0) {
			SetDParam(0, _additional_cash_required);
			if (my_cmd) ShowErrorMessage(error_part1, STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY, x, y);
			if (res2.GetCost() == 0) goto callb_err;
		}
	}

	_docommand_recursive = 0;

	if (callback) callback(true, tile, p1, p2);
	ClearStorageChanges(true);
	return true;

show_error:
	/* show error message if the command fails? */
	if (IsLocalCompany() && error_part1 != 0 && my_cmd) {
		ShowErrorMessage(error_part1, _error_message, x, y);
	}

callb_err:
	_docommand_recursive = 0;

	if (callback) callback(false, tile, p1, p2);
	ClearStorageChanges(false);
	return false;
}
Example #9
0
/** Build a ship.
 * @param tile tile of depot where ship is built
 * @param flags type of operation
 * @param p1 ship type being built (engine)
 * @param p2 unused
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	UnitID unit_num;

	if (!IsEngineBuildable(p1, VEH_SHIP, _current_company)) return_cmd_error(STR_ERROR_SHIP_NOT_AVAILABLE);

	const Engine *e = Engine::Get(p1);
	CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());

	/* Engines without valid cargo should not be available */
	if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;

	if (flags & DC_QUERY_COST) return value;

	/* The ai_new queries the vehicle cost before building the route,
	 * so we must check against cheaters no sooner than now. --pasky */
	if (!IsShipDepotTile(tile)) return CMD_ERROR;
	if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;

	unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_SHIP);

	if (!Vehicle::CanAllocateItem() || unit_num > _settings_game.vehicle.max_ships)
		return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);

	if (flags & DC_EXEC) {
		int x;
		int y;

		const ShipVehicleInfo *svi = &e->u.ship;

		Ship *v = new Ship();
		v->unitnumber = unit_num;

		v->owner = _current_company;
		v->tile = tile;
		x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
		y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
		v->x_pos = x;
		v->y_pos = y;
		v->z_pos = GetSlopeZ(x, y);

		v->UpdateDeltaXY(v->direction);
		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;

		v->spritenum = svi->image_index;
		v->cargo_type = e->GetDefaultCargoType();
		v->cargo_cap = svi->capacity;
		v->value = value.GetCost();

		v->last_station_visited = INVALID_STATION;
		v->max_speed = svi->max_speed;
		v->engine_type = p1;

		v->reliability = e->reliability;
		v->reliability_spd_dec = e->reliability_spd_dec;
		v->max_age = e->GetLifeLengthInDays();
		_new_vehicle_id = v->index;

		v->state = TRACK_BIT_DEPOT;

		v->service_interval = Company::Get(_current_company)->settings.vehicle.servint_ships;
		v->date_of_last_service = _date;
		v->build_year = _cur_year;
		v->cur_image = SPR_IMG_QUERY;
		v->random_bits = VehicleRandomBits();

		if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);

		v->InvalidateNewGRFCacheOfChain();

		v->cargo_cap = GetVehicleCapacity(v);

		v->InvalidateNewGRFCacheOfChain();

		VehicleMove(v, false);

		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
		InvalidateWindowClassesData(WC_SHIPS_LIST, 0);
		SetWindowDirty(WC_COMPANY, v->owner);
		if (IsLocalCompany()) {
			InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Ship window
		}

		Company::Get(_current_company)->num_engines[p1]++;
	}

	return value;
}
Example #10
0
static void ShipController(Ship *v)
{
	uint32 r;
	const byte *b;
	Direction dir;
	Track track;
	TrackBits tracks;

	v->tick_counter++;
	v->current_order_time++;

	if (v->breakdown_ctr != 0) {
		if (v->breakdown_ctr <= 2) {
			HandleBrokenShip(v);
			return;
		}
		if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
	}

	if (v->vehstatus & VS_STOPPED) return;

	ProcessOrders(v);
	v->HandleLoading();

	if (v->current_order.IsType(OT_LOADING)) return;

	CheckShipLeaveDepot(v);

	if (!ShipAccelerate(v)) return;

	GetNewVehiclePosResult gp = GetNewVehiclePos(v);
	if (v->state != TRACK_BIT_WORMHOLE) {
		/* Not on a bridge */
		if (gp.old_tile == gp.new_tile) {
			/* Staying in tile */
			if (v->IsInDepot()) {
				gp.x = v->x_pos;
				gp.y = v->y_pos;
			} else {
				/* Not inside depot */
				r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
				if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;

				/* A leave station order only needs one tick to get processed, so we can
				 * always skip ahead. */
				if (v->current_order.IsType(OT_LEAVESTATION)) {
					v->current_order.Free();
					SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
				} else if (v->dest_tile != 0) {
					/* We have a target, let's see if we reached it... */
					if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
							DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
						/* We got within 3 tiles of our target buoy, so let's skip to our
						 * next order */
						UpdateVehicleTimetable(v, true);
						v->IncrementOrderIndex();
						v->current_order.MakeDummy();
					} else {
						/* Non-buoy orders really need to reach the tile */
						if (v->dest_tile == gp.new_tile) {
							if (v->current_order.IsType(OT_GOTO_DEPOT)) {
								if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
									VehicleEnterDepot(v);
									return;
								}
							} else if (v->current_order.IsType(OT_GOTO_STATION)) {
								v->last_station_visited = v->current_order.GetDestination();

								/* Process station in the orderlist. */
								Station *st = Station::Get(v->current_order.GetDestination());
								if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
									ShipArrivesAt(v, st);
									v->BeginLoading();
								} else { // leave stations without docks right aways
									v->current_order.MakeLeaveStation();
									v->IncrementOrderIndex();
								}
							}
						}
					}
				}
			}
		} else {
			DiagDirection diagdir;
			/* New tile */
			if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY()) {
				goto reverse_direction;
			}

			dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile);
			assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW);
			diagdir = DirToDiagDir(dir);
			tracks = GetAvailShipTracks(gp.new_tile, diagdir);
			if (tracks == TRACK_BIT_NONE) goto reverse_direction;

			/* Choose a direction, and continue if we find one */
			track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
			if (track == INVALID_TRACK) goto reverse_direction;

			b = _ship_subcoord[diagdir][track];

			gp.x = (gp.x & ~0xF) | b[0];
			gp.y = (gp.y & ~0xF) | b[1];

			/* Call the landscape function and tell it that the vehicle entered the tile */
			r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
			if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;

			if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
				v->tile = gp.new_tile;
				v->state = TrackToTrackBits(track);
			}

			v->direction = (Direction)b[2];
		}
	} else {
		/* On a bridge */
		if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
			v->x_pos = gp.x;
			v->y_pos = gp.y;
			VehicleMove(v, !(v->vehstatus & VS_HIDDEN));
			return;
		}
	}

	/* update image of ship, as well as delta XY */
	dir = ShipGetNewDirection(v, gp.x, gp.y);
	v->x_pos = gp.x;
	v->y_pos = gp.y;
	v->z_pos = GetSlopeZ(gp.x, gp.y);

getout:
	v->UpdateViewport(true, true);
	return;

reverse_direction:
	dir = ReverseDir(v->direction);
	v->direction = dir;
	goto getout;
}