Changeset - r5856:e64c21408d74
[Not reviewed]
master
0 3 0
tron - 18 years ago 2007-01-27 16:45:16
tron@openttd.org
(svn r8430) -Fix

Replace the rather obscure control flow for handling aircraft/ship/train orders by something remotly comprehensible (see r3584)
3 files changed with 36 insertions and 25 deletions:
0 comments (0 inline, 0 general)
src/aircraft_cmd.cpp
Show inline comments
 
@@ -492,1561 +492,1565 @@ int32 CmdStartStopAircraft(TileIndex til
 

	
 
		v->vehstatus ^= VS_STOPPED;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		InvalidateWindowClasses(WC_AIRCRAFT_LIST);
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Send an aircraft to the hangar.
 
 * @param tile unused
 
 * @param p1 vehicle ID to send to the hangar
 
 * @param p2 various bitmasked elements
 
 * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h)
 
 * - p2 bit 8-10 - VLW flag (for mass goto depot)
 
 */
 
int32 CmdSendAircraftToHangar(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	if (p2 & DEPOT_MASS_SEND) {
 
		/* Mass goto depot requested */
 
		if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
 
		return SendAllVehiclesToDepot(VEH_Aircraft, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1);
 
	}
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Aircraft || !CheckOwnership(v->owner) || IsAircraftInHangar(v)) return CMD_ERROR;
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT && !(p2 & DEPOT_LOCATE_HANGAR)) {
 
		if (!!(p2 & DEPOT_SERVICE) == HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT)) {
 
			/* We called with a different DEPOT_SERVICE setting.
 
			 * Now we change the setting to apply the new one and let the vehicle head for the same hangar.
 
			 * Note: the if is (true for requesting service == true for ordered to stop in hangar) */
 
			if (flags & DC_EXEC) {
 
				TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			}
 
			return 0;
 
		}
 

	
 
		if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of hangar orders
 
		if (flags & DC_EXEC) {
 
			if (v->current_order.flags & OF_UNLOAD) v->cur_order_index++;
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
	} else {
 
		bool next_airport_has_hangar = true;
 
		StationID next_airport_index = v->u.air.targetairport;
 
		const Station *st = GetStation(next_airport_index);
 
		/* If the station is not a valid airport or if it has no hangars */
 
		if (!IsValidStation(st) || st->airport_tile == 0 || GetAirport(st->airport_type)->nof_depots == 0) {
 
			StationID station;
 

	
 
			// the aircraft has to search for a hangar on its own
 
			station = FindNearestHangar(v);
 

	
 
			next_airport_has_hangar = false;
 
			if (station == INVALID_STATION) return CMD_ERROR;
 
			st = GetStation(station);
 
			next_airport_index = station;
 

	
 
		}
 

	
 
		if (flags & DC_EXEC) {
 
			v->current_order.type = OT_GOTO_DEPOT;
 
			v->current_order.flags = OF_NON_STOP;
 
			if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
			v->current_order.refit_cargo = CT_INVALID;
 
			v->current_order.dest = next_airport_index;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			if (v->u.air.state == FLYING && !next_airport_has_hangar) {
 
				/* The aircraft is now heading for a different hangar than the next in the orders */
 
				AircraftNextAirportPos_and_Order(v);
 
				v->u.air.targetairport = next_airport_index;
 
			}
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
/** Refits an aircraft to the specified cargo type.
 
 * @param tile unused
 
 * @param p1 vehicle ID of the aircraft to refit
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit 0-7) - the new cargo type to refit to
 
 * - p2 = (bit 8-15) - the new cargo subtype to refit to
 
 */
 
int32 CmdRefitAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	int pass, mail;
 
	int32 cost;
 
	CargoID new_cid = GB(p2, 0, 8);
 
	byte new_subtype = GB(p2, 8, 8);
 
	const AircraftVehicleInfo *avi;
 
	uint16 callback = CALLBACK_FAILED;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Aircraft || !CheckOwnership(v->owner)) return CMD_ERROR;
 
	if (!IsAircraftInHangarStopped(v)) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
 

	
 
	avi = AircraftVehInfo(v->engine_type);
 

	
 
	/* Check cargo */
 
	if (new_cid > NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_RUN);
 

	
 
	/* Check the refit capacity callback */
 
	if (HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_REFIT_CAPACITY)) {
 
		/* Back up the existing cargo type */
 
		CargoID temp_cid = v->cargo_type;
 
		byte temp_subtype = v->cargo_subtype;
 
		v->cargo_type = new_cid;
 
		v->cargo_subtype = new_subtype;
 

	
 
		callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
 

	
 
		/* Restore the cargo type */
 
		v->cargo_type = temp_cid;
 
		v->cargo_subtype = temp_subtype;
 
	}
 

	
 
	if (callback == CALLBACK_FAILED) {
 
		/* If the callback failed, or wasn't executed, use the aircraft's
 
		 * default cargo capacity */
 
		pass = AircraftDefaultCargoCapacity(new_cid, v->engine_type);
 
	} else {
 
		pass = callback;
 
	}
 
	_returned_refit_capacity = pass;
 

	
 
	cost = 0;
 
	if (IsHumanPlayer(v->owner) && new_cid != v->cargo_type) {
 
		cost = GetRefitCost(v->engine_type);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		Vehicle *u;
 
		v->cargo_cap = pass;
 

	
 
		u = v->next;
 
		mail = (new_cid != CT_PASSENGERS) ? 0 : avi->mail_capacity;
 
		u->cargo_cap = mail;
 
		if (v->cargo_type == new_cid) {
 
			v->cargo_count = min(pass, v->cargo_count);
 
			u->cargo_count = min(mail, u->cargo_count);
 
		} else {
 
			v->cargo_count = 0;
 
			u->cargo_count = 0;
 
		}
 
		v->cargo_type = new_cid;
 
		v->cargo_subtype = new_subtype;
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		RebuildVehicleLists();
 
	}
 

	
 
	return cost;
 
}
 

	
 

	
 
static void CheckIfAircraftNeedsService(Vehicle *v)
 
{
 
	const Station* st;
 

	
 
	if (_patches.servint_aircraft == 0) return;
 
	if (!VehicleNeedsService(v)) return;
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT &&
 
			v->current_order.flags & OF_HALT_IN_DEPOT)
 
		return;
 

	
 
	if (_patches.gotodepot && VehicleHasDepotOrders(v)) return;
 

	
 
	if (IsAircraftInHangar(v)) {
 
		VehicleServiceInDepot(v);
 
		return;
 
	}
 

	
 
	st = GetStation(v->current_order.dest);
 
	// only goto depot if the target airport has terminals (eg. it is airport)
 
	if (IsValidStation(st) && st->airport_tile != 0 && GetAirport(st->airport_type)->terminals != NULL) {
 
//		printf("targetairport = %d, st->index = %d\n", v->u.air.targetairport, st->index);
 
//		v->u.air.targetairport = st->index;
 
		v->current_order.type = OT_GOTO_DEPOT;
 
		v->current_order.flags = OF_NON_STOP;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	} else if (v->current_order.type == OT_GOTO_DEPOT) {
 
		v->current_order.type = OT_DUMMY;
 
		v->current_order.flags = 0;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 
}
 

	
 
void OnNewDay_Aircraft(Vehicle *v)
 
{
 
	int32 cost;
 

	
 
	if (!IsNormalAircraft(v)) return;
 

	
 
	if ((++v->day_counter & 7) == 0) DecreaseVehicleValue(v);
 

	
 
	CheckOrders(v);
 

	
 
	CheckVehicleBreakdown(v);
 
	AgeVehicle(v);
 
	CheckIfAircraftNeedsService(v);
 

	
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	cost = AircraftVehInfo(v->engine_type)->running_cost * _price.aircraft_running / 364;
 

	
 
	v->profit_this_year -= cost >> 8;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_RUN);
 
	SubtractMoneyFromPlayerFract(v->owner, cost);
 

	
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
 
}
 

	
 
void AircraftYearlyLoop(void)
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Aircraft && IsNormalAircraft(v)) {
 
			v->profit_last_year = v->profit_this_year;
 
			v->profit_this_year = 0;
 
			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		}
 
	}
 
}
 

	
 
static void AgeAircraftCargo(Vehicle *v)
 
{
 
	if (_age_cargo_skip_counter != 0) return;
 

	
 
	do {
 
		if (v->cargo_days != 0xFF) v->cargo_days++;
 
		v = v->next;
 
	} while (v != NULL);
 
}
 

	
 
static void HelicopterTickHandler(Vehicle *v)
 
{
 
	Vehicle *u;
 
	int tick,spd;
 
	SpriteID img;
 

	
 
	u = v->next->next;
 

	
 
	if (u->vehstatus & VS_HIDDEN) return;
 

	
 
	// if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
 
	// loading/unloading at a terminal or stopped
 
	if (v->current_order.type == OT_LOADING || (v->vehstatus & VS_STOPPED)) {
 
		if (u->cur_speed != 0) {
 
			u->cur_speed++;
 
			if (u->cur_speed >= 0x80 && u->u.air.state == HRS_ROTOR_MOVING_3) {
 
				u->cur_speed = 0;
 
			}
 
		}
 
	} else {
 
		if (u->cur_speed == 0)
 
			u->cur_speed = 0x70;
 

	
 
		if (u->cur_speed >= 0x50)
 
			u->cur_speed--;
 
	}
 

	
 
	tick = ++u->tick_counter;
 
	spd = u->cur_speed >> 4;
 

	
 
	if (spd == 0) {
 
		u->u.air.state = HRS_ROTOR_STOPPED;
 
		img = GetRotorImage(v);
 
		if (u->cur_image == img) return;
 
	} else if (tick >= spd) {
 
		u->tick_counter = 0;
 
		u->u.air.state++;
 
		if (u->u.air.state > HRS_ROTOR_MOVING_3) u->u.air.state = HRS_ROTOR_MOVING_1;
 
		img = GetRotorImage(v);
 
	} else {
 
		return;
 
	}
 

	
 
	u->cur_image = img;
 

	
 
	BeginVehicleMove(u);
 
	VehiclePositionChanged(u);
 
	EndVehicleMove(u);
 
}
 

	
 
static void SetAircraftPosition(Vehicle *v, int x, int y, int z)
 
{
 
	Vehicle *u;
 
	int safe_x;
 
	int safe_y;
 

	
 
	v->x_pos = x;
 
	v->y_pos = y;
 
	v->z_pos = z;
 

	
 
	v->cur_image = GetAircraftImage(v, v->direction);
 
	if (v->subtype == AIR_HELICOPTER) v->next->next->cur_image = GetRotorImage(v);
 

	
 
	BeginVehicleMove(v);
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 

	
 
	u = v->next;
 

	
 
	safe_x = clamp(x, 0, MapMaxX() * TILE_SIZE);
 
	safe_y = clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
 
	u->x_pos = x;
 
	u->y_pos = y - ((v->z_pos-GetSlopeZ(safe_x, safe_y)) >> 3);;
 

	
 
	safe_y = clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
 
	u->z_pos = GetSlopeZ(safe_x, safe_y);
 
	u->cur_image = v->cur_image;
 

	
 
	BeginVehicleMove(u);
 
	VehiclePositionChanged(u);
 
	EndVehicleMove(u);
 

	
 
	u = u->next;
 
	if (u != NULL) {
 
		u->x_pos = x;
 
		u->y_pos = y;
 
		u->z_pos = z + 5;
 

	
 
		BeginVehicleMove(u);
 
		VehiclePositionChanged(u);
 
		EndVehicleMove(u);
 
	}
 
}
 

	
 
/** Handle Aircraft specific tasks when a an Aircraft enters a hangar
 
 * @param *v Vehicle that enters the hangar
 
 */
 
void HandleAircraftEnterHangar(Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	v->subspeed = 0;
 
	v->progress = 0;
 

	
 
	u = v->next;
 
	u->vehstatus |= VS_HIDDEN;
 
	u = u->next;
 
	if (u != NULL) {
 
		u->vehstatus |= VS_HIDDEN;
 
		u->cur_speed = 0;
 
	}
 

	
 
	SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
 
}
 

	
 
static void PlayAircraftSound(const Vehicle* v)
 
{
 
	if (!PlayVehicleSound(v, VSE_START)) {
 
		SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
 
	}
 
}
 

	
 
static bool UpdateAircraftSpeed(Vehicle *v)
 
{
 
	uint spd = v->acceleration * 2;
 
	byte t;
 

	
 
	v->subspeed = (t=v->subspeed) + (byte)spd;
 
	spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), v->max_speed);
 

	
 
	// adjust speed for broken vehicles
 
	if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, 27);
 

	
 
	//updates statusbar only if speed have changed to save CPU time
 
	if (spd != v->cur_speed) {
 
		v->cur_speed = spd;
 
		if (_patches.vehicle_speed)
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 

	
 
	if (!(v->direction & 1)) spd = spd * 3 / 4;
 

	
 
	if (spd == 0) return false;
 

	
 
	if ((byte)++spd == 0) return true;
 

	
 
	v->progress = (t = v->progress) - (byte)spd;
 

	
 
	return t < v->progress;
 
}
 

	
 
// get Aircraft running altitude
 
static byte GetAircraftFlyingAltitude(const Vehicle *v)
 
{
 
	switch (v->max_speed) {
 
		case 37: return 162;
 
		case 74: return 171;
 
		default: return 180;
 
	}
 
}
 

	
 
static bool AircraftController(Vehicle *v)
 
{
 
	Station *st;
 
	const AirportMovingData *amd;
 
	Vehicle *u;
 
	byte z, maxz, curz;
 
	Direction newdir;
 
	GetNewVehiclePosResult gp;
 
	uint dist;
 
	int x,y;
 

	
 
	st = GetStation(v->u.air.targetairport);
 

	
 
	// prevent going to 0,0 if airport is deleted.
 
	{
 
		TileIndex tile = st->airport_tile;
 

	
 
		if (tile == 0) tile = st->xy;
 
		// xy of destination
 
		x = TileX(tile) * TILE_SIZE;
 
		y = TileY(tile) * TILE_SIZE;
 
	}
 

	
 
	// get airport moving data
 
	amd = GetAirportMovingData(st->airport_type, v->u.air.pos);
 

	
 
	// Helicopter raise
 
	if (amd->flag & AMED_HELI_RAISE) {
 
		u = v->next->next;
 

	
 
		// Make sure the rotors don't rotate too fast
 
		if (u->cur_speed > 32) {
 
			v->cur_speed = 0;
 
			if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
 
		} else {
 
			u->cur_speed = 32;
 
			if (UpdateAircraftSpeed(v)) {
 
				v->tile = 0;
 

	
 
				// Reached altitude?
 
				if (v->z_pos >= 184) {
 
					v->cur_speed = 0;
 
					return true;
 
				}
 
				SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos+1);
 
			}
 
		}
 
		return false;
 
	}
 

	
 
	// Helicopter landing.
 
	if (amd->flag & AMED_HELI_LOWER) {
 
		if (UpdateAircraftSpeed(v)) {
 
			if (st->airport_tile == 0) {
 
				// FIXME - AircraftController -> if station no longer exists, do not land
 
				// helicopter will circle until sign disappears, then go to next order
 
				// * what to do when it is the only order left, right now it just stays in 1 place
 
				v->u.air.state = FLYING;
 
				AircraftNextAirportPos_and_Order(v);
 
				return false;
 
			}
 

	
 
			// Vehicle is now at the airport.
 
			v->tile = st->airport_tile;
 

	
 
			// Find altitude of landing position.
 
			z = GetSlopeZ(x, y) + 1;
 
			if (st->airport_type == AT_OILRIG) z += 54;
 
			if (st->airport_type == AT_HELIPORT) z += 60;
 

	
 
			if (z == v->z_pos) {
 
				u = v->next->next;
 

	
 
				// Increase speed of rotors. When speed is 80, we've landed.
 
				if (u->cur_speed >= 80) return true;
 
				u->cur_speed += 4;
 
			} else if (v->z_pos > z) {
 
				SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos-1);
 
			} else {
 
				SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos+1);
 
			}
 
		}
 
		return false;
 
	}
 

	
 
	// Get distance from destination pos to current pos.
 
	dist = myabs(x + amd->x - v->x_pos) +  myabs(y + amd->y - v->y_pos);
 

	
 
	// Need exact position?
 
	if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U))
 
		return true;
 

	
 
	// At final pos?
 
	if (dist == 0) {
 
		DirDiff dirdiff;
 

	
 
		if (v->cur_speed > 12) v->cur_speed = 12;
 

	
 
		// Change direction smoothly to final direction.
 
		dirdiff = DirDifference(amd->direction, v->direction);
 
		// if distance is 0, and plane points in right direction, no point in calling
 
		// UpdateAircraftSpeed(). So do it only afterwards
 
		if (dirdiff == DIRDIFF_SAME) {
 
			v->cur_speed = 0;
 
			return true;
 
		}
 

	
 
		if (!UpdateAircraftSpeed(v)) return false;
 

	
 
		v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
 
		v->cur_speed >>= 1;
 

	
 
		SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
 
		return false;
 
	}
 

	
 
	if (!(amd->flag & AMED_NOSPDCLAMP) && v->cur_speed > 12) v->cur_speed = 12;
 

	
 
	if (!UpdateAircraftSpeed(v)) return false;
 

	
 
	if (v->load_unload_time_rem != 0) v->load_unload_time_rem--;
 

	
 
	// Turn. Do it slowly if in the air.
 
	newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
 
	if (newdir != v->direction) {
 
		if (amd->flag & AMED_SLOWTURN) {
 
			if (v->load_unload_time_rem == 0) v->load_unload_time_rem = 8;
 
			v->direction = newdir;
 
		} else {
 
			v->cur_speed >>= 1;
 
			v->direction = newdir;
 
		}
 
	}
 

	
 
	// Move vehicle.
 
	GetNewVehiclePos(v, &gp);
 
	v->tile = gp.new_tile;
 

	
 
	// If vehicle is in the air, use tile coordinate 0.
 
	if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
 

	
 
	// Adjust Z for land or takeoff?
 
	z = v->z_pos;
 

	
 
	if (amd->flag & AMED_TAKEOFF) {
 
		z += 2;
 
		maxz = GetAircraftFlyingAltitude(v);
 
		if (z > maxz) z = maxz;
 
	}
 

	
 
	if (amd->flag & AMED_LAND) {
 
		if (st->airport_tile == 0) {
 
			v->u.air.state = FLYING;
 
			AircraftNextAirportPos_and_Order(v);
 
			// get aircraft back on running altitude
 
			SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
 
			return false;
 
		}
 

	
 
		curz = GetSlopeZ(x, y) + 1;
 

	
 
		if (curz > z) {
 
			z++;
 
		} else {
 
			int t = max(1U, dist - 4);
 

	
 
			z -= ((z - curz) + t - 1) / t;
 
			if (z < curz) z = curz;
 
		}
 
	}
 

	
 
	// We've landed. Decrase speed when we're reaching end of runway.
 
	if (amd->flag & AMED_BRAKE) {
 
		curz = GetSlopeZ(x, y) + 1;
 

	
 
		if (z > curz) {
 
			z--;
 
		} else if (z < curz) {
 
			z++;
 
		}
 

	
 
		if (dist < 64 && v->cur_speed > 12) v->cur_speed -= 4;
 
	}
 

	
 
	SetAircraftPosition(v, gp.x, gp.y, z);
 
	return false;
 
}
 

	
 

	
 
