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
 
@@ -876,793 +876,797 @@ static bool UpdateAircraftSpeed(Vehicle 
 

	
 
	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
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... */
src/train_cmd.cpp
Show inline comments
 
@@ -2224,804 +2224,808 @@ static bool CheckTrainStayInDepot(Vehicl
 

	
 
/* 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;
 
					}
0 comments (0 inline, 0 general)