static void HandleCrashedAircraft(Vehicle *v)
 
{
 
	uint32 r;
 
	Station *st;
 
	int z;
 

	
 
	v->u.air.crashed_counter++;
 

	
 
	st = GetStation(v->u.air.targetairport);
 

	
 
	// make aircraft crash down to the ground
 
	if (v->u.air.crashed_counter < 500 && st->airport_tile==0 && ((v->u.air.crashed_counter % 3) == 0) ) {
 
		z = GetSlopeZ(v->x_pos, v->y_pos);
 
		v->z_pos -= 1;
 
		if (v->z_pos == z) {
 
			v->u.air.crashed_counter = 500;
 
			v->z_pos++;
 
		}
 
	}
 

	
 
	if (v->u.air.crashed_counter < 650) {
 
		if (CHANCE16R(1,32,r)) {
 
			static const DirDiff delta[] = {
 
				DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
 
			};
 

	
 
			v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
 
			SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
 
			r = Random();
 
			CreateEffectVehicleRel(v,
 
				GB(r, 0, 4) + 4,
 
				GB(r, 4, 4) + 4,
 
				GB(r, 8, 4),
 
				EV_EXPLOSION_SMALL);
 
		}
 
	} else if (v->u.air.crashed_counter >= 10000) {
 
		// remove rubble of crashed airplane
 

	
 
		// clear runway-in on all airports, set by crashing plane
 
		// small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
 
		// but they all share the same number
 
		CLRBITS(st->airport_flags, RUNWAY_IN_block);
 
		CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block); // commuter airport
 
		CLRBITS(st->airport_flags, RUNWAY_IN2_block); // intercontinental
 

	
 
		BeginVehicleMove(v);
 
		EndVehicleMove(v);
 

	
 
		DoDeleteAircraft(v);
 
	}
 
}
 

	
 
static void HandleBrokenAircraft(Vehicle *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->vehstatus |= VS_AIRCRAFT_BROKEN;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	}
 
}
 

	
 

	
 
static void HandleAircraftSmoke(Vehicle *v)
 
{
 
	static const struct {
 
		int8 x;
 
		int8 y;
 
	} smoke_pos[] = {
 
		{  5,  5 },
 
		{  6,  0 },
 
		{  5, -5 },
 
		{  0, -6 },
 
		{ -5, -5 },
 
		{ -6,  0 },
 
		{ -5,  5 },
 
		{  0,  6 }
 
	};
 

	
 
	if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
 

	
 
	if (v->cur_speed < 10) {
 
		v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
 
		v->breakdown_ctr = 0;
 
		return;
 
	}
 

	
 
	if ((v->tick_counter & 0x1F) == 0) {
 
		CreateEffectVehicleRel(v,
 
			smoke_pos[v->direction].x,
 
			smoke_pos[v->direction].y,
 
			2,
 
			EV_SMOKE
 
		);
 
	}
 
}
 

	
 
static void ProcessAircraftOrder(Vehicle *v)
 
{
 
	const Order *order;
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_DEPOT:
 
			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return;
 
			if (v->current_order.flags & OF_SERVICE_IF_NEEDED &&
 
					!VehicleNeedsService(v)) {
 
				v->cur_order_index++;
 
			}
 
			break;
 

	
 
		case OT_LOADING: return;
 

	
 
		default: break;
 
	}
 

	
 
	if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
 

	
 
	order = GetVehicleOrder(v, v->cur_order_index);
 

	
 
	if (order == NULL) {
 
		v->current_order.type = OT_NOTHING;
 
		v->current_order.flags = 0;
 
		return;
 
	}
 

	
 
	if (order->type == OT_DUMMY && !CheckForValidOrders(v)) CrashAirplane(v);
 

	
 
	if (order->type  == v->current_order.type  &&
 
			order->flags == v->current_order.flags &&
 
			order->dest  == v->current_order.dest)
 
		return;
 

	
 
	v->current_order = *order;
 

	
 
	// orders are changed in flight, ensure going to the right station
 
	if (order->type == OT_GOTO_STATION && v->u.air.state == FLYING) {
 
		AircraftNextAirportPos_and_Order(v);
 
		v->u.air.targetairport = order->dest;
 
	}
 

	
 
	InvalidateVehicleOrder(v);
 

	
 
	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
 
}
 

	
 
/** Mark all views dirty for an aircraft.
 
 * @param v vehicle to be redrawn.
 
 */
 
static void MarkAircraftDirty(Vehicle *v)
 
{
 
		v->cur_image = GetAircraftImage(v, v->direction);
 
		if (v->subtype == AIR_HELICOPTER) v->next->next->cur_image = GetRotorImage(v);
 
		MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
 
}
 

	
 
static void HandleAircraftLoading(Vehicle *v, int mode)
 
{
 
	if (v->current_order.type == OT_NOTHING) return;
 

	
 
	if (v->current_order.type != OT_DUMMY) {
 
		if (v->current_order.type != OT_LOADING) return;
 
	switch (v->current_order.type) {
 
		case OT_LOADING:
 
		if (mode != 0) return;
 
		if (--v->load_unload_time_rem != 0) return;
 

	
 
		if (CanFillVehicle(v) && (v->current_order.flags & OF_FULL_LOAD ||
 
				(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED)))) {
 
			if (CanFillVehicle(v) && (
 
						v->current_order.flags & OF_FULL_LOAD ||
 
						(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED))
 
					)) {
 
			SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC);
 
			if (LoadUnloadVehicle(v, false)) {
 
				InvalidateWindow(WC_AIRCRAFT_LIST, v->owner);
 
				MarkAircraftDirty(v);
 
			}
 
			return;
 
		}
 

	
 
		{
 
			Order b = v->current_order;
 
			v->current_order.type = OT_NOTHING;
 
			v->current_order.flags = 0;
 
			MarkAircraftDirty(v);
 
			if (!(b.flags & OF_NON_STOP)) return;
 
			break;
 

	
 
		case OT_DUMMY: break;
 

	
 
		default: return;
 
		}
 
	}
 

	
 
	v->cur_order_index++;
 
	InvalidateVehicleOrder(v);
 
}
 

	
 
static void CrashAirplane(Vehicle *v)
 
{
 
	uint16 amt;
 
	Station *st;
 
	StringID newsitem;
 

	
 
	v->vehstatus |= VS_CRASHED;
 
	v->u.air.crashed_counter = 0;
 

	
 
	CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
 

	
 
	InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 

	
 
	amt = 2;
 
	if (v->cargo_type == CT_PASSENGERS) amt += v->cargo_count;
 
	SetDParam(0, amt);
 

	
 
	v->cargo_count = 0;
 
	v->next->cargo_count = 0,
 
	st = GetStation(v->u.air.targetairport);
 
	if (st->airport_tile == 0) {
 
		newsitem = STR_PLANE_CRASH_OUT_OF_FUEL;
 
	} else {
 
		SetDParam(1, st->index);
 
		newsitem = STR_A034_PLANE_CRASH_DIE_IN_FIREBALL;
 
	}
 

	
 
	SetDParam(1, st->index);
 
	AddNewsItem(newsitem,
 
		NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
 
		v->index,
 
		0);
 

	
 
	SndPlayVehicleFx(SND_12_EXPLOSION, v);
 
}
 

	
 
static void MaybeCrashAirplane(Vehicle *v)
 
{
 
	Station *st;
 
	uint16 prob;
 
	uint i;
 

	
 
	st = GetStation(v->u.air.targetairport);
 

	
 
	//FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports
 
	prob = 0x10000 / 1500;
 
	if (((st->airport_type == AT_SMALL) || (st->airport_type == AT_COMMUTER)) && (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) {
 
		prob = 0x10000 / 20;
 
	}
 

	
 
	if (GB(Random(), 0, 16) > prob) return;
 

	
 
	// Crash the airplane. Remove all goods stored at the station.
 
	for (i = 0; i != NUM_CARGO; i++) {
 
		st->goods[i].rating = 1;
 
		SB(st->goods[i].waiting_acceptance, 0, 12, 0);
 
	}
 

	
 
	CrashAirplane(v);
 
}
 

	
 
// we've landed and just arrived at a terminal
 
static void AircraftEntersTerminal(Vehicle *v)
 
{
 
	Station *st;
 
	Order old_order;
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT) return;
 

	
 
	st = GetStation(v->u.air.targetairport);
 
	v->last_station_visited = v->u.air.targetairport;
 

	
 
	/* Check if station was ever visited before */
 
	if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
 
		uint32 flags;
 

	
 
		st->had_vehicle_of_type |= HVOT_AIRCRAFT;
 
		SetDParam(0, st->index);
 
		// show newsitem of celebrating citizens
 
		flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0);
 
		AddNewsItem(
 
			STR_A033_CITIZENS_CELEBRATE_FIRST,
 
			flags,
 
			v->index,
 
			0);
 
	}
 

	
 
	old_order = v->current_order;
 
	v->BeginLoading();
 
	v->current_order.flags = 0;
 

	
 
	if (old_order.type == OT_GOTO_STATION &&
 
			v->current_order.dest == v->last_station_visited) {
 
		v->current_order.flags =
 
			(old_order.flags & (OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER)) | OF_NON_STOP;
 
	}
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC);
 
	LoadUnloadVehicle(v, true);
 
	MarkAircraftDirty(v);
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
 
}
 

	
 
static void AircraftLand(Vehicle *v)
 
{
 
	v->sprite_width = v->sprite_height = 2;
 
}
 

	
 
static void AircraftLandAirplane(Vehicle *v)
 
{
 
	AircraftLand(v);
 
	if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
 
		SndPlayVehicleFx(SND_17_SKID_PLANE, v);
 
	}
 
	MaybeCrashAirplane(v);
 
}
 

	
 
// set the right pos when heading to other airports after takeoff
 
static void AircraftNextAirportPos_and_Order(Vehicle *v)
 
{
 
	const Station* st;
 
	const AirportFTAClass *apc;
 

	
 
	if (v->current_order.type == OT_GOTO_STATION ||
 
			v->current_order.type == OT_GOTO_DEPOT)
 
		v->u.air.targetairport = v->current_order.dest;
 

	
 
	st = GetStation(v->u.air.targetairport);
 
	apc = GetAirport(st->airport_type);
 
	v->u.air.pos = v->u.air.previous_pos = apc->entry_point;
 
}
 

	
 
static void AircraftLeaveHangar(Vehicle *v)
 
{
 
	v->cur_speed = 0;
 
	v->subspeed = 0;
 
	v->progress = 0;
 
	v->direction = DIR_SE;
 
	v->vehstatus &= ~VS_HIDDEN;
 
	{
 
		Vehicle *u = v->next;
 
		u->vehstatus &= ~VS_HIDDEN;
 

	
 
		// Rotor blades
 
		u = u->next;
 
		if (u != NULL) {
 
			u->vehstatus &= ~VS_HIDDEN;
 
			u->cur_speed = 80;
 
		}
 
	}
 

	
 
	VehicleServiceInDepot(v);
 
	SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
 
	InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
 
}
 

	
 

	
 
////////////////////////////////////////////////////////////////////////////////
 
///////////////////   AIRCRAFT MOVEMENT SCHEME  ////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////
 
static void AircraftEventHandler_EnterTerminal(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	AircraftEntersTerminal(v);
 
	v->u.air.state = apc->layout[v->u.air.pos].heading;
 
}
 

	
 
static void AircraftEventHandler_EnterHangar(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	VehicleEnterDepot(v);
 
	v->u.air.state = apc->layout[v->u.air.pos].heading;
 
}
 

	
 
// In an Airport Hangar
 
static void AircraftEventHandler_InHangar(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	// if we just arrived, execute EnterHangar first
 
	if (v->u.air.previous_pos != v->u.air.pos) {
 
		AircraftEventHandler_EnterHangar(v, apc);
 
		return;
 
	}
 

	
 
	// if we were sent to the depot, stay there
 
	if (v->current_order.type == OT_GOTO_DEPOT && (v->vehstatus & VS_STOPPED)) {
 
		v->current_order.type = OT_NOTHING;
 
		v->current_order.flags = 0;
 
		return;
 
	}
 

	
 
	if (v->current_order.type != OT_GOTO_STATION &&
 
			v->current_order.type != OT_GOTO_DEPOT)
 
		return;
 

	
 
	// if the block of the next position is busy, stay put
 
	if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
 

	
 
	// We are already at the target airport, we need to find a terminal
 
	if (v->current_order.dest == v->u.air.targetairport) {
 
		// FindFreeTerminal:
 
		// 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal
 
		if (v->subtype == AIR_HELICOPTER) {
 
			if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
 
		} else {
 
			if (!AirportFindFreeTerminal(v, apc)) return; // airplane
 
		}
 
	} else { // Else prepare for launch.
 
		// airplane goto state takeoff, helicopter to helitakeoff
 
		v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
 
	}
 
	AircraftLeaveHangar(v);
 
	AirportMove(v, apc);
 
}
 

	
 
// At one of the Airport's Terminals
 
static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	// if we just arrived, execute EnterTerminal first
 
	if (v->u.air.previous_pos != v->u.air.pos) {
 
		AircraftEventHandler_EnterTerminal(v, apc);
 
		// on an airport with helipads, a helicopter will always land there
 
		// and get serviced at the same time - patch setting
 
		if (_patches.serviceathelipad) {
 
			if (v->subtype == AIR_HELICOPTER && apc->helipads != NULL) {
 
				// an exerpt of ServiceAircraft, without the invisibility stuff
 
				v->date_of_last_service = _date;
 
				v->breakdowns_since_last_service = 0;
 
				v->reliability = GetEngine(v->engine_type)->reliability;
 
				InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
			}
 
		}
 
		return;
 
	}
 

	
 
	if (v->current_order.type == OT_NOTHING) return;
 

	
 
	// if the block of the next position is busy, stay put
 
	if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
 

	
 
	// airport-road is free. We either have to go to another airport, or to the hangar
 
	// ---> start moving
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_STATION: // ready to fly to another airport
 
			// airplane goto state takeoff, helicopter to helitakeoff
 
			v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
 
			break;
 
		case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
 
			if (v->current_order.dest == v->u.air.targetairport) {
 
				v->u.air.state = HANGAR;
 
			} else {
 
				v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
 
			}
 
			break;
 
		default:  // orders have been deleted (no orders), goto depot and don't bother us
 
			v->current_order.type = OT_NOTHING;
 
			v->current_order.flags = 0;
 
			v->u.air.state = HANGAR;
 
	}
 
	AirportMove(v, apc);
 
}
 

	
 
static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	assert("OK, you shouldn't be here, check your Airport Scheme!" && 0);
 
}
 

	
 
static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *apc) {
 
	PlayAircraftSound(v); // play takeoffsound for airplanes
 
	v->u.air.state = STARTTAKEOFF;
 
}
 

	
 
static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	v->sprite_width = v->sprite_height = 24; // ??? no idea what this is
 
	v->u.air.state = ENDTAKEOFF;
 
}
 

	
 
static void AircraftEventHandler_EndTakeOff(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	v->u.air.state = FLYING;
 
	// get the next position to go to, differs per airport
 
	AircraftNextAirportPos_and_Order(v);
 
}
 

	
 
static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	const Player* p = GetPlayer(v->owner);
 
	v->sprite_width = v->sprite_height = 24; // ??? no idea what this is
 
	v->u.air.state = FLYING;
 
	// get the next position to go to, differs per airport
 
	AircraftNextAirportPos_and_Order(v);
 

	
 
	// check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
 
	// unless it is due for renewal but the engine is no longer available
 
	if (v->owner == _local_player && (
 
				EngineHasReplacementForPlayer(p, v->engine_type) ||
 
				((p->engine_renew && v->age - v->max_age > p->engine_renew_months * 30) &&
 
				HASBIT(GetEngine(v->engine_type)->player_avail, _local_player))
 
			)) {
 
		_current_player = _local_player;
 
		DoCommandP(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_SHOW_NO_ERROR);
 
		_current_player = OWNER_NONE;
 
	}
 
}
 

	
 
static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	Station *st;
 
	byte landingtype;
 
	AirportFTA *current;
 
	uint16 tcur_speed, tsubspeed;
 

	
 
	st = GetStation(v->u.air.targetairport);
 
	// flying device is accepted at this station
 
	// small airport --> no helicopters (AIRCRAFT_ONLY)
 
	// all other airports --> all types of flying devices (ALL)
 
	// heliport/oilrig, etc --> no airplanes (HELICOPTERS_ONLY)
 
	// runway busy or not allowed to use this airstation, circle
 
	if (v->subtype != apc->acc_planes &&
 
			st->airport_tile != 0 &&
 
			(st->owner == OWNER_NONE || st->owner == v->owner)) {
 
		// {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
 
		// if it is an airplane, look for LANDING, for helicopter HELILANDING
 
		// it is possible to choose from multiple landing runways, so loop until a free one is found
 
		landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
 
		current = apc->layout[v->u.air.pos].next;
 
		while (current != NULL) {
 
			if (current->heading == landingtype) {
 
				// save speed before, since if AirportHasBlock is false, it resets them to 0
 
				// we don't want that for plane in air
 
				// hack for speed thingie
 
				tcur_speed = v->cur_speed;
 
				tsubspeed = v->subspeed;
 
				if (!AirportHasBlock(v, current, apc)) {
 
					v->u.air.state = landingtype; // LANDING / HELILANDING
 
					// it's a bit dirty, but I need to set position to next position, otherwise
 
					// if there are multiple runways, plane won't know which one it took (because
 
					// they all have heading LANDING). And also occupy that block!
 
					v->u.air.pos = current->next_position;
 
					SETBITS(st->airport_flags, apc->layout[v->u.air.pos].block);
 
					return;
 
				}
 
				v->cur_speed = tcur_speed;
 
				v->subspeed = tsubspeed;
 
			}
 
			current = current->next;
 
		}
 
	}
 
	v->u.air.state = FLYING;
 
	v->u.air.pos = apc->layout[v->u.air.pos].next_position;
 
}
 

	
 
static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	const Player* p = GetPlayer(v->owner);
 
	AircraftLandAirplane(v);  // maybe crash airplane
 
	v->u.air.state = ENDLANDING;
 
	// check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
 
	if (v->current_order.type != OT_GOTO_DEPOT && v->owner == _local_player) {
 
		// only the vehicle owner needs to calculate the rest (locally)
 
		if (EngineHasReplacementForPlayer(p, v->engine_type) ||
 
			(p->engine_renew && v->age - v->max_age > (p->engine_renew_months * 30))) {
 
			// send the aircraft to the hangar at next airport
 
			_current_player = _local_player;
 
			DoCommandP(v->tile, v->index, DEPOT_SERVICE, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_SHOW_NO_ERROR);
 
			_current_player = OWNER_NONE;
 
		}
 
	}
 
}
 

	
 
static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	AircraftLand(v); // helicopters don't crash
 
	v->u.air.state = HELIENDLANDING;
 
}
 

	
 
static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	// next block busy, don't do a thing, just wait
 
	if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
 

	
 
	// if going to terminal (OT_GOTO_STATION) choose one
 
	// 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
 
	// 2. not going for terminal (but depot, no order),
 
	// --> get out of the way to the hangar.
 
	if (v->current_order.type == OT_GOTO_STATION) {
 
		if (AirportFindFreeTerminal(v, apc)) return;
 
	}
 
	v->u.air.state = HANGAR;
 

	
 
}
 

	
 
static void AircraftEventHandler_HeliEndLanding(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	// next block busy, don't do a thing, just wait
 
	if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
 

	
 
	// if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
 
	// 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
 
	// 2. not going for terminal (but depot, no order),
 
	// --> get out of the way to the hangar IF there are terminals on the airport.
 
	// --> else TAKEOFF
 
	// the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
 
	// must go to a hangar.
 
	if (v->current_order.type == OT_GOTO_STATION) {
 
		if (AirportFindFreeHelipad(v, apc)) return;
 
	}
 
	v->u.air.state = (apc->nof_depots != 0) ? HANGAR : HELITAKEOFF;
 
}
 

	
 
typedef void AircraftStateHandler(Vehicle *v, const AirportFTAClass *apc);
 
static AircraftStateHandler * const _aircraft_state_handlers[] = {
 
	AircraftEventHandler_General,        // TO_ALL         =  0
 
	AircraftEventHandler_InHangar,       // HANGAR         =  1
 
	AircraftEventHandler_AtTerminal,     // TERM1          =  2
 
	AircraftEventHandler_AtTerminal,     // TERM2          =  3
 
	AircraftEventHandler_AtTerminal,     // TERM3          =  4
 
	AircraftEventHandler_AtTerminal,     // TERM4          =  5
 
	AircraftEventHandler_AtTerminal,     // TERM5          =  6
 
	AircraftEventHandler_AtTerminal,     // TERM6          =  7
 
	AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
 
	AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
 
	AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
 
	AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
 
	AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
 
	AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
 
	AircraftEventHandler_Flying,         // FLYING         = 14
 
	AircraftEventHandler_Landing,        // LANDING        = 15
 
	AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
 
	AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
 
	AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
 
	AircraftEventHandler_AtTerminal,     // TERM7          = 19
 
	AircraftEventHandler_AtTerminal,     // TERM8          = 20
 
	AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
 
	AircraftEventHandler_AtTerminal,     // HELIPAD4       = 22
 
};
 

	
 
static void AirportClearBlock(const Vehicle *v, const AirportFTAClass *apc)
 
{
 
	// we have left the previous block, and entered the new one. Free the previous block
 
	if (apc->layout[v->u.air.previous_pos].block != apc->layout[v->u.air.pos].block) {
 
		Station *st = GetStation(v->u.air.targetairport);
 

	
 
		CLRBITS(st->airport_flags, apc->layout[v->u.air.previous_pos].block);
 
	}
 
}
 

	
 
static void AirportGoToNextPosition(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	// if aircraft is not in position, wait until it is
 
	if (!AircraftController(v)) return;
 

	
 
	AirportClearBlock(v, apc);
 
	AirportMove(v, apc); // move aircraft to next position
 
}
 

	
 
// gets pos from vehicle and next orders
 
static bool AirportMove(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	AirportFTA *current;
 
	byte prev_pos;
 

	
 
	// error handling
 
	if (v->u.air.pos >= apc->nofelements) {
 
		DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->u.air.pos, apc->nofelements-1);
 
		assert(v->u.air.pos < apc->nofelements);
 
	}
 

	
 
	current = &apc->layout[v->u.air.pos];
 
	// we have arrived in an important state (eg terminal, hangar, etc.)
 
	if (current->heading == v->u.air.state) {
 
		prev_pos = v->u.air.pos; // location could be changed in state, so save it before-hand
 
		_aircraft_state_handlers[v->u.air.state](v, apc);
 
		if (v->u.air.state != FLYING) v->u.air.previous_pos = prev_pos;
 
		return true;
 
	}
 

	
 
	v->u.air.previous_pos = v->u.air.pos; // save previous location
 

	
 
	// there is only one choice to move to
 
	if (current->next == NULL) {
 
		if (AirportSetBlocks(v, current, apc)) {
 
			v->u.air.pos = current->next_position;
 
		} // move to next position
 
		return false;
 
	}
 

	
 
	// there are more choices to choose from, choose the one that
 
	// matches our heading
 
	do {
 
		if (v->u.air.state == current->heading || current->heading == TO_ALL) {
 
			if (AirportSetBlocks(v, current, apc)) {
 
				v->u.air.pos = current->next_position;
 
			} // move to next position
 
			return false;
 
		}
 
		current = current->next;
 
	} while (current != NULL);
 

	
 
	DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d)", v->u.air.pos, v->u.air.state);
 
	DEBUG(misc, 0, "[Ap] airport entry point: %d, Vehicle: %d", apc->entry_point, v->index);
 
	assert(0);
 
	return false;
 
}
 

	
 
// returns true if the road ahead is busy, eg. you must wait before proceeding
 
static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
 
{
 
	const AirportFTA *reference = &apc->layout[v->u.air.pos];
 
	const AirportFTA *next = &apc->layout[current_pos->next_position];
 

	
 
	// same block, then of course we can move
 
	if (apc->layout[current_pos->position].block != next->block) {
 
		const Station *st = GetStation(v->u.air.targetairport);
 
		uint32 airport_flags = next->block;
 

	
 
		// check additional possible extra blocks
 
		if (current_pos != reference && current_pos->block != NOTHING_block) {
 
			airport_flags |= current_pos->block;
 
		}
 

	
 
		if (HASBITS(st->airport_flags, airport_flags)) {
 
			v->cur_speed = 0;
 
			v->subspeed = 0;
 
			return true;
 
		}
 
	}
 
	return false;
 
}
 

	
 
// returns true on success. Eg, next block was free and we have occupied it
 
static bool AirportSetBlocks(Vehicle *v, AirportFTA *current_pos, const AirportFTAClass *apc)
 
{
 
	AirportFTA *next = &apc->layout[current_pos->next_position];
 
	AirportFTA *reference = &apc->layout[v->u.air.pos];
 

	
 
	// if the next position is in another block, check it and wait until it is free
 
	if ((apc->layout[current_pos->position].block & next->block) != next->block) {
 
		uint32 airport_flags = next->block;
 
		Station* st = GetStation(v->u.air.targetairport);
 
		//search for all all elements in the list with the same state, and blocks != N
 
		// this means more blocks should be checked/set
 
			AirportFTA *current = current_pos;
 
		if (current == reference) current = current->next;
 
		while (current != NULL) {
 
			if (current->heading == current_pos->heading && current->block != 0) {
 
				airport_flags |= current->block;
 
				break;
 
			}
 
			current = current->next;
 
		};
 

	
 
		// if the block to be checked is in the next position, then exclude that from
 
		// checking, because it has been set by the airplane before
 
		if (current_pos->block == next->block) airport_flags ^= next->block;
 

	
 
		if (HASBITS(st->airport_flags, airport_flags)) {
 
			v->cur_speed = 0;
 
			v->subspeed = 0;
 
			return false;
 
		}
 

	
 
		if (next->block != NOTHING_block) {
 
			SETBITS(st->airport_flags, airport_flags); // occupy next block
 
		}
 
	}
 
	return true;
 
}
 

	
 
static bool FreeTerminal(Vehicle *v, byte i, byte last_terminal)
 
{
 
	Station *st = GetStation(v->u.air.targetairport);
 
	for (; i < last_terminal; i++) {
 
		if (!HASBIT(st->airport_flags, _airport_terminal_flag[i])) {
 
			// TERMINAL# HELIPAD#
 
			v->u.air.state = _airport_terminal_state[i]; // start moving to that terminal/helipad
 
			SETBIT(st->airport_flags, _airport_terminal_flag[i]); // occupy terminal/helipad
 
			return true;
 
		}
 
	}
 
	return false;
 
}
 

	
 
static uint GetNumTerminals(const AirportFTAClass *apc)
 
{
 
	uint num = 0;
 
	uint i;
 

	
 
	for (i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
 

	
 
	return num;
 
}
 

	
 
static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	/* example of more terminalgroups
 
	 * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
 
	 * Heading 255 denotes a group. We see 2 groups here:
 
	 * 1. group 0 -- TERM_GROUP1_block (check block)
 
	 * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
 
	 * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
 
	 * looks at the corresponding terminals of that group. If no free ones are found, other
 
	 * possible groups are checked (in this case group 1, since that is after group 0). If that
 
	 * fails, then attempt fails and plane waits
 
	 */
 
	if (apc->terminals[0] > 1) {
 
		Station *st = GetStation(v->u.air.targetairport);
 
		AirportFTA *temp = apc->layout[v->u.air.pos].next;
 

	
 
		while (temp != NULL) {
 
			if (temp->heading == 255) {
 
				if (!HASBITS(st->airport_flags, temp->block)) {
 
					int target_group;
 
					int i;
 
					int group_start = 0;
 
					int group_end;
 

	
 
					//read which group do we want to go to?
 
					//(the first free group)
 
					target_group = temp->next_position + 1;
 

	
 
					//at what terminal does the group start?
 
					//that means, sum up all terminals of
 
					//groups with lower number
 
					for (i = 1; i < target_group; i++)
 
						group_start += apc->terminals[i];
 

	
 
					group_end = group_start + apc->terminals[target_group];
 
					if (FreeTerminal(v, group_start, group_end)) return true;
 
				}
 
			} else {
 
				/* once the heading isn't 255, we've exhausted the possible blocks.
 
				 * So we cannot move */
 
				return false;
 
			}
 
			temp = temp->next;
 
		}
 
	}
 

	
 
	// if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max)
 
	return FreeTerminal(v, 0, GetNumTerminals(apc));
 
}
 

	
 
static uint GetNumHelipads(const AirportFTAClass *apc)
 
{
 
	uint num = 0;
 
	uint i;
 

	
 
	for (i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i];
 

	
 
	return num;
 
}
 

	
 

	
 
static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	// if an airport doesn't have helipads, use terminals
 
	if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc);
 

	
 
	// if there are more helicoptergroups, pick one, just as in AirportFindFreeTerminal()
 
	if (apc->helipads[0] > 1) {
 
		const Station* st = GetStation(v->u.air.targetairport);
 
		const AirportFTA* temp = apc->layout[v->u.air.pos].next;
 

	
 
		while (temp != NULL) {
 
			if (temp->heading == 255) {
 
				if (!HASBITS(st->airport_flags, temp->block)) {
 
					int target_group;
 
					int i;
 
					int group_start = 0;
 
					int group_end;
 

	
 
					//read which group do we want to go to?
 
					//(the first free group)
 
					target_group = temp->next_position + 1;
 

	
 
					//at what terminal does the group start?
 
					//that means, sum up all terminals of
 
					//groups with lower number
 
					for (i = 1; i < target_group; i++)
 
						group_start += apc->helipads[i];
 

	
 
					group_end = group_start + apc->helipads[target_group];
 
					if (FreeTerminal(v, group_start, group_end)) return true;
 
				}
 
			} else {
 
				/* once the heading isn't 255, we've exhausted the possible blocks.
 
				 * So we cannot move */
 
				return false;
 
			}
 
			temp = temp->next;
 
		}
 
	} else {
 
		// only 1 helicoptergroup, check all helipads
 
		// The blocks for helipads start after the last terminal (MAX_TERMINALS)
 
		return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS);
 
	}
 
	return false; // it shouldn't get here anytime, but just to be sure
 
}
 

	
 
static void AircraftEventHandler(Vehicle *v, int loop)
 
{
 
	v->tick_counter++;
 

	
 
	if (v->vehstatus & VS_CRASHED) {
 
		HandleCrashedAircraft(v);
 
		return;
 
	}
 

	
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	/* aircraft is broken down? */
 
	if (v->breakdown_ctr != 0) {
 
		if (v->breakdown_ctr <= 2) {
 
			HandleBrokenAircraft(v);
 
		} else {
 
			v->breakdown_ctr--;
 
		}
 
	}
 

	
 
	HandleAircraftSmoke(v);
 
	ProcessAircraftOrder(v);
 
	HandleAircraftLoading(v, loop);
 

	
 
	if (v->current_order.type >= OT_LOADING) return;
 

	
 
	// pass the right airport structure to the functions
 
	// DEREF_STATION gets target airport (Station *st), its type is passed to GetAirport
 
	// that returns the correct layout depending on type
 
	AirportGoToNextPosition(v, GetAirport(GetStation(v->u.air.targetairport)->airport_type));
 
}
 

	
 
void Aircraft_Tick(Vehicle *v)
 
{
 
	int i;
 

	
 
	if (!IsNormalAircraft(v)) return;
 

	
 
	if (v->subtype == AIR_HELICOPTER) HelicopterTickHandler(v);
 

	
 
	AgeAircraftCargo(v);
 

	
 
	for (i = 0; i != 6; i++) {
 
		AircraftEventHandler(v, i);
 
		if (v->type != VEH_Aircraft) // In case it was deleted
 
			break;
 
	}
 
}
 

	
 
void UpdateOilRig(void)
 
{
 
	Station* st;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		if (st->airport_type == 5) st->airport_type = AT_OILRIG;
 
	}
 
}
 

	
 
// need to be called to load aircraft from old version
 
void UpdateOldAircraft(void)
 
{
 
	Station *st;
 
	Vehicle *v_oldstyle;
src/ship_cmd.cpp
Show inline comments
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "ship.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "vehicle.h"
 
#include "command.h"
 
#include "pathfind.h"
 
#include "station_map.h"
 
#include "station.h"
 
#include "news.h"
 
#include "engine.h"
 
#include "player.h"
 
#include "sound.h"
 
#include "npf.h"
 
#include "depot.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_engine.h"
 
#include "water_map.h"
 
#include "yapf/yapf.h"
 
#include "debug.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_text.h"
 
#include "newgrf_sound.h"
 
#include "date.h"
 

	
 
static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
 

	
 
static const TrackBits _ship_sometracks[4] = {
 
	TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_LEFT,  // 0x19, // DIAGDIR_NE
 
	TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_LEFT,  // 0x16, // DIAGDIR_SE
 
	TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT, // 0x25, // DIAGDIR_SW
 
	TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_RIGHT, // 0x2A, // DIAGDIR_NW
 
};
 

	
 
static TrackBits GetTileShipTrackStatus(TileIndex tile)
 
{
 
	uint32 r = GetTileTrackStatus(tile, TRANSPORT_WATER);
 
	return TrackdirBitsToTrackBits((TrackdirBits)(TRACKDIR_BIT_MASK & (r | r >> 8)));
 
}
 

	
 
void DrawShipEngine(int x, int y, EngineID engine, SpriteID pal)
 
{
 
	int spritenum = ShipVehInfo(engine)->image_index;
 

	
 
	if (is_custom_sprite(spritenum)) {
 
		int sprite = GetCustomVehicleIcon(engine, DIR_W);
 

	
 
		if (sprite != 0) {
 
			DrawSprite(sprite, pal, x, y);
 
			return;
 
		}
 
		spritenum = orig_ship_vehicle_info[engine - SHIP_ENGINES_INDEX].image_index;
 
	}
 
	DrawSprite(6 + _ship_sprites[spritenum], pal, x, y);
 
}
 

	
 
int GetShipImage(const Vehicle* v, Direction direction)
 
{
 
	int spritenum = v->spritenum;
 

	
 
	if (is_custom_sprite(spritenum)) {
 
		int sprite = GetCustomVehicleSprite(v, direction);
 

	
 
		if (sprite != 0) return sprite;
 
		spritenum = orig_ship_vehicle_info[v->engine_type - SHIP_ENGINES_INDEX].image_index;
 
	}
 
	return _ship_sprites[spritenum] + direction;
 
}
 

	
 
static const Depot* FindClosestShipDepot(const Vehicle* v)
 
{
 
	const Depot* depot;
 
	const Depot* best_depot = NULL;
 
	uint dist;
 
	uint best_dist = (uint)-1;
 
	TileIndex tile;
 
	TileIndex tile2 = v->tile;
 

	
 
	if (_patches.new_pathfinding_all) {
 
		NPFFoundTargetData ftd;
 
		Trackdir trackdir = GetVehicleTrackdir(v);
 
		ftd = NPFRouteToDepotTrialError(v->tile, trackdir, TRANSPORT_WATER, v->owner, INVALID_RAILTYPE);
 
		if (ftd.best_bird_dist == 0) {
 
			best_depot = GetDepotByTile(ftd.node.tile); /* Found target */
 
		} else {
 
			best_depot = NULL; /* Did not find target */
 
		}
 
	} else {
 
		FOR_ALL_DEPOTS(depot) {
 
			tile = depot->xy;
 
			if (IsTileDepotType(tile, TRANSPORT_WATER) && IsTileOwner(tile, v->owner)) {
 
				dist = DistanceManhattan(tile, tile2);
 
				if (dist < best_dist) {
 
					best_dist = dist;
 
					best_depot = depot;
 
				}
 
			}
 
		}
 
	}
 
	return best_depot;
 
}
 

	
 
static void CheckIfShipNeedsService(Vehicle *v)
 
{
 
	const Depot* depot;
 

	
 
	if (_patches.servint_ships == 0) return;
 
	if (!VehicleNeedsService(v))     return;
 
	if (v->vehstatus & VS_STOPPED)   return;
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT &&
 
			v->current_order.flags & OF_HALT_IN_DEPOT)
 
		return;
 

	
 
	if (_patches.gotodepot && VehicleHasDepotOrders(v)) return;
 

	
 
	if (IsShipInDepot(v)) {
 
		VehicleServiceInDepot(v);
 
		return;
 
	}
 

	
 
	depot = FindClosestShipDepot(v);
 

	
 
	if (depot == NULL || DistanceManhattan(v->tile, depot->xy) > 12) {
 
		if (v->current_order.type == OT_GOTO_DEPOT) {
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return;
 
	}
 

	
 
	v->current_order.type = OT_GOTO_DEPOT;
 
	v->current_order.flags = OF_NON_STOP;
 
	v->current_order.dest = depot->index;
 
	v->dest_tile = depot->xy;
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
}
 

	
 
void OnNewDay_Ship(Vehicle *v)
 
{
 
	int32 cost;
 

	
 
	if ((++v->day_counter & 7) == 0)
 
		DecreaseVehicleValue(v);
 

	
 
	CheckVehicleBreakdown(v);
 
	AgeVehicle(v);
 
	CheckIfShipNeedsService(v);
 

	
 
	CheckOrders(v);
 

	
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	cost = ShipVehInfo(v->engine_type)->running_cost * _price.ship_running / 364;
 
	v->profit_this_year -= cost >> 8;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_SHIP_RUN);
 
	SubtractMoneyFromPlayerFract(v->owner, cost);
 

	
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	//we need this for the profit
 
	InvalidateWindowClasses(WC_SHIPS_LIST);
 
}
 

	
 
static void HandleBrokenShip(Vehicle *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->cur_speed = 0;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
 
			SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
 
				SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
 
		}
 

	
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
 
			if (u != NULL) u->u.special.unk0 = v->breakdown_delay * 2;
 
		}
 
	}
 

	
 
	if (!(v->tick_counter & 1)) {
 
		if (!--v->breakdown_delay) {
 
			v->breakdown_ctr = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		}
 
	}
 
}
 

	
 
static void MarkShipDirty(Vehicle *v)
 
{
 
	v->cur_image = GetShipImage(v, v->direction);
 
	MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
 
}
 

	
 
static void PlayShipSound(Vehicle *v)
 
{
 
	if (!PlayVehicleSound(v, VSE_START)) {
 
		SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
 
	}
 
}
 

	
 
static void ProcessShipOrder(Vehicle *v)
 
{
 
	const Order *order;
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_DEPOT:
 
			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return;
 
			if (v->current_order.flags & OF_SERVICE_IF_NEEDED &&
 
					!VehicleNeedsService(v)) {
 
				v->cur_order_index++;
 
			}
 
			break;
 

	
 
		case OT_LOADING:
 
		case OT_LEAVESTATION:
 
			return;
 

	
 
		default: break;
 
	}
 

	
 
	if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
 

	
 
	order = GetVehicleOrder(v, v->cur_order_index);
 

	
 
	if (order == NULL) {
 
		v->current_order.type  = OT_NOTHING;
 
		v->current_order.flags = 0;
 
		v->dest_tile = 0;
 
		return;
 
	}
 

	
 
	if (order->type  == v->current_order.type &&
 
			order->flags == v->current_order.flags &&
 
			order->dest  == v->current_order.dest)
 
		return;
 

	
 
	v->current_order = *order;
 

	
 
	if (order->type == OT_GOTO_STATION) {
 
		const Station *st;
 

	
 
		if (order->dest == v->last_station_visited)
 
			v->last_station_visited = INVALID_STATION;
 

	
 
		st = GetStation(order->dest);
 
		if (st->dock_tile != 0) {
 
			v->dest_tile = TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
 
		}
 
	} else if (order->type == OT_GOTO_DEPOT) {
 
		v->dest_tile = GetDepot(order->dest)->xy;
 
	} else {
 
		v->dest_tile = 0;
 
	}
 

	
 
	InvalidateVehicleOrder(v);
 

	
 
	InvalidateWindowClasses(WC_SHIPS_LIST);
 
}
 

	
 
static void HandleShipLoading(Vehicle *v)
 
{
 
	if (v->current_order.type == OT_NOTHING) return;
 

	
 
	if (v->current_order.type != OT_DUMMY) {
 
		if (v->current_order.type != OT_LOADING) return;
 
	switch (v->current_order.type) {
 
		case OT_LOADING:
 
		if (--v->load_unload_time_rem) return;
 

	
 
		if (CanFillVehicle(v) && (v->current_order.flags & OF_FULL_LOAD ||
 
				(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED)))) {
 
			if (CanFillVehicle(v) && (
 
						v->current_order.flags & OF_FULL_LOAD ||
 
						(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED))
 
					)) {
 
			SET_EXPENSES_TYPE(EXPENSES_SHIP_INC);
 
			if (LoadUnloadVehicle(v, false)) {
 
				InvalidateWindow(WC_SHIPS_LIST, v->owner);
 
				MarkShipDirty(v);
 
			}
 
			return;
 
		}
 
		PlayShipSound(v);
 

	
 
		{
 
			Order b = v->current_order;
 
			v->LeaveStation();
 
			if (!(b.flags & OF_NON_STOP)) return;
 
		}
 
			break;
 

	
 
		case OT_DUMMY: break;
 

	
 
		default: return;
 
	}
 

	
 
	v->cur_order_index++;
 
	InvalidateVehicleOrder(v);
 
}
 

	
 
static void UpdateShipDeltaXY(Vehicle *v, int dir)
 
{
 
#define MKIT(d,c,b,a) ((a&0xFF)<<24) | ((b&0xFF)<<16) | ((c&0xFF)<<8) | ((d&0xFF)<<0)
 
	static const uint32 _delta_xy_table[8] = {
 
		MKIT( -3,  -3,  6,  6),
 
		MKIT(-16,  -3, 32,  6),
 
		MKIT( -3,  -3,  6,  6),
 
		MKIT( -3, -16,  6, 32),
 
		MKIT( -3,  -3,  6,  6),
 
		MKIT(-16,  -3, 32,  6),
 
		MKIT( -3,  -3,  6,  6),
 
		MKIT( -3, -16,  6, 32),
 
	};
 
#undef MKIT
 
	uint32 x = _delta_xy_table[dir];
 
	v->x_offs        = GB(x,  0, 8);
 
	v->y_offs        = GB(x,  8, 8);
 
	v->sprite_width  = GB(x, 16, 8);
 
	v->sprite_height = GB(x, 24, 8);
 
}
 

	
 
void RecalcShipStuff(Vehicle *v)
 
{
 
	UpdateShipDeltaXY(v, v->direction);
 
	v->cur_image = GetShipImage(v, v->direction);
 
	MarkShipDirty(v);
 
	InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
}
 

	
 
static const TileIndexDiffC _ship_leave_depot_offs[] = {
 
	{-1,  0},
 
	{ 0, -1}
 
};
 

	
 
static void CheckShipLeaveDepot(Vehicle *v)
 
{
 
	TileIndex tile;
 
	Axis axis;
 
	uint m;
 

	
 
	if (!IsShipInDepot(v)) return;
 

	
 
	tile = v->tile;
 
	axis = GetShipDepotAxis(tile);
 

	
 
	// Check first side
 
	if (_ship_sometracks[axis] & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
 
		m = (axis == AXIS_X) ? 0x101 : 0x207;
 
	// Check second side
 
	} else if (_ship_sometracks[axis + 2] & GetTileShipTrackStatus(TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
 
		m = (axis == AXIS_X) ? 0x105 : 0x203;
 
	} else {
 
		return;
 
	}
 
	v->direction    = (Direction)GB(m, 0, 8);
 
	v->u.ship.state = (TrackBits)GB(m, 8, 8);
 
	v->vehstatus &= ~VS_HIDDEN;
 

	
 
	v->cur_speed = 0;
 
	RecalcShipStuff(v);
 

	
 
	PlayShipSound(v);
 
	VehicleServiceInDepot(v);
 
	InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	InvalidateWindowClasses(WC_SHIPS_LIST);
 
}
 

	
 
static bool ShipAccelerate(Vehicle *v)
 
{
 
	uint spd;
 
	byte t;
 

	
 
	spd = min(v->cur_speed + 1, v->max_speed);
 

	
 
	//updates statusbar only if speed have changed to save CPU time
 
	if (spd != v->cur_speed) {
 
		v->cur_speed = spd;
 
		if (_patches.vehicle_speed)
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 

	
 
	// Decrease somewhat when turning
 
	if (!(v->direction & 1)) spd = spd * 3 / 4;
 

	
 
	if (spd == 0) return false;
 
	if ((byte)++spd == 0) return true;
 

	
 
	v->progress = (t = v->progress) - (byte)spd;
 

	
 
	return (t < v->progress);
 
}
 

	
 
static int32 EstimateShipCost(EngineID engine_type)
 
{
 
	return ShipVehInfo(engine_type)->base_cost * (_price.ship_base>>3)>>5;
 
}
 

	
 
static void ShipArrivesAt(const Vehicle* v, Station* st)
 
{
 
	/* Check if station was ever visited before */
 
	if (!(st->had_vehicle_of_type & HVOT_SHIP)) {
 
		uint32 flags;
 

	
 
		st->had_vehicle_of_type |= HVOT_SHIP;
 

	
 
		SetDParam(0, st->index);
 
		flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0);
 
		AddNewsItem(
 
			STR_9833_CITIZENS_CELEBRATE_FIRST,
 
			flags,
 
			v->index,
 
			0);
 
	}
 
}
 

	
 
typedef struct {
 
	TileIndex skiptile;
 
	TileIndex dest_coords;
 
	uint best_bird_dist;
 
	uint best_length;
 
} PathFindShip;
 

	
 
static bool ShipTrackFollower(TileIndex tile, PathFindShip *pfs, int track, uint length, byte *state)
 
{
 
	// Found dest?
 
	if (tile == pfs->dest_coords) {
 
		pfs->best_bird_dist = 0;
 

	
 
		pfs->best_length = minu(pfs->best_length, length);
 
		return true;
 
	}
 

	
 
	// Skip this tile in the calculation
 
	if (tile != pfs->skiptile) {
 
		pfs->best_bird_dist = minu(pfs->best_bird_dist, DistanceMaxPlusManhattan(pfs->dest_coords, tile));
 
	}
 

	
 
	return false;
 
}
 

	
 
static const byte _ship_search_directions[6][4] = {
 
	{ 0, 9, 2, 9 },
 
	{ 9, 1, 9, 3 },
 
	{ 9, 0, 3, 9 },
 
	{ 1, 9, 9, 2 },
 
	{ 3, 2, 9, 9 },
 
	{ 9, 9, 1, 0 },
 
};
 

	
 
static const byte _pick_shiptrack_table[6] = {1, 3, 2, 2, 0, 0};
 

	
 
static uint FindShipTrack(Vehicle *v, TileIndex tile, DiagDirection dir, TrackBits bits, TileIndex skiptile, Track *track)
 
{
 
	PathFindShip pfs;
 
	Track i, best_track;
 
	uint best_bird_dist = 0;
 
	uint best_length    = 0;
 
	uint r;
 
	byte ship_dir = v->direction & 3;
 

	
 
	pfs.dest_coords = v->dest_tile;
 
	pfs.skiptile = skiptile;
 

	
 
	best_track = INVALID_TRACK;
 

	
 
	do {
 
		i = RemoveFirstTrack(&bits);
 

	
 
		pfs.best_bird_dist = (uint)-1;
 
		pfs.best_length = (uint)-1;
 

	
 
		FollowTrack(tile, 0x3800 | TRANSPORT_WATER, (DiagDirection)_ship_search_directions[i][dir], (TPFEnumProc*)ShipTrackFollower, NULL, &pfs);
 

	
 
		if (best_track != INVALID_TRACK) {
 
			if (pfs.best_bird_dist != 0) {
 
				/* neither reached the destination, pick the one with the smallest bird dist */
 
				if (pfs.best_bird_dist > best_bird_dist) goto bad;
 
				if (pfs.best_bird_dist < best_bird_dist) goto good;
 
			} else {
 
				if (pfs.best_length > best_length) goto bad;
 
				if (pfs.best_length < best_length) goto good;
 
			}
 

	
 
			/* if we reach this position, there's two paths of equal value so far.
 
			 * pick one randomly. */
 
			r = GB(Random(), 0, 8);
 
			if (_pick_shiptrack_table[i] == ship_dir) r += 80;
 
			if (_pick_shiptrack_table[best_track] == ship_dir) r -= 80;
 
			if (r <= 127) goto bad;
 
		}
 
good:;
 
		best_track = i;
 
		best_bird_dist = pfs.best_bird_dist;
 
		best_length = pfs.best_length;
 
bad:;
 

	
 
	} while (bits != 0);
 

	
 
	*track = best_track;
 
	return best_bird_dist;
 
}
 

	
 
static inline NPFFoundTargetData PerfNPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes)
 
{
 

	
 
	void* perf = NpfBeginInterval();
 
	NPFFoundTargetData ret = NPFRouteToStationOrTile(tile, trackdir, target, type, owner, railtypes);
 
	int t = NpfEndInterval(perf);
 
	DEBUG(yapf, 4, "[NPFW] %d us - %d rounds - %d open - %d closed -- ", t, 0, _aystar_stats_open_size, _aystar_stats_closed_size);
 
	return ret;
 
}
 

	
 
/* returns the track to choose on the next tile, or -1 when it's better to
 
 * reverse. The tile given is the tile we are about to enter, enterdir is the
 
 * direction in which we are entering the tile */
 
static Track ChooseShipTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
 
{
 
	assert(enterdir>=0 && enterdir<=3);
 

	
 
	if (_patches.yapf.ship_use_yapf) {
 
		Trackdir trackdir = YapfChooseShipTrack(v, tile, enterdir, tracks);
 
		return (trackdir != INVALID_TRACKDIR) ? TrackdirToTrack(trackdir) : INVALID_TRACK;
 
	} else if (_patches.new_pathfinding_all) {
 
		NPFFindStationOrTileData fstd;
 
		NPFFoundTargetData ftd;
 
		TileIndex src_tile = TILE_ADD(tile, TileOffsByDiagDir(ReverseDiagDir(enterdir)));
 
		Trackdir trackdir = GetVehicleTrackdir(v);
 
		assert(trackdir != INVALID_TRACKDIR); /* Check that we are not in a depot */
 

	
 
		NPFFillWithOrderData(&fstd, v);
 

	
 
		ftd = PerfNPFRouteToStationOrTile(src_tile, trackdir, &fstd, TRANSPORT_WATER, v->owner, INVALID_RAILTYPE);
 

	
 
		if (ftd.best_trackdir != 0xff) {
 
			/* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
 
			the direction we need to take to get there, if ftd.best_bird_dist is not 0,
 
			we did not find our target, but ftd.best_trackdir contains the direction leading
 
			to the tile closest to our target. */
 
			return TrackdirToTrack(ftd.best_trackdir); /* TODO: Wrapper function? */
 
		} else {
 
			return INVALID_TRACK; /* Already at target, reverse? */
 
		}
 
	} else {
 
		uint tot_dist, dist;
 
		Track track;
 
		TileIndex tile2;
 

	
 
		tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir));
 
		tot_dist = (uint)-1;
 

	
 
		/* Let's find out how far it would be if we would reverse first */
 
		TrackBits b = GetTileShipTrackStatus(tile2) & _ship_sometracks[ReverseDiagDir(enterdir)] & v->u.ship.state;
 
		if (b != 0) {
 
			dist = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track);
 
			if (dist != (uint)-1)
 
				tot_dist = dist + 1;
 
		}
 
		/* And if we would not reverse? */
 
		dist = FindShipTrack(v, tile, enterdir, tracks, 0, &track);
 
		if (dist > tot_dist)
 
			/* We could better reverse */
 
			return INVALID_TRACK;
 
		return track;
 
	}
 
}
 

	
 
static const Direction _new_vehicle_direction_table[] = {
 
	DIR_N , DIR_NW, DIR_W , INVALID_DIR,
 
	DIR_NE, DIR_N , DIR_SW, INVALID_DIR,
 
	DIR_E , DIR_SE, DIR_S
 
};
 

	
 
static Direction ShipGetNewDirectionFromTiles(TileIndex new_tile, TileIndex old_tile)
 
{
 
	uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 +
 
							TileX(new_tile) - TileX(old_tile) + 1;
 
	assert(offs < 11 && offs != 3 && offs != 7);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static Direction ShipGetNewDirection(Vehicle *v, int x, int y)
 
{
 
	uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1);
 
	assert(offs < 11 && offs != 3 && offs != 7);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static TrackBits GetAvailShipTracks(TileIndex tile, int dir)
 
{
 
	uint32 r = GetTileTrackStatus(tile, TRANSPORT_WATER);
 
	return (TrackBits)((r | r >> 8) & _ship_sometracks[dir]);
 
}
 

	
 
static const byte _ship_subcoord[4][6][3] = {
 
	{
 
		{15, 8, 1},
 
		{ 0, 0, 0},
 
		{ 0, 0, 0},
 
		{15, 8, 2},
 
		{15, 7, 0},
 
		{ 0, 0, 0},
 
	},
 
	{
 
		{ 0, 0, 0},
 
		{ 8, 0, 3},
 
		{ 7, 0, 2},
 
		{ 0, 0, 0},
 
		{ 8, 0, 4},
 
		{ 0, 0, 0},
 
	},
 
	{
 
		{ 0, 8, 5},
 
		{ 0, 0, 0},
 
		{ 0, 7, 6},
 
		{ 0, 0, 0},
 
		{ 0, 0, 0},
 
		{ 0, 8, 4},
 
	},
 
	{
 
		{ 0, 0, 0},
 
		{ 8,15, 7},
 
		{ 0, 0, 0},
 
		{ 8,15, 6},
 
		{ 0, 0, 0},
 
		{ 7,15, 0},
 
	}
 
};
 

	
 
static void ShipController(Vehicle *v)
 
{
 
	GetNewVehiclePosResult gp;
 
	uint32 r;
 
	const byte *b;
 
	Direction dir;
 
	Track track;
 
	TrackBits tracks;
 

	
 
	v->tick_counter++;
 

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

	
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	ProcessShipOrder(v);
 
	HandleShipLoading(v);
 

	
 
	if (v->current_order.type == OT_LOADING) return;
 

	
 
	CheckShipLeaveDepot(v);
 

	
 
	if (!ShipAccelerate(v)) return;
 

	
 
	BeginVehicleMove(v);
 

	
 
	if (GetNewVehiclePos(v, &gp)) {
 
		// staying in tile
 
		if (IsShipInDepot(v)) {
 
			gp.x = v->x_pos;
 
			gp.y = v->y_pos;
 
		} else {
 
			/* isnot inside depot */
 
			r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
 
			if (r & 0x8) goto reverse_direction;
 

	
 
			/* A leave station order only needs one tick to get processed, so we can
 
			 * always skip ahead. */
 
			if (v->current_order.type == OT_LEAVESTATION) {
 
				v->current_order.type = OT_NOTHING;
 
				v->current_order.flags = 0;
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			} else if (v->dest_tile != 0) {
 
				/* We have a target, let's see if we reached it... */
 
				if (v->current_order.type == OT_GOTO_STATION &&
 
						IsBuoyTile(v->dest_tile) &&
 
						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 */
 
					v->cur_order_index++;
 
					v->current_order.type = OT_DUMMY;
 
					InvalidateVehicleOrder(v);
 
				} else {
 
					/* Non-buoy orders really need to reach the tile */
 
					if (v->dest_tile == gp.new_tile) {
 
						if (v->current_order.type == OT_GOTO_DEPOT) {
 
							if ((gp.x&0xF)==8 && (gp.y&0xF)==8) {
 
								VehicleEnterDepot(v);
 
								return;
 
							}
 
						} else if (v->current_order.type == OT_GOTO_STATION) {
 
							Station *st;
 

	
 
							v->last_station_visited = v->current_order.dest;
 

	
 
							/* Process station in the orderlist. */
 
							st = GetStation(v->current_order.dest);
 
							if (st->facilities & FACIL_DOCK) { /* ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations */
 
								v->BeginLoading();
 
								v->current_order.flags &= OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER;
 
								v->current_order.flags |= OF_NON_STOP;
 
								ShipArrivesAt(v, st);
 

	
 
								SET_EXPENSES_TYPE(EXPENSES_SHIP_INC);
 
								if (LoadUnloadVehicle(v, true)) {
 
									InvalidateWindow(WC_SHIPS_LIST, v->owner);
 
									MarkShipDirty(v);
 
								}
 
								InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
							} else { /* leave stations without docks right aways */
 
								v->current_order.type = OT_LEAVESTATION;
 
								v->cur_order_index++;
 
								InvalidateVehicleOrder(v);
 
							}
 
						}
 
					}
 
				}
 
			}
 
		}
 
	} 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 == 0)
 
			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 (r&0x8) goto reverse_direction;
 

	
 
		if (!(r&0x4)) {
 
			v->tile = gp.new_tile;
 
			v->u.ship.state = TrackToTrackBits(track);
 
		}
 

	
 
		v->direction = (Direction)b[2];
 
	}
 

	
 
	/* 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:
 
	UpdateShipDeltaXY(v, dir);
 
	v->cur_image = GetShipImage(v, dir);
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
	return;
 

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

	
 
static void AgeShipCargo(Vehicle *v)
 
{
 
	if (_age_cargo_skip_counter != 0) return;
 
	if (v->cargo_days != 255) v->cargo_days++;
 
}
 

	
 
void Ship_Tick(Vehicle *v)
 
{
 
	AgeShipCargo(v);
 
	ShipController(v);
 
}
 

	
 

	
 
void ShipsYearlyLoop(void)
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Ship) {
 
			v->profit_last_year = v->profit_this_year;
 
			v->profit_this_year = 0;
 
			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		}
 
	}
 
}
 

	
 
/** Build a ship.
 
 * @param tile tile of depot where ship is built
 
 * @param p1 ship type being built (engine)
 
 * @param p2 bit 0 when set, the unitnumber will be 0, otherwise it will be a free number
 
 */
 
int32 CmdBuildShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 value;
 
	Vehicle *v;
 
	UnitID unit_num;
 
	Engine *e;
 

	
 
	if (!IsEngineBuildable(p1, VEH_Ship, _current_player)) return_cmd_error(STR_ENGINE_NOT_BUILDABLE);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	value = EstimateShipCost(p1);
 
	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 (!IsTileDepotType(tile, TRANSPORT_WATER)) return CMD_ERROR;
 
	if (!IsTileOwner(tile, _current_player)) return CMD_ERROR;
 

	
 
	v = AllocateVehicle();
 
	unit_num = HASBIT(p2, 0) ? 0 : GetFreeUnitNumber(VEH_Ship);
 

	
 
	if (v == NULL || IsOrderPoolFull() || unit_num > _patches.max_ships)
 
		return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

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

	
 
		const ShipVehicleInfo *svi = ShipVehInfo(p1);
 

	
 
		v->unitnumber = unit_num;
 

	
 
		v->owner = _current_player;
 
		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->z_height = 6;
 
		v->sprite_width = 6;
 
		v->sprite_height = 6;
 
		v->x_offs = -3;
 
		v->y_offs = -3;
 
		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
 

	
 
		v->spritenum = svi->image_index;
 
		v->cargo_type = svi->cargo_type;
 
		v->cargo_subtype = 0;
 
		v->cargo_cap = svi->capacity;
 
		v->value = value;
 

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

	
 
		e = GetEngine(p1);
 
		v->reliability = e->reliability;
 
		v->reliability_spd_dec = e->reliability_spd_dec;
 
		v->max_age = e->lifelength * 366;
 
		_new_vehicle_id = v->index;
 

	
 
		v->string_id = STR_SV_SHIP_NAME;
 
		v->u.ship.state = TRACK_BIT_SPECIAL;
 

	
 
		v->service_interval = _patches.servint_ships;
 
		v->date_of_last_service = _date;
 
		v->build_year = _cur_year;
 
		v->cur_image = 0x0E5E;
 
		v->type = VEH_Ship;
 
		v->random_bits = VehicleRandomBits();
 

	
 
		VehiclePositionChanged(v);
 
		GetPlayer(_current_player)->num_engines[p1]++;
 

	
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
		RebuildVehicleLists();
 
		InvalidateWindow(WC_COMPANY, v->owner);
 
		if (IsLocalPlayer())
 
			InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Ship); // updates the replace Ship window
 
	}
 

	
 
	return value;
 
}
 

	
 
/** Sell a ship.
 
 * @param tile unused
 
 * @param p1 vehicle ID to be sold
 
 * @param p2 unused
 
 */
 
int32 CmdSellShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Ship || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	if (!IsShipInDepotStopped(v)) {
 
		return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		RebuildVehicleLists();
 
		InvalidateWindow(WC_COMPANY, v->owner);
 
		DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
		DeleteDepotHighlightOfVehicle(v);
 
		DeleteVehicle(v);
 
		if (IsLocalPlayer())
 
			InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Ship); // updates the replace Ship window
 
	}
 

	
 
	return -(int32)v->value;
 
}
 

	
 
/** Start/Stop a ship.
 
 * @param tile unused
 
 * @param p1 ship ID to start/stop
 
 * @param p2 unused
 
 */
 
int32 CmdStartStopShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	uint16 callback;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Ship || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	/* Check if this ship can be started/stopped. The callback will fail or
 
	 * return 0xFF if it can. */
 
	callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
 
	if (callback != CALLBACK_FAILED && callback != 0xFF) {
 
		StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
 
		return_cmd_error(error);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		if (IsShipInDepotStopped(v)) {
 
			DeleteVehicleNews(p1, STR_981C_SHIP_IS_WAITING_IN_DEPOT);
 
		}
 

	
 
		v->vehstatus ^= VS_STOPPED;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		InvalidateWindowClasses(WC_SHIPS_LIST);
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Send a ship to the depot.
 
 * @param tile unused
 
 * @param p1 vehicle ID to send to the depot
 
 * @param p2 various bitmasked elements
 
 * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h)
 
 * - p2 bit 8-10 - VLW flag (for mass goto depot)
 
 */
 
int32 CmdSendShipToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	const Depot *dep;
 

	
 
	if (p2 & DEPOT_MASS_SEND) {
 
		/* Mass goto depot requested */
 
		if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
 
		return SendAllVehiclesToDepot(VEH_Ship, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1);
 
	}
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Ship || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
 

	
 
	if (IsShipInDepot(v)) return CMD_ERROR;
 

	
 
	/* If the current orders are already goto-depot */
 
	if (v->current_order.type == OT_GOTO_DEPOT) {
 
		if (!!(p2 & DEPOT_SERVICE) == HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT)) {
 
			/* We called with a different DEPOT_SERVICE setting.
 
			 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
 
			 * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
 
			if (flags & DC_EXEC) {
 
				TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			}
 
			return 0;
 
		}
 

	
 
		if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
 
		if (flags & DC_EXEC) {
 
			/* If the orders to 'goto depot' are in the orders list (forced servicing),
 
			 * then skip to the next order; effectively cancelling this forced service */
 
			if (HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS))
 
				v->cur_order_index++;
 

	
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return 0;
 
	}
 

	
 
	dep = FindClosestShipDepot(v);
 
	if (dep == NULL) return_cmd_error(STR_981A_UNABLE_TO_FIND_LOCAL_DEPOT);
 

	
 
	if (flags & DC_EXEC) {
 
		v->dest_tile = dep->xy;
 
		v->current_order.type = OT_GOTO_DEPOT;
 
		v->current_order.flags = OF_NON_STOP;
 
		if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
		v->current_order.refit_cargo = CT_INVALID;
 
		v->current_order.dest = dep->index;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
/** Refits a ship to the specified cargo type.
 
 * @param tile unused
 
 * @param p1 vehicle ID of the ship to refit
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit 0-7) - the new cargo type to refit to (p2 & 0xFF)
 
 * - p2 = (bit 8-15) - the new cargo subtype to refit to
 
 */
 
int32 CmdRefitShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	int32 cost;
 
	CargoID new_cid = GB(p2, 0, 8); //gets the cargo number
 
	byte new_subtype = GB(p2, 8, 8);
 
	uint16 capacity = CALLBACK_FAILED;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Ship || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (!IsShipInDepotStopped(v)) {
src/train_cmd.cpp
Show inline comments
 
@@ -1840,1572 +1840,1576 @@ int32 CmdRefitRailVehicle(TileIndex tile
 

	
 
			if (HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_REFIT_CAPACITY)) {
 
				/* Back up the vehicle's cargo type */
 
				CargoID temp_cid = v->cargo_type;
 
				byte temp_subtype = v->cargo_subtype;
 
				v->cargo_type = new_cid;
 
				v->cargo_subtype = new_subtype;
 
				/* Check the refit capacity callback */
 
				amount = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
 
				/* Restore the original cargo type */
 
				v->cargo_type = temp_cid;
 
				v->cargo_subtype = temp_subtype;
 
			}
 

	
 
			if (amount == CALLBACK_FAILED) { // callback failed or not used, use default
 
				CargoID old_cid = rvi->cargo_type;
 
				/* normally, the capacity depends on the cargo type, a rail vehicle can
 
				 * carry twice as much mail/goods as normal cargo, and four times as
 
				 * many passengers
 
				 */
 
				amount = rvi->capacity;
 
				switch (old_cid) {
 
					case CT_PASSENGERS: break;
 
					case CT_MAIL:
 
					case CT_GOODS: amount *= 2; break;
 
					default:       amount *= 4; break;
 
				}
 
				switch (new_cid) {
 
					case CT_PASSENGERS: break;
 
					case CT_MAIL:
 
					case CT_GOODS: amount /= 2; break;
 
					default:       amount /= 4; break;
 
				}
 
			};
 

	
 
			if (amount != 0) {
 
				if (new_cid != v->cargo_type) {
 
					cost += GetRefitCost(v->engine_type);
 
				}
 

	
 
				num += amount;
 
				if (flags & DC_EXEC) {
 
					v->cargo_count = (v->cargo_type == new_cid) ? min(amount, v->cargo_count) : 0;
 
					v->cargo_type = new_cid;
 
					v->cargo_cap = amount;
 
					v->cargo_subtype = new_subtype;
 
					InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
					InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
					RebuildVehicleLists();
 
				}
 
			}
 
		}
 
	} while ((v = v->next) != NULL);
 

	
 
	_returned_refit_capacity = num;
 

	
 
	/* Update the train's cached variables */
 
	if (flags & DC_EXEC) TrainConsistChanged(GetFirstVehicleInChain(GetVehicle(p1)));
 

	
 
	return cost;
 
}
 

	
 
typedef struct TrainFindDepotData {
 
	uint best_length;
 
	TileIndex tile;
 
	PlayerID owner;
 
	/**
 
	 * true if reversing is necessary for the train to get to this depot
 
	 * This value is unused when new depot finding and NPF are both disabled
 
	 */
 
	bool reverse;
 
} TrainFindDepotData;
 

	
 
static bool NtpCallbFindDepot(TileIndex tile, TrainFindDepotData *tfdd, int track, uint length)
 
{
 
	if (IsTileType(tile, MP_RAILWAY) &&
 
			IsTileOwner(tile, tfdd->owner) &&
 
			IsRailDepot(tile)) {
 
		/* approximate number of tiles by dividing by DIAG_FACTOR */
 
		tfdd->best_length = length / DIAG_FACTOR;
 
		tfdd->tile = tile;
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
// returns the tile of a depot to goto to. The given vehicle must not be
 
// crashed!
 
static TrainFindDepotData FindClosestTrainDepot(Vehicle *v, int max_distance)
 
{
 
	TrainFindDepotData tfdd;
 
	TileIndex tile = v->tile;
 

	
 
	assert(!(v->vehstatus & VS_CRASHED));
 

	
 
	tfdd.owner = v->owner;
 
	tfdd.best_length = (uint)-1;
 
	tfdd.reverse = false;
 

	
 
	if (IsTileDepotType(tile, TRANSPORT_RAIL)){
 
		tfdd.tile = tile;
 
		tfdd.best_length = 0;
 
		return tfdd;
 
	}
 

	
 
	if (_patches.yapf.rail_use_yapf) {
 
		bool found = YapfFindNearestRailDepotTwoWay(v, max_distance, NPF_INFINITE_PENALTY, &tfdd.tile, &tfdd.reverse);
 
		tfdd.best_length = found ? max_distance / 2 : -1; // some fake distance or NOT_FOUND
 
	} else if (_patches.new_pathfinding_all) {
 
		NPFFoundTargetData ftd;
 
		Vehicle* last = GetLastVehicleInChain(v);
 
		Trackdir trackdir = GetVehicleTrackdir(v);
 
		Trackdir trackdir_rev = ReverseTrackdir(GetVehicleTrackdir(last));
 

	
 
		assert (trackdir != INVALID_TRACKDIR);
 
		ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, last->tile, trackdir_rev, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes, NPF_INFINITE_PENALTY);
 
		if (ftd.best_bird_dist == 0) {
 
			/* Found target */
 
			tfdd.tile = ftd.node.tile;
 
			/* Our caller expects a number of tiles, so we just approximate that
 
			 * number by this. It might not be completely what we want, but it will
 
			 * work for now :-) We can possibly change this when the old pathfinder
 
			 * is removed. */
 
			tfdd.best_length = ftd.best_path_dist / NPF_TILE_LENGTH;
 
			if (NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE)) tfdd.reverse = true;
 
		}
 
	} else {
 
		// search in the forward direction first.
 
		DiagDirection i;
 

	
 
		i = DirToDiagDir(v->direction);
 
		if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) {
 
			i = ChangeDiagDir(i, DIAGDIRDIFF_90LEFT);
 
		}
 
		NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd);
 
		if (tfdd.best_length == (uint)-1){
 
			tfdd.reverse = true;
 
			// search in backwards direction
 
			i = ReverseDiagDir(DirToDiagDir(v->direction));
 
			if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) {
 
				i = ChangeDiagDir(i, DIAGDIRDIFF_90LEFT);
 
			}
 
			NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd);
 
		}
 
	}
 

	
 
	return tfdd;
 
}
 

	
 
/** Send a train to a depot
 
 * @param tile unused
 
 * @param p1 train to send to the depot
 
 * @param p2 various bitmasked elements
 
 * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h)
 
 * - p2 bit 8-10 - VLW flag (for mass goto depot)
 
 */
 
int32 CmdSendTrainToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	TrainFindDepotData tfdd;
 

	
 
	if (p2 & DEPOT_MASS_SEND) {
 
		/* Mass goto depot requested */
 
		if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
 
		return SendAllVehiclesToDepot(VEH_Train, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1);
 
	}
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT) {
 
		if (!!(p2 & DEPOT_SERVICE) == HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT)) {
 
			/* We called with a different DEPOT_SERVICE setting.
 
			 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
 
			 * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
 
			if (flags & DC_EXEC) {
 
				TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			}
 
			return 0;
 
		}
 

	
 
		if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
 
		if (flags & DC_EXEC) {
 
			if (HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) {
 
				v->u.rail.days_since_order_progr = 0;
 
				v->cur_order_index++;
 
			}
 

	
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return 0;
 
	}
 

	
 
	/* check if at a standstill (not stopped only) in a depot
 
	 * the check is down here to make it possible to alter stop/service for trains entering the depot */
 
	if (IsTileDepotType(v->tile, TRANSPORT_RAIL) && v->cur_speed == 0) return CMD_ERROR;
 

	
 
	tfdd = FindClosestTrainDepot(v, 0);
 
	if (tfdd.best_length == (uint)-1) return_cmd_error(STR_883A_UNABLE_TO_FIND_ROUTE_TO);
 

	
 
	if (flags & DC_EXEC) {
 
		v->dest_tile = tfdd.tile;
 
		v->current_order.type = OT_GOTO_DEPOT;
 
		v->current_order.flags = OF_NON_STOP;
 
		if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
		v->current_order.dest = GetDepotByTile(tfdd.tile)->index;
 
		v->current_order.refit_cargo = CT_INVALID;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		/* If there is no depot in front, reverse automatically */
 
		if (tfdd.reverse)
 
			DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
void OnTick_Train(void)
 
{
 
	_age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
 
}
 

	
 
static const int8 _vehicle_smoke_pos[8] = {
 
	1, 1, 1, 0, -1, -1, -1, 0
 
};
 

	
 
static void HandleLocomotiveSmokeCloud(const Vehicle* v)
 
{
 
	const Vehicle* u;
 
	bool sound = false;
 

	
 
	if (v->vehstatus & VS_TRAIN_SLOWING || v->load_unload_time_rem != 0 || v->cur_speed < 2)
 
		return;
 

	
 
	u = v;
 

	
 
	do {
 
		const RailVehicleInfo *rvi = RailVehInfo(v->engine_type);
 
		int effect_offset = GB(v->u.rail.cached_vis_effect, 0, 4) - 8;
 
		byte effect_type = GB(v->u.rail.cached_vis_effect, 4, 2);
 
		bool disable_effect = HASBIT(v->u.rail.cached_vis_effect, 6);
 
		int x, y;
 

	
 
		// no smoke?
 
		if ((rvi->flags & RVI_WAGON && effect_type == 0) ||
 
				disable_effect ||
 
				rvi->railtype > RAILTYPE_ELECTRIC ||
 
				v->vehstatus & VS_HIDDEN) {
 
			continue;
 
		}
 

	
 
		// No smoke in depots or tunnels
 
		if (IsTileDepotType(v->tile, TRANSPORT_RAIL) || IsTunnelTile(v->tile)) continue;
 

	
 
		// No sparks for electric vehicles on nonelectrified tracks
 
		if (!HasPowerOnRail(v->u.rail.railtype, GetTileRailType(v->tile, TrackdirToTrack(GetVehicleTrackdir(v))))) continue;
 

	
 
		if (effect_type == 0) {
 
			// Use default effect type for engine class.
 
			effect_type = rvi->engclass;
 
		} else {
 
			effect_type--;
 
		}
 

	
 
		x = _vehicle_smoke_pos[v->direction] * effect_offset;
 
		y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
 

	
 
		if (HASBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION)) {
 
			x = -x;
 
			y = -y;
 
		}
 

	
 
		switch (effect_type) {
 
		case 0:
 
			// steam smoke.
 
			if (GB(v->tick_counter, 0, 4) == 0) {
 
				CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
 
				sound = true;
 
			}
 
			break;
 

	
 
		case 1:
 
			// diesel smoke
 
			if (u->cur_speed <= 40 && CHANCE16(15, 128)) {
 
				CreateEffectVehicleRel(v, 0, 0, 10, EV_DIESEL_SMOKE);
 
				sound = true;
 
			}
 
			break;
 

	
 
		case 2:
 
			// blue spark
 
			if (GB(v->tick_counter, 0, 2) == 0 && CHANCE16(1, 45)) {
 
				CreateEffectVehicleRel(v, 0, 0, 10, EV_ELECTRIC_SPARK);
 
				sound = true;
 
			}
 
			break;
 
		}
 
	} while ((v = v->next) != NULL);
 

	
 
	if (sound) PlayVehicleSound(u, VSE_TRAIN_EFFECT);
 
}
 

	
 
static void TrainPlayLeaveStationSound(const Vehicle* v)
 
{
 
	static const SoundFx sfx[] = {
 
		SND_04_TRAIN,
 
		SND_0A_TRAIN_HORN,
 
		SND_0A_TRAIN_HORN
 
	};
 

	
 
	EngineID engtype = v->engine_type;
 

	
 
	if (PlayVehicleSound(v, VSE_START)) return;
 

	
 
	switch (RailVehInfo(engtype)->railtype) {
 
		case RAILTYPE_RAIL:
 
		case RAILTYPE_ELECTRIC:
 
			SndPlayVehicleFx(sfx[RailVehInfo(engtype)->engclass], v);
 
			break;
 

	
 
		case RAILTYPE_MONO: SndPlayVehicleFx(SND_47_MAGLEV_2, v); break;
 
		case RAILTYPE_MAGLEV: SndPlayVehicleFx(SND_41_MAGLEV, v); break;
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
static bool CheckTrainStayInDepot(Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	// bail out if not all wagons are in the same depot or not in a depot at all
 
	for (u = v; u != NULL; u = u->next) {
 
		if (u->u.rail.track != 0x80 || u->tile != v->tile) return false;
 
	}
 

	
 
	// if the train got no power, then keep it in the depot
 
	if (v->u.rail.cached_power == 0) {
 
		v->vehstatus |= VS_STOPPED;
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		return true;
 
	}
 

	
 
	if (v->u.rail.force_proceed == 0) {
 
		if (++v->load_unload_time_rem < 37) {
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
			return true;
 
		}
 

	
 
		v->load_unload_time_rem = 0;
 

	
 
		if (UpdateSignalsOnSegment(v->tile, DirToDiagDir(v->direction))) {
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
			return true;
 
		}
 
	}
 

	
 
	VehicleServiceInDepot(v);
 
	InvalidateWindowClasses(WC_TRAINS_LIST);
 
	TrainPlayLeaveStationSound(v);
 

	
 
	v->u.rail.track = TRACK_BIT_X;
 
	if (v->direction & 2) v->u.rail.track = TRACK_BIT_Y;
 

	
 
	v->vehstatus &= ~VS_HIDDEN;
 
	v->cur_speed = 0;
 

	
 
	UpdateTrainDeltaXY(v, v->direction);
 
	v->cur_image = GetTrainImage(v, v->direction);
 
	VehiclePositionChanged(v);
 
	UpdateSignalsOnSegment(v->tile, DirToDiagDir(v->direction));
 
	UpdateTrainAcceleration(v);
 
	InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 

	
 
	return false;
 
}
 

	
 
/* Check for station tiles */
 
typedef struct TrainTrackFollowerData {
 
	TileIndex dest_coords;
 
	StationID station_index; // station index we're heading for
 
	uint best_bird_dist;
 
	uint best_track_dist;
 
	TrackdirByte best_track;
 
} TrainTrackFollowerData;
 

	
 
static bool NtpCallbFindStation(TileIndex tile, TrainTrackFollowerData *ttfd, Trackdir track, uint length)
 
{
 
	// heading for nowhere?
 
	if (ttfd->dest_coords == 0) return false;
 

	
 
	// did we reach the final station?
 
	if ((ttfd->station_index == INVALID_STATION && tile == ttfd->dest_coords) || (
 
				IsTileType(tile, MP_STATION) &&
 
				IsRailwayStation(tile) &&
 
				GetStationIndex(tile) == ttfd->station_index
 
			)) {
 
		/* We do not check for dest_coords if we have a station_index,
 
		 * because in that case the dest_coords are just an
 
		 * approximation of where the station is */
 
		// found station
 
		ttfd->best_track = track;
 
		return true;
 
	} else {
 
		uint dist;
 

	
 
		// didn't find station, keep track of the best path so far.
 
		dist = DistanceManhattan(tile, ttfd->dest_coords);
 
		if (dist < ttfd->best_bird_dist) {
 
			ttfd->best_bird_dist = dist;
 
			ttfd->best_track = track;
 
		}
 
		return false;
 
	}
 
}
 

	
 
static void FillWithStationData(TrainTrackFollowerData* fd, const Vehicle* v)
 
{
 
	fd->dest_coords = v->dest_tile;
 
	if (v->current_order.type == OT_GOTO_STATION) {
 
		fd->station_index = v->current_order.dest;
 
	} else {
 
		fd->station_index = INVALID_STATION;
 
	}
 
}
 

	
 
static const byte _initial_tile_subcoord[6][4][3] = {
 
{{ 15, 8, 1 }, { 0, 0, 0 }, { 0, 8, 5 }, { 0,  0, 0 }},
 
{{  0, 0, 0 }, { 8, 0, 3 }, { 0, 0, 0 }, { 8, 15, 7 }},
 
{{  0, 0, 0 }, { 7, 0, 2 }, { 0, 7, 6 }, { 0,  0, 0 }},
 
{{ 15, 8, 2 }, { 0, 0, 0 }, { 0, 0, 0 }, { 8, 15, 6 }},
 
{{ 15, 7, 0 }, { 8, 0, 4 }, { 0, 0, 0 }, { 0,  0, 0 }},
 
{{  0, 0, 0 }, { 0, 0, 0 }, { 0, 8, 4 }, { 7, 15, 0 }},
 
};
 

	
 
static const uint32 _reachable_tracks[4] = {
 
	0x10091009,
 
	0x00160016,
 
	0x05200520,
 
	0x2A002A00,
 
};
 

	
 
static const byte _search_directions[6][4] = {
 
	{ 0, 9, 2, 9 }, // track 1
 
	{ 9, 1, 9, 3 }, // track 2
 
	{ 9, 0, 3, 9 }, // track upper
 
	{ 1, 9, 9, 2 }, // track lower
 
	{ 3, 2, 9, 9 }, // track left
 
	{ 9, 9, 1, 0 }, // track right
 
};
 

	
 
static const byte _pick_track_table[6] = {1, 3, 2, 2, 0, 0};
 

	
 
/* choose a track */
 
static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
 
{
 
	TrainTrackFollowerData fd;
 
	Track best_track;
 
	// pathfinders are able to tell that route was only 'guessed'
 
	bool path_not_found = false;
 

	
 
#ifdef PF_BENCHMARK
 
	TIC()
 
#endif
 

	
 
	assert((tracks & ~0x3F) == 0);
 

	
 
	/* quick return in case only one possible track is available */
 
	if (KILL_FIRST_BIT(tracks) == 0) return FindFirstTrack(tracks);
 

	
 
	if (_patches.yapf.rail_use_yapf) {
 
		Trackdir trackdir = YapfChooseRailTrack(v, tile, enterdir, tracks, &path_not_found);
 
		if (trackdir != INVALID_TRACKDIR) {
 
			best_track = TrackdirToTrack(trackdir);
 
		} else {
 
			best_track = FindFirstTrack(tracks);
 
		}
 
	} else if (_patches.new_pathfinding_all) { /* Use a new pathfinding for everything */
 
		void* perf = NpfBeginInterval();
 
		int time = 0;
 

	
 
		NPFFindStationOrTileData fstd;
 
		NPFFoundTargetData ftd;
 
		Trackdir trackdir;
 

	
 
		NPFFillWithOrderData(&fstd, v);
 
		/* The enterdir for the new tile, is the exitdir for the old tile */
 
		trackdir = GetVehicleTrackdir(v);
 
		assert(trackdir != 0xff);
 

	
 
		ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes);
 

	
 
		if (ftd.best_trackdir == 0xff) {
 
			/* We are already at our target. Just do something */
 
			//TODO: maybe display error?
 
			//TODO: go straight ahead if possible?
 
			best_track = FindFirstTrack(tracks);
 
		} else {
 
			/* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
 
			the direction we need to take to get there, if ftd.best_bird_dist is not 0,
 
			we did not find our target, but ftd.best_trackdir contains the direction leading
 
			to the tile closest to our target. */
 
			if (ftd.best_bird_dist != 0) path_not_found = true;
 
			/* Discard enterdir information, making it a normal track */
 
			best_track = TrackdirToTrack(ftd.best_trackdir);
 
		}
 

	
 
		time = NpfEndInterval(perf);
 
		DEBUG(yapf, 4, "[NPFT] %d us - %d rounds - %d open - %d closed -- ", time, 0, _aystar_stats_open_size, _aystar_stats_closed_size);
 
	} else {
 
		void* perf = NpfBeginInterval();
 
		int time = 0;
 

	
 
		FillWithStationData(&fd, v);
 

	
 
		/* New train pathfinding */
 
		fd.best_bird_dist = (uint)-1;
 
		fd.best_track_dist = (uint)-1;
 
		fd.best_track = INVALID_TRACKDIR;
 

	
 
		NewTrainPathfind(tile - TileOffsByDiagDir(enterdir), v->dest_tile,
 
			v->u.rail.compatible_railtypes, enterdir, (NTPEnumProc*)NtpCallbFindStation, &fd);
 

	
 
		// check whether the path was found or only 'guessed'
 
		if (fd.best_bird_dist != 0) path_not_found = true;
 

	
 
		if (fd.best_track == 0xff) {
 
			// blaha
 
			best_track = FindFirstTrack(tracks);
 
		} else {
 
			best_track = TrackdirToTrack(fd.best_track);
 
		}
 

	
 
		time = NpfEndInterval(perf);
 
		DEBUG(yapf, 4, "[NTPT] %d us - %d rounds - %d open - %d closed -- ", time, 0, 0, 0);
 
	}
 
	// handle "path not found" state
 
	if (path_not_found) {
 
		// PF didn't find the route
 
		if (!HASBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) {
 
			// it is first time the problem occurred, set the "path not found" flag
 
			SETBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION);
 
			// and notify user about the event
 
			if (_patches.lost_train_warn && v->owner == _local_player) {
 
				SetDParam(0, v->unitnumber);
 
				AddNewsItem(
 
					STR_TRAIN_IS_LOST,
 
					NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0),
 
					v->index,
 
					0);
 
			}
 
		}
 
	} else {
 
		// route found, is the train marked with "path not found" flag?
 
		if (HASBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) {
 
			// clear the flag as the PF's problem was solved
 
			CLRBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION);
 
			// can we also delete the "News" item somehow?
 
		}
 
	}
 

	
 
#ifdef PF_BENCHMARK
 
	TOC("PF time = ", 1)
 
#endif
 

	
 
	return best_track;
 
}
 

	
 

	
 
static bool CheckReverseTrain(Vehicle *v)
 
{
 
	TrainTrackFollowerData fd;
 
	int i, r;
 
	int best_track;
 
	uint best_bird_dist  = 0;
 
	uint best_track_dist = 0;
 
	uint reverse, reverse_best;
 

	
 
	if (_opt.diff.line_reverse_mode != 0 ||
 
			v->u.rail.track & 0xC0 ||
 
			!(v->direction & 1))
 
		return false;
 

	
 
	FillWithStationData(&fd, v);
 

	
 
	best_track = -1;
 
	reverse_best = reverse = 0;
 

	
 
	assert(v->u.rail.track);
 

	
 
	i = _search_directions[FIND_FIRST_BIT(v->u.rail.track)][DirToDiagDir(v->direction)];
 

	
 
	if (_patches.yapf.rail_use_yapf) {
 
		reverse_best = YapfCheckReverseTrain(v);
 
	} else if (_patches.new_pathfinding_all) { /* Use a new pathfinding for everything */
 
		NPFFindStationOrTileData fstd;
 
		NPFFoundTargetData ftd;
 
		Trackdir trackdir, trackdir_rev;
 
		Vehicle* last = GetLastVehicleInChain(v);
 

	
 
		NPFFillWithOrderData(&fstd, v);
 

	
 
		trackdir = GetVehicleTrackdir(v);
 
		trackdir_rev = ReverseTrackdir(GetVehicleTrackdir(last));
 
		assert(trackdir != 0xff);
 
		assert(trackdir_rev != 0xff);
 

	
 
		ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, last->tile, trackdir_rev, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes);
 
		if (ftd.best_bird_dist != 0) {
 
			/* We didn't find anything, just keep on going straight ahead */
 
			reverse_best = false;
 
		} else {
 
			if (NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE)) {
 
				reverse_best = true;
 
			} else {
 
				reverse_best = false;
 
			}
 
		}
 
	} else {
 
		for (;;) {
 
			fd.best_bird_dist = (uint)-1;
 
			fd.best_track_dist = (uint)-1;
 

	
 
			NewTrainPathfind(v->tile, v->dest_tile, v->u.rail.compatible_railtypes, (DiagDirection)(reverse ^ i), (NTPEnumProc*)NtpCallbFindStation, &fd);
 

	
 
			if (best_track != -1) {
 
				if (best_bird_dist != 0) {
 
					if (fd.best_bird_dist != 0) {
 
						/* neither reached the destination, pick the one with the smallest bird dist */
 
						if (fd.best_bird_dist > best_bird_dist) goto bad;
 
						if (fd.best_bird_dist < best_bird_dist) goto good;
 
					} else {
 
						/* we found the destination for the first time */
 
						goto good;
 
					}
 
				} else {
 
					if (fd.best_bird_dist != 0) {
 
						/* didn't find destination, but we've found the destination previously */
 
						goto bad;
 
					} else {
 
						/* both old & new reached the destination, compare track length */
 
						if (fd.best_track_dist > best_track_dist) goto bad;
 
						if (fd.best_track_dist < best_track_dist) goto good;
 
					}
 
				}
 

	
 
				/* if we reach this position, there's two paths of equal value so far.
 
				 * pick one randomly. */
 
				r = GB(Random(), 0, 8);
 
				if (_pick_track_table[i] == (v->direction & 3)) r += 80;
 
				if (_pick_track_table[best_track] == (v->direction & 3)) r -= 80;
 
				if (r <= 127) goto bad;
 
			}
 
good:;
 
			best_track = i;
 
			best_bird_dist = fd.best_bird_dist;
 
			best_track_dist = fd.best_track_dist;
 
			reverse_best = reverse;
 
bad:;
 
			if (reverse != 0) break;
 
			reverse = 2;
 
		}
 
	}
 

	
 
	return reverse_best != 0;
 
}
 

	
 
static bool ProcessTrainOrder(Vehicle *v)
 
{
 
	const Order *order;
 
	bool at_waypoint = false;
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_DEPOT:
 
			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return false;
 
			if ((v->current_order.flags & OF_SERVICE_IF_NEEDED) &&
 
					!VehicleNeedsService(v)) {
 
				v->cur_order_index++;
 
			}
 
			break;
 

	
 
		case OT_LOADING:
 
		case OT_LEAVESTATION:
 
			return false;
 

	
 
		default: break;
 
	}
 

	
 
	// check if we've reached the waypoint?
 
	if (v->current_order.type == OT_GOTO_WAYPOINT && v->tile == v->dest_tile) {
 
		v->cur_order_index++;
 
		at_waypoint = true;
 
	}
 

	
 
	// check if we've reached a non-stop station while TTDPatch nonstop is enabled..
 
	if (_patches.new_nonstop &&
 
			v->current_order.flags & OF_NON_STOP &&
 
			IsTileType(v->tile, MP_STATION) &&
 
			v->current_order.dest == GetStationIndex(v->tile)) {
 
		v->cur_order_index++;
 
	}
 

	
 
	// Get the current order
 
	if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
 

	
 
	order = GetVehicleOrder(v, v->cur_order_index);
 

	
 
	// If no order, do nothing.
 
	if (order == NULL) {
 
		v->current_order.type = OT_NOTHING;
 
		v->current_order.flags = 0;
 
		v->dest_tile = 0;
 
		return false;
 
	}
 

	
 
	// If it is unchanged, keep it.
 
	if (order->type  == v->current_order.type &&
 
			order->flags == v->current_order.flags &&
 
			order->dest  == v->current_order.dest)
 
		return false;
 

	
 
	// Otherwise set it, and determine the destination tile.
 
	v->current_order = *order;
 

	
 
	v->dest_tile = 0;
 

	
 
	InvalidateVehicleOrder(v);
 

	
 
	switch (order->type) {
 
		case OT_GOTO_STATION:
 
			if (order->dest == v->last_station_visited)
 
				v->last_station_visited = INVALID_STATION;
 
			v->dest_tile = GetStation(order->dest)->xy;
 
			break;
 

	
 
		case OT_GOTO_DEPOT:
 
			v->dest_tile = GetDepot(order->dest)->xy;
 
			break;
 

	
 
		case OT_GOTO_WAYPOINT:
 
			v->dest_tile = GetWaypoint(order->dest)->xy;
 
			break;
 

	
 
		default:
 
			return false;
 
	}
 

	
 
	return !at_waypoint && CheckReverseTrain(v);
 
}
 

	
 
static void MarkTrainDirty(Vehicle *v)
 
{
 
	do {
 
		v->cur_image = GetTrainImage(v, v->direction);
 
		MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
 
	} while ((v = v->next) != NULL);
 
}
 

	
 
static void HandleTrainLoading(Vehicle *v, bool mode)
 
{
 
	if (v->current_order.type == OT_NOTHING) return;
 

	
 
	if (v->current_order.type != OT_DUMMY) {
 
		if (v->current_order.type != OT_LOADING) return;
 
	switch (v->current_order.type) {
 
		case OT_LOADING:
 
		if (mode) return;
 

	
 
		// don't mark the train as lost if we're loading on the final station.
 
		if (v->current_order.flags & OF_NON_STOP)
 
			if (v->current_order.flags & OF_NON_STOP) {
 
			v->u.rail.days_since_order_progr = 0;
 
			}
 

	
 
		if (--v->load_unload_time_rem) return;
 

	
 
		if (CanFillVehicle(v) && (v->current_order.flags & OF_FULL_LOAD ||
 
				(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED)))) {
 
			if (CanFillVehicle(v) && (
 
						v->current_order.flags & OF_FULL_LOAD ||
 
						(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED))
 
					)) {
 
			v->u.rail.days_since_order_progr = 0; /* Prevent a train lost message for full loading trains */
 
			SET_EXPENSES_TYPE(EXPENSES_TRAIN_INC);
 
			if (LoadUnloadVehicle(v, false)) {
 
				InvalidateWindow(WC_TRAINS_LIST, v->owner);
 
				MarkTrainDirty(v);
 

	
 
				// need to update acceleration and cached values since the goods on the train changed.
 
				TrainCargoChanged(v);
 
				UpdateTrainAcceleration(v);
 
			}
 
			return;
 
		}
 

	
 
		TrainPlayLeaveStationSound(v);
 

	
 
		{
 
			Order b = v->current_order;
 
			v->LeaveStation();
 

	
 
			// If this was not the final order, don't remove it from the list.
 
			if (!(b.flags & OF_NON_STOP)) return;
 
		}
 
			break;
 

	
 
		case OT_DUMMY: break;
 

	
 
		default: return;
 
	}
 

	
 
	v->u.rail.days_since_order_progr = 0;
 
	v->cur_order_index++;
 
	InvalidateVehicleOrder(v);
 
}
 

	
 
static int UpdateTrainSpeed(Vehicle *v)
 
{
 
	uint spd;
 
	uint accel;
 

	
 
	if (v->vehstatus & VS_STOPPED || HASBIT(v->u.rail.flags, VRF_REVERSING)) {
 
		if (_patches.realistic_acceleration) {
 
			accel = GetTrainAcceleration(v, AM_BRAKE) * 2;
 
		} else {
 
			accel = v->acceleration * -2;
 
		}
 
	} else {
 
		if (_patches.realistic_acceleration) {
 
			accel = GetTrainAcceleration(v, AM_ACCEL);
 
		} else {
 
			accel = v->acceleration;
 
		}
 
	}
 

	
 
	spd = v->subspeed + accel * 2;
 
	v->subspeed = (byte)spd;
 
	{
 
		int tempmax = v->max_speed;
 
		if (v->cur_speed > v->max_speed)
 
			tempmax = v->cur_speed - (v->cur_speed / 10) - 1;
 
		v->cur_speed = spd = clamp(v->cur_speed + ((int)spd >> 8), 0, tempmax);
 
	}
 

	
 
	if (!(v->direction & 1)) spd = spd * 3 >> 2;
 

	
 
	spd += v->progress;
 
	v->progress = (byte)spd;
 
	return (spd >> 8);
 
}
 

	
 
static void TrainEnterStation(Vehicle *v, StationID station)
 
{
 
	Station *st;
 
	uint32 flags;
 

	
 
	v->last_station_visited = station;
 

	
 
	/* check if a train ever visited this station before */
 
	st = GetStation(station);
 
	if (!(st->had_vehicle_of_type & HVOT_TRAIN)) {
 
		st->had_vehicle_of_type |= HVOT_TRAIN;
 
		SetDParam(0, st->index);
 
		flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0);
 
		AddNewsItem(
 
			STR_8801_CITIZENS_CELEBRATE_FIRST,
 
			flags,
 
			v->index,
 
			0
 
		);
 
	}
 

	
 
	// Did we reach the final destination?
 
	if (v->current_order.type == OT_GOTO_STATION &&
 
			v->current_order.dest == station) {
 
		// Yeah, keep the load/unload flags
 
		// Non Stop now means if the order should be increased.
 
		v->BeginLoading();
 
		v->current_order.flags &= OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER;
 
		v->current_order.flags |= OF_NON_STOP;
 
	} else {
 
		// No, just do a simple load
 
		v->BeginLoading();
 
		v->current_order.flags = 0;
 
	}
 
	v->current_order.dest = 0;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_TRAIN_INC);
 
	if (LoadUnloadVehicle(v, true) != 0) {
 
		InvalidateWindow(WC_TRAINS_LIST, v->owner);
 
		TrainCargoChanged(v);
 
		UpdateTrainAcceleration(v);
 
	}
 
	MarkTrainDirty(v);
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
}
 

	
 
static byte AfterSetTrainPos(Vehicle *v, bool new_tile)
 
{
 
	byte new_z, old_z;
 

	
 
	// need this hint so it returns the right z coordinate on bridges.
 
	new_z = GetSlopeZ(v->x_pos, v->y_pos);
 

	
 
	old_z = v->z_pos;
 
	v->z_pos = new_z;
 

	
 
	if (new_tile) {
 
		CLRBIT(v->u.rail.flags, VRF_GOINGUP);
 
		CLRBIT(v->u.rail.flags, VRF_GOINGDOWN);
 

	
 
		if (new_z != old_z) {
 
			TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
 

	
 
			// XXX workaround, whole UP/DOWN detection needs overhaul
 
			if (!IsTunnelTile(tile)) {
 
				SETBIT(v->u.rail.flags, (new_z > old_z) ? VRF_GOINGUP : VRF_GOINGDOWN);
 
			}
 
		}
 
	}
 

	
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
	return old_z;
 
}
 

	
 
static const Direction _new_vehicle_direction_table[11] = {
 
	DIR_N , DIR_NW, DIR_W , INVALID_DIR,
 
	DIR_NE, DIR_N , DIR_SW, INVALID_DIR,
 
	DIR_E , DIR_SE, DIR_S
 
};
 

	
 
static Direction GetNewVehicleDirectionByTile(TileIndex new_tile, TileIndex old_tile)
 
{
 
	uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 +
 
							TileX(new_tile) - TileX(old_tile) + 1;
 
	assert(offs < 11);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static Direction GetNewVehicleDirection(const Vehicle *v, int x, int y)
 
{
 
	uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1);
 
	assert(offs < 11);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static int GetDirectionToVehicle(const Vehicle *v, int x, int y)
 
{
 
	byte offs;
 

	
 
	x -= v->x_pos;
 
	if (x >= 0) {
 
		offs = (x > 2) ? 0 : 1;
 
	} else {
 
		offs = (x < -2) ? 2 : 1;
 
	}
 

	
 
	y -= v->y_pos;
 
	if (y >= 0) {
 
		offs += ((y > 2) ? 0 : 1) * 4;
 
	} else {
 
		offs += ((y < -2) ? 2 : 1) * 4;
 
	}
 

	
 
	assert(offs < 11);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
/* Check if the vehicle is compatible with the specified tile */
 
static bool CheckCompatibleRail(const Vehicle *v, TileIndex tile)
 
{
 
	return
 
		IsTileOwner(tile, v->owner) && (
 
			!IsFrontEngine(v) ||
 
			HASBIT(
 
				v->u.rail.compatible_railtypes,
 
				IsTileType(tile, MP_STREET) ? GetRailTypeCrossing(tile) : GetRailType(tile)
 
			)
 
		);
 
}
 

	
 
typedef struct {
 
	byte small_turn, large_turn;
 
	byte z_up; // fraction to remove when moving up
 
	byte z_down; // fraction to remove when moving down
 
} RailtypeSlowdownParams;
 

	
 
static const RailtypeSlowdownParams _railtype_slowdown[] = {
 
	// normal accel
 
	{256 / 4, 256 / 2, 256 / 4, 2}, // normal
 
	{256 / 4, 256 / 2, 256 / 4, 2}, // electrified
 
	{256 / 4, 256 / 2, 256 / 4, 2}, // monorail
 
	{0,       256 / 2, 256 / 4, 2}, // maglev
 
};
 

	
 
/* Modify the speed of the vehicle due to a turn */
 
static void AffectSpeedByDirChange(Vehicle* v, Direction new_dir)
 
{
 
	DirDiff diff;
 
	const RailtypeSlowdownParams *rsp;
 

	
 
	if (_patches.realistic_acceleration) return;
 

	
 
	diff = DirDifference(v->direction, new_dir);
 
	if (diff == DIRDIFF_SAME) return;
 

	
 
	rsp = &_railtype_slowdown[v->u.rail.railtype];
 
	v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? rsp->small_turn : rsp->large_turn) * v->cur_speed >> 8;
 
}
 

	
 
/* Modify the speed of the vehicle due to a change in altitude */
 
static void AffectSpeedByZChange(Vehicle *v, byte old_z)
 
{
 
	const RailtypeSlowdownParams *rsp;
 
	if (old_z == v->z_pos || _patches.realistic_acceleration) return;
 

	
 
	rsp = &_railtype_slowdown[v->u.rail.railtype];
 

	
 
	if (old_z < v->z_pos) {
 
		v->cur_speed -= (v->cur_speed * rsp->z_up >> 8);
 
	} else {
 
		uint16 spd = v->cur_speed + rsp->z_down;
 
		if (spd <= v->max_speed) v->cur_speed = spd;
 
	}
 
}
 

	
 
static const DiagDirection _otherside_signal_directions[] = {
 
	DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE, INVALID_DIAGDIR, INVALID_DIAGDIR,
 
	DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
 
};
 

	
 
static void TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
 
{
 
	if (IsTileType(tile, MP_RAILWAY) &&
 
			GetRailTileType(tile) == RAIL_TILE_SIGNALS) {
 
		uint i = FindFirstBit2x64(GetTrackBits(tile) * 0x101 & _reachable_tracks[dir]);
 
		UpdateSignalsOnSegment(tile, _otherside_signal_directions[i]);
 
	}
 
}
 

	
 

	
 
typedef struct TrainCollideChecker {
 
	const Vehicle *v;
 
	const Vehicle *v_skip;
 
} TrainCollideChecker;
 

	
 
static void *FindTrainCollideEnum(Vehicle *v, void *data)
 
{
 
	const TrainCollideChecker* tcc = (TrainCollideChecker*)data;
 

	
 
	if (v != tcc->v &&
 
			v != tcc->v_skip &&
 
			v->type == VEH_Train &&
 
			v->u.rail.track != 0x80 &&
 
			myabs(v->z_pos - tcc->v->z_pos) <= 6 &&
 
			myabs(v->x_pos - tcc->v->x_pos) < 6 &&
 
			myabs(v->y_pos - tcc->v->y_pos) < 6) {
 
		return v;
 
	} else {
 
		return NULL;
 
	}
 
}
 

	
 
static void SetVehicleCrashed(Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	if (v->u.rail.crash_anim_pos != 0) return;
 

	
 
	v->u.rail.crash_anim_pos++;
 

	
 
	u = v;
 
	BEGIN_ENUM_WAGONS(v)
 
		v->vehstatus |= VS_CRASHED;
 
	END_ENUM_WAGONS(v)
 

	
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, u->index, STATUS_BAR);
 
}
 

	
 
static uint CountPassengersInTrain(const Vehicle* v)
 
{
 
	uint num = 0;
 
	BEGIN_ENUM_WAGONS(v)
 
		if (v->cargo_type == CT_PASSENGERS) num += v->cargo_count;
 
	END_ENUM_WAGONS(v)
 
	return num;
 
}
 

	
 
/*
 
 * Checks whether the specified train has a collision with another vehicle. If
 
 * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front.
 
 * Reports the incident in a flashy news item, modifies station ratings and
 
 * plays a sound.
 
 */
 
static void CheckTrainCollision(Vehicle *v)
 
{
 
	TrainCollideChecker tcc;
 
	Vehicle *coll;
 
	Vehicle *realcoll;
 
	uint num;
 

	
 
	/* can't collide in depot */
 
	if (v->u.rail.track == 0x80) return;
 

	
 
	assert(v->u.rail.track == 0x40 || TileVirtXY(v->x_pos, v->y_pos) == v->tile);
 

	
 
	tcc.v = v;
 
	tcc.v_skip = v->next;
 

	
 
	/* find colliding vehicle */
 
	realcoll = (Vehicle*)VehicleFromPos(TileVirtXY(v->x_pos, v->y_pos), &tcc, FindTrainCollideEnum);
 
	if (realcoll == NULL) return;
 

	
 
	coll = GetFirstVehicleInChain(realcoll);
 

	
 
	/* it can't collide with its own wagons */
 
	if (v == coll ||
 
			(v->u.rail.track & 0x40 && (v->direction & 2) != (realcoll->direction & 2)))
 
		return;
 

	
 
	//two drivers + passangers killed in train v
 
	num = 2 + CountPassengersInTrain(v);
 
	if (!(coll->vehstatus & VS_CRASHED))
 
		//two drivers + passangers killed in train coll (if it was not crashed already)
 
		num += 2 + CountPassengersInTrain(coll);
 

	
 
	SetVehicleCrashed(v);
 
	if (IsFrontEngine(coll)) SetVehicleCrashed(coll);
 

	
 
	SetDParam(0, num);
 
	AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL,
 
		NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, 0),
 
		v->index,
 
		0
 
	);
 

	
 
	ModifyStationRatingAround(v->tile, v->owner, -160, 30);
 
	SndPlayVehicleFx(SND_13_BIG_CRASH, v);
 
}
 

	
 
typedef struct VehicleAtSignalData {
 
	TileIndex tile;
 
	Direction direction;
 
} VehicleAtSignalData;
 

	
 
static void *CheckVehicleAtSignal(Vehicle *v, void *data)
 
{
 
	const VehicleAtSignalData* vasd = (VehicleAtSignalData*)data;
 

	
 
	if (v->type == VEH_Train && IsFrontEngine(v) && v->tile == vasd->tile) {
 
		DirDiff diff = ChangeDirDiff(DirDifference(v->direction, vasd->direction), DIRDIFF_90RIGHT);
 

	
 
		if (diff == DIRDIFF_90RIGHT || (v->cur_speed <= 5 && diff <= DIRDIFF_REVERSE)) return v;
 
	}
 
	return NULL;
 
}
 

	
 
static void TrainController(Vehicle *v, bool update_image)
 
{
 
	Vehicle *prev;
 
	GetNewVehiclePosResult gp;
 
	uint32 r, tracks,ts;
 
	Trackdir i;
 
	DiagDirection enterdir;
 
	Direction dir;
 
	Direction newdir;
 
	Direction chosen_dir;
 
	TrackBits chosen_track;
 
	byte old_z;
 

	
 
	/* For every vehicle after and including the given vehicle */
 
	for (prev = GetPrevVehicleInChain(v); v != NULL; prev = v, v = v->next) {
 
		BeginVehicleMove(v);
 

	
 
		if (v->u.rail.track != 0x40) {
 
			/* Not inside tunnel */
 
			if (GetNewVehiclePos(v, &gp)) {
 
				/* Staying in the old tile */
 
				if (v->u.rail.track == 0x80) {
 
					/* inside depot */
 
					gp.x = v->x_pos;
 
					gp.y = v->y_pos;
 
				} else {
 
					/* is not inside depot */
 

	
 
					if (IsFrontEngine(v) && !TrainCheckIfLineEnds(v)) return;
 

	
 
					r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
 
					if (r & 0x8) {
 
						//debug("%x & 0x8", r);
 
						goto invalid_rail;
 
					}
 
					if (r & 0x2) {
 
						TrainEnterStation(v, r >> 8);
 
						return;
 
					}
 

	
 
					if (v->current_order.type == OT_LEAVESTATION) {
 
						v->current_order.type = OT_NOTHING;
 
						v->current_order.flags = 0;
 
						InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
					}
 
				}
 
			} else {
 
				/* A new tile is about to be entered. */
 

	
 
				TrackBits bits;
 
				/* Determine what direction we're entering the new tile from */
 
				dir = GetNewVehicleDirectionByTile(gp.new_tile, gp.old_tile);
 
				enterdir = DirToDiagDir(dir);
 
				assert(enterdir==0 || enterdir==1 || enterdir==2 || enterdir==3);
 

	
 
				/* Get the status of the tracks in the new tile and mask
 
				 * away the bits that aren't reachable. */
 
				ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL) & _reachable_tracks[enterdir];
 

	
 
				/* Combine the from & to directions.
 
				 * Now, the lower byte contains the track status, and the byte at bit 16 contains
 
				 * the signal status. */
 
				tracks = ts | (ts >> 8);
 
				bits = (TrackBits)(tracks & TRACK_BIT_MASK);
 
				if ((_patches.new_pathfinding_all || _patches.yapf.rail_use_yapf) && _patches.forbid_90_deg && prev == NULL) {
 
					/* We allow wagons to make 90 deg turns, because forbid_90_deg
 
					 * can be switched on halfway a turn */
 
					bits &= ~TrackCrossesTracks(FindFirstTrack(v->u.rail.track));
 
				}
 

	
 
				if (bits == TRACK_BIT_NONE) {
 
					//debug("%x == 0", bits);
 
					goto invalid_rail;
 
				}
 

	
 
				/* Check if the new tile contrains tracks that are compatible
 
				 * with the current train, if not, bail out. */
 
				if (!CheckCompatibleRail(v, gp.new_tile)) {
 
					//debug("!CheckCompatibleRail(%p, %x)", v, gp.new_tile);
 
					goto invalid_rail;
 
				}
 

	
 
				if (prev == NULL) {
 
					/* Currently the locomotive is active. Determine which one of the
 
					 * available tracks to choose */
 
					chosen_track = 1 << ChooseTrainTrack(v, gp.new_tile, enterdir, bits);
 
					assert(chosen_track & tracks);
 

	
 
					/* Check if it's a red signal and that force proceed is not clicked. */
 
					if ( (tracks>>16)&chosen_track && v->u.rail.force_proceed == 0) goto red_light;
 
				} else {
 
					static byte _matching_tracks[8] = {0x30, 1, 0xC, 2, 0x30, 1, 0xC, 2};
 

	
 
					/* The wagon is active, simply follow the prev vehicle. */
 
					chosen_track = (TrackBits)(byte)(_matching_tracks[GetDirectionToVehicle(prev, gp.x, gp.y)] & bits);
 
				}
 

	
 
				/* make sure chosen track is a valid track */
 
				assert(chosen_track==1 || chosen_track==2 || chosen_track==4 || chosen_track==8 || chosen_track==16 || chosen_track==32);
 

	
 
				/* Update XY to reflect the entrance to the new tile, and select the direction to use */
 
				{
 
					const byte *b = _initial_tile_subcoord[FIND_FIRST_BIT(chosen_track)][enterdir];
 
					gp.x = (gp.x & ~0xF) | b[0];
 
					gp.y = (gp.y & ~0xF) | b[1];
 
					chosen_dir = (Direction)b[2];
 
				}
 

	
 
				/* Call the landscape function and tell it that the vehicle entered the tile */
 
				r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
 
				if (r & 0x8) {
 
					//debug("%x & 0x8", r);
 
					goto invalid_rail;
 
				}
 

	
 
				if (IsLevelCrossingTile(v->tile) && v->next == NULL) {
 
					UnbarCrossing(v->tile);
 
					MarkTileDirtyByTile(v->tile);
 
				}
 

	
 
				if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
 

	
 
				if (!(r&0x4)) {
 
					v->tile = gp.new_tile;
 

	
 
					if (GetTileRailType(gp.new_tile, FindFirstTrack(chosen_track)) != GetTileRailType(gp.old_tile, FindFirstTrack(v->u.rail.track))) {
 
						TrainPowerChanged(GetFirstVehicleInChain(v));
 
					}
 

	
 
					v->u.rail.track = chosen_track;
 
					assert(v->u.rail.track);
 
				}
 

	
 
				if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
 

	
 
				/* Signals can only change when the first
 
				 * (above) or the last vehicle moves. */
 
				if (v->next == NULL)
 
					TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
 

	
 
				if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir);
 

	
 
				v->direction = chosen_dir;
 
			}
 
		} else {
 
			/* in tunnel on on a bridge */
 
			GetNewVehiclePos(v, &gp);
 

	
 
			SetSpeedLimitOnBridge(v);
 

	
 
			if (!(IsTunnelTile(gp.new_tile) || IsBridgeTile(gp.new_tile)) || !(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y) & 0x4)) {
 
				v->x_pos = gp.x;
 
				v->y_pos = gp.y;
 
				VehiclePositionChanged(v);
 
				if (!(v->vehstatus & VS_HIDDEN)) EndVehicleMove(v);
 
				continue;
 
			}
 
		}
 

	
 
		/* update image of train, as well as delta XY */
 
		newdir = GetNewVehicleDirection(v, gp.x, gp.y);
 
		UpdateTrainDeltaXY(v, newdir);
 
		if (update_image) v->cur_image = GetTrainImage(v, newdir);
 

	
 
		v->x_pos = gp.x;
 
		v->y_pos = gp.y;
 

	
 
		/* update the Z position of the vehicle */
 
		old_z = AfterSetTrainPos(v, (gp.new_tile != gp.old_tile));
 

	
 
		if (prev == NULL) {
 
			/* This is the first vehicle in the train */
 
			AffectSpeedByZChange(v, old_z);
 
		}
 
	}
 
	return;
 

	
 
invalid_rail:
 
	/* We've reached end of line?? */
 
	if (prev != NULL) error("!Disconnecting train");
 
	goto reverse_train_direction;
 

	
 
red_light: {
 
	/* We're in front of a red signal ?? */
 
		/* find the first set bit in ts. need to do it in 2 steps, since
 
		 * FIND_FIRST_BIT only handles 6 bits at a time. */
 
		i = FindFirstTrackdir((TrackdirBits)(uint16)ts);
 

	
 
		if (!HasSignalOnTrackdir(gp.new_tile, ReverseTrackdir(i))) {
 
			v->cur_speed = 0;
 
			v->subspeed = 0;
 
			v->progress = 255 - 100;
 
			if (++v->load_unload_time_rem < _patches.wait_oneway_signal * 20) return;
 
		} else if (HasSignalOnTrackdir(gp.new_tile, i)){
 
			v->cur_speed = 0;
 
			v->subspeed = 0;
 
			v->progress = 255-10;
 
			if (++v->load_unload_time_rem < _patches.wait_twoway_signal * 73) {
 
				TileIndex o_tile = gp.new_tile + TileOffsByDiagDir(enterdir);
 
				VehicleAtSignalData vasd;
 
				vasd.tile = o_tile;
 
				vasd.direction = ReverseDir(dir);
 

	
 
				/* check if a train is waiting on the other side */
 
				if (VehicleFromPos(o_tile, &vasd, CheckVehicleAtSignal) == NULL) return;
 
			}
 
		}
 
	}
 

	
 
reverse_train_direction:
 
	v->load_unload_time_rem = 0;
 
	v->cur_speed = 0;
 
	v->subspeed = 0;
 
	ReverseTrainDirection(v);
 
}
 

	
 
extern TileIndex CheckTunnelBusy(TileIndex tile, uint *length);
 

	
 
/**
 
 * Deletes/Clears the last wagon of a crashed train. It takes the engine of the
 
 * train, then goes to the last wagon and deletes that. Each call to this function
 
 * will remove the last wagon of a crashed train. If this wagon was on a crossing,
 
 * or inside a tunnel, recalculate the signals as they might need updating
 
 * @param v the @Vehicle of which last wagon is to be removed
 
 */
 
static void DeleteLastWagon(Vehicle *v)
 
{
 
	Vehicle *u = v;
 

	
 
	/* Go to the last wagon and delete the link pointing there
 
	 * *u is then the one-before-last wagon, and *v the last
 
	 * one which will physicially be removed */
 
	for (; v->next != NULL; v = v->next) u = v;
 
	u->next = NULL;
 

	
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
	RebuildVehicleLists();
 
	InvalidateWindow(WC_COMPANY, v->owner);
 

	
 
	BeginVehicleMove(v);
 
	EndVehicleMove(v);
 
	DeleteVehicle(v);
 

	
 
	if (!(v->u.rail.track & 0xC0))
 
		SetSignalsOnBothDir(v->tile, FIND_FIRST_BIT(v->u.rail.track));
 

	
 
	/* Check if the wagon was on a road/rail-crossing and disable it if no
 
	 * others are on it */
 
	DisableTrainCrossing(v->tile);
 

	
 
	if ( (v->u.rail.track == 0x40 && v->vehstatus & VS_HIDDEN) ) { // inside a tunnel
 
		TileIndex endtile = CheckTunnelBusy(v->tile, NULL);
 

	
 
		if (endtile == INVALID_TILE) return; // tunnel is busy (error returned)
 

	
 
		switch (v->direction) {
 
			case 1:
 
			case 5:
 
				SetSignalsOnBothDir(v->tile, 0);
 
				SetSignalsOnBothDir(endtile, 0);
 
				break;
 

	
 
			case 3:
 
			case 7:
 
				SetSignalsOnBothDir(v->tile, 1);
 
				SetSignalsOnBothDir(endtile, 1);
 
				break;
 

	
 
			default:
 
				break;
 
		}
 
	}
 
}
 

	
 
static void ChangeTrainDirRandomly(Vehicle *v)
 
{
 
	static const DirDiff delta[] = {
 
		DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
 
	};
 

	
 
	do {
 
		/* We don't need to twist around vehicles if they're not visible */
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
 
			BeginVehicleMove(v);
 
			UpdateTrainDeltaXY(v, v->direction);
 
			v->cur_image = GetTrainImage(v, v->direction);
 
			/* Refrain from updating the z position of the vehicle when on
 
			   a bridge, because AfterSetTrainPos will put the vehicle under
 
			   the bridge in that case */
 
			if (!(v->u.rail.track & 0x40)) AfterSetTrainPos(v, false);
 
		}
 
	} while ((v = v->next) != NULL);
 
}
 

	
 
static void HandleCrashedTrain(Vehicle *v)
 
{
 
	int state = ++v->u.rail.crash_anim_pos;
 
	uint32 r;
 
	Vehicle *u;
 

	
 
	if (state == 4 && !(v->u.rail.track & VS_HIDDEN)) {
 
		CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
 
	}
 

	
 
	if (state <= 200 && CHANCE16R(1, 7, r)) {
 
		int index = (r * 10 >> 16);
 

	
 
		u = v;
 
		do {
 
			if (--index < 0) {
 
				r = Random();
 

	
 
				CreateEffectVehicleRel(u,
 
					GB(r,  8, 3) + 2,
 
					GB(r, 16, 3) + 2,
 
					GB(r,  0, 3) + 5,
 
					EV_EXPLOSION_SMALL);
 
				break;
 
			}
 
		} while ((u = u->next) != NULL);
 
	}
 

	
 
	if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
 

	
 
	if (state >= 4440 && !(v->tick_counter&0x1F)) {
 
		DeleteLastWagon(v);
 
		InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train);
 
	}
 
}
 

	
 
static void HandleBrokenTrain(Vehicle *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->cur_speed = 0;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
 
			SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
 
				SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
 
		}
 

	
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
 
			if (u != NULL) u->u.special.unk0 = v->breakdown_delay * 2;
 
		}
 
	}
 

	
 
	if (!(v->tick_counter & 3)) {
 
		if (!--v->breakdown_delay) {
 
			v->breakdown_ctr = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		}
 
	}
 
}
 

	
 
static const byte _breakdown_speeds[16] = {
 
	225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
 
};
 

	
 
static bool TrainCheckIfLineEnds(Vehicle *v)
 
{
 
	TileIndex tile;
 
	uint x,y;
 
	uint16 break_speed;
 
	DiagDirection dir;
 
	int t;
 
	uint32 ts;
 

	
 
	t = v->breakdown_ctr;
 
	if (t > 1) {
 
		v->vehstatus |= VS_TRAIN_SLOWING;
 

	
 
		break_speed = _breakdown_speeds[GB(~t, 4, 4)];
 
		if (break_speed < v->cur_speed) v->cur_speed = break_speed;
 
	} else {
 
		v->vehstatus &= ~VS_TRAIN_SLOWING;
 
	}
 

	
 
	if (v->u.rail.track & 0x40) return true; // exit if inside a tunnel
 
	if (v->u.rail.track & 0x80) return true; // exit if inside a depot
 

	
 
	tile = v->tile;
 

	
 
	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
		DiagDirection dir;
 

	
 
		dir = IsTunnel(tile) ? GetTunnelDirection(tile) : GetBridgeRampDirection(tile);
 
		if (DiagDirToDir(dir) == v->direction) return true;
 
	}
 

	
 
	// depot?
 
	/* XXX -- When enabled, this makes it possible to crash trains of others
 
	     (by building a depot right against a station) */
 
/*	if (IsTileType(tile, MP_RAILWAY) && GetRailTileType(tile) == RAIL_TILE_DEPOT_WAYPOINT)
 
		return true;*/
 

	
 
	/* Determine the non-diagonal direction in which we will exit this tile */
 
	dir = DirToDiagDir(v->direction);
 
	if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[dir]) {
 
		dir = ChangeDiagDir(dir, DIAGDIRDIFF_90LEFT);
 
	}
 
	/* Calculate next tile */
 
	tile += TileOffsByDiagDir(dir);
 
	// determine the track status on the next tile.
 
	ts = GetTileTrackStatus(tile, TRANSPORT_RAIL) & _reachable_tracks[dir];
 

	
 
	/* Calc position within the current tile ?? */
 
	x = v->x_pos & 0xF;
 
	y = v->y_pos & 0xF;
 

	
 
	switch (v->direction) {
0 comments (0 inline, 0 general)