File diff r9412:d3e87d714ab7 → r9413:fcf267325763
src/aircraft_cmd.cpp
Show inline comments
 
@@ -194,310 +194,310 @@ static SpriteID GetAircraftIcon(EngineID
 

	
 
	if (is_custom_sprite(spritenum)) {
 
		SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
 
		if (sprite != 0) return sprite;
 

	
 
		spritenum = GetEngine(engine)->image_index;
 
	}
 

	
 
	return 6 + _aircraft_sprite[spritenum];
 
}
 

	
 
void DrawAircraftEngine(int x, int y, EngineID engine, SpriteID pal)
 
{
 
	DrawSprite(GetAircraftIcon(engine), pal, x, y);
 

	
 
	if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
 
		SpriteID rotor_sprite = GetCustomRotorIcon(engine);
 
		if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
 
		DrawSprite(rotor_sprite, PAL_NONE, x, y - 5);
 
	}
 
}
 

	
 
/** Get the size of the sprite of an aircraft sprite heading west (used for lists)
 
 * @param engine The engine to get the sprite from
 
 * @param width The width of the sprite
 
 * @param height The height of the sprite
 
 */
 
void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height)
 
{
 
	const Sprite *spr = GetSprite(GetAircraftIcon(engine));
 

	
 
	width  = spr->width;
 
	height = spr->height;
 
}
 

	
 
static CommandCost EstimateAircraftCost(EngineID engine, const AircraftVehicleInfo *avi)
 
{
 
	return CommandCost(EXPENSES_NEW_VEHICLES, GetEngineProperty(engine, 0x0B, avi->base_cost) * (_price.aircraft_base >> 3) >> 5);
 
}
 

	
 

	
 
/**
 
 * Calculates cargo capacity based on an aircraft's passenger
 
 * and mail capacities.
 
 * @param cid Which cargo type to calculate a capacity for.
 
 * @param avi Which engine to find a cargo capacity for.
 
 * @return New cargo capacity value.
 
 */
 
uint16 AircraftDefaultCargoCapacity(CargoID cid, const AircraftVehicleInfo *avi)
 
{
 
	assert(cid != CT_INVALID);
 

	
 
	/* An aircraft can carry twice as much goods as normal cargo,
 
	 * and four times as many passengers. */
 
	switch (cid) {
 
		case CT_PASSENGERS:
 
			return avi->passenger_capacity;
 
		case CT_MAIL:
 
			return avi->passenger_capacity + avi->mail_capacity;
 
		case CT_GOODS:
 
			return (avi->passenger_capacity + avi->mail_capacity) / 2;
 
		default:
 
			return (avi->passenger_capacity + avi->mail_capacity) / 4;
 
	}
 
}
 

	
 
/** Build an aircraft.
 
 * @param tile tile of depot where aircraft is built
 
 * @param flags for command
 
 * @param p1 aircraft type being built (engine)
 
 * @param p2 bit 0 when set, the unitnumber will be 0, otherwise it will be a free number
 
 * return result of operation.  Could be cost, error
 
 */
 
CommandCost CmdBuildAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_player)) return_cmd_error(STR_AIRCRAFT_NOT_AVAILABLE);
 

	
 
	const AircraftVehicleInfo *avi = AircraftVehInfo(p1);
 
	CommandCost value = EstimateAircraftCost(p1, avi);
 

	
 
	/* to just query the cost, it is not neccessary to have a valid tile (automation/AI) */
 
	if (flags & DC_QUERY_COST) return value;
 

	
 
	if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_player)) return CMD_ERROR;
 

	
 
	/* Prevent building aircraft types at places which can't handle them */
 
	if (!CanAircraftUseStation(p1, tile)) return CMD_ERROR;
 

	
 
	/* Allocate 2 or 3 vehicle structs, depending on type
 
	 * vl[0] = aircraft, vl[1] = shadow, [vl[2] = rotor] */
 
	Vehicle *vl[3];
 
	if (!Vehicle::AllocateList(vl, avi->subtype & AIR_CTOL ? 2 : 3)) {
 
		return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 
	}
 

	
 
	UnitID unit_num = HasBit(p2, 0) ? 0 : GetFreeUnitNumber(VEH_AIRCRAFT);
 
	if (unit_num > _settings.vehicle.max_aircraft)
 
	if (unit_num > _settings_game.vehicle.max_aircraft)
 
		return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
	if (flags & DC_EXEC) {
 
		Vehicle *v = vl[0]; // aircraft
 
		Vehicle *u = vl[1]; // shadow
 

	
 
		v = new (v) Aircraft();
 
		u = new (u) Aircraft();
 
		v->unitnumber = unit_num;
 
		v->direction = DIR_SE;
 

	
 
		v->owner = u->owner = _current_player;
 

	
 
		v->tile = tile;
 
//		u->tile = 0;
 

	
 
		uint x = TileX(tile) * TILE_SIZE + 5;
 
		uint y = TileY(tile) * TILE_SIZE + 3;
 

	
 
		v->x_pos = u->x_pos = x;
 
		v->y_pos = u->y_pos = y;
 

	
 
		u->z_pos = GetSlopeZ(x, y);
 
		v->z_pos = u->z_pos + 1;
 

	
 
		v->running_ticks = 0;
 

	
 
//		u->delta_x = u->delta_y = 0;
 

	
 
		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
 
		u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
 

	
 
		v->spritenum = avi->image_index;
 
//		v->cargo_count = u->number_of_pieces = 0;
 

	
 
		v->cargo_cap = avi->passenger_capacity;
 
		u->cargo_cap = avi->mail_capacity;
 

	
 
		v->cargo_type = CT_PASSENGERS;
 
		u->cargo_type = CT_MAIL;
 

	
 
		v->cargo_subtype = 0;
 

	
 
		v->name = NULL;
 
//		v->next_order_param = v->next_order = 0;
 

	
 
//		v->load_unload_time_rem = 0;
 
//		v->progress = 0;
 
		v->last_station_visited = INVALID_STATION;
 
//		v->destination_coords = 0;
 

	
 
		v->max_speed = avi->max_speed;
 
		v->acceleration = avi->acceleration;
 
		v->engine_type = p1;
 

	
 
		v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
 
		v->UpdateDeltaXY(INVALID_DIR);
 
		v->value = value.GetCost();
 

	
 
		u->subtype = AIR_SHADOW;
 
		u->UpdateDeltaXY(INVALID_DIR);
 

	
 
		/* Danger, Will Robinson!
 
		 * If the aircraft is refittable, but cannot be refitted to
 
		 * passengers, we select the cargo type from the refit mask.
 
		 * This is a fairly nasty hack to get around the fact that TTD
 
		 * has no default cargo type specifier for planes... */
 
		CargoID cargo = FindFirstRefittableCargo(p1);
 
		if (cargo != CT_INVALID && cargo != CT_PASSENGERS) {
 
			uint16 callback = CALLBACK_FAILED;
 

	
 
			v->cargo_type = cargo;
 

	
 
			if (HasBit(EngInfo(p1)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
 
				callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
 
			}
 

	
 
			if (callback == CALLBACK_FAILED) {
 
				/* Callback failed, or not executed; use the default cargo capacity */
 
				v->cargo_cap = AircraftDefaultCargoCapacity(v->cargo_type, avi);
 
			} else {
 
				v->cargo_cap = callback;
 
			}
 

	
 
			/* Set the 'second compartent' capacity to none */
 
			u->cargo_cap = 0;
 
		}
 

	
 
		const Engine *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;
 

	
 
		/* When we click on hangar we know the tile it is on. By that we know
 
		 * its position in the array of depots the airport has.....we can search
 
		 * layout for #th position of depot. Since layout must start with a listing
 
		 * of all depots, it is simple */
 
		for (uint i = 0;; i++) {
 
			const Station *st = GetStationByTile(tile);
 
			const AirportFTAClass *apc = st->Airport();
 

	
 
			assert(i != apc->nof_depots);
 
			if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == tile) {
 
				assert(apc->layout[i].heading == HANGAR);
 
				v->u.air.pos = apc->layout[i].position;
 
				break;
 
			}
 
		}
 

	
 
		v->u.air.state = HANGAR;
 
		v->u.air.previous_pos = v->u.air.pos;
 
		v->u.air.targetairport = GetStationIndex(tile);
 
		v->SetNext(u);
 

	
 
		v->service_interval = _settings.vehicle.servint_aircraft;
 
		v->service_interval = _settings_game.vehicle.servint_aircraft;
 

	
 
		v->date_of_last_service = _date;
 
		v->build_year = u->build_year = _cur_year;
 

	
 
		v->cur_image = u->cur_image = 0xEA0;
 

	
 
		v->random_bits = VehicleRandomBits();
 
		u->random_bits = VehicleRandomBits();
 

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

	
 
		UpdateAircraftCache(v);
 

	
 
		VehiclePositionChanged(v);
 
		VehiclePositionChanged(u);
 

	
 
		/* Aircraft with 3 vehicles (chopper)? */
 
		if (v->subtype == AIR_HELICOPTER) {
 
			Vehicle *w = vl[2];
 

	
 
			w = new (w) Aircraft();
 
			w->direction = DIR_N;
 
			w->owner = _current_player;
 
			w->x_pos = v->x_pos;
 
			w->y_pos = v->y_pos;
 
			w->z_pos = v->z_pos + 5;
 
			w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
 
			w->spritenum = 0xFF;
 
			w->subtype = AIR_ROTOR;
 
			w->cur_image = SPR_ROTOR_STOPPED;
 
			w->random_bits = VehicleRandomBits();
 
			/* Use rotor's air.state to store the rotor animation frame */
 
			w->u.air.state = HRS_ROTOR_STOPPED;
 
			w->UpdateDeltaXY(INVALID_DIR);
 

	
 
			u->SetNext(w);
 
			VehiclePositionChanged(w);
 
		}
 

	
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
		InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
 
		InvalidateWindow(WC_COMPANY, v->owner);
 
		if (IsLocalPlayer())
 
			InvalidateAutoreplaceWindow(v->engine_type, v->group_id); //updates the replace Aircraft window
 

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

	
 
	return value;
 
}
 

	
 

	
 
static void DoDeleteAircraft(Vehicle *v)
 
{
 
	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
	InvalidateWindow(WC_COMPANY, v->owner);
 
	DeleteDepotHighlightOfVehicle(v);
 
	DeleteVehicleChain(v);
 
	InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
 
}
 

	
 
/** Sell an aircraft.
 
 * @param tile unused
 
 * @param flags for command type
 
 * @param p1 vehicle ID to be sold
 
 * @param p2 unused
 
 * @return result of operation.  Error or sold value
 
 */
 
CommandCost CmdSellAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	Vehicle *v = GetVehicle(p1);
 

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

	
 
	if (HASBITS(v->vehstatus, VS_CRASHED)) return_cmd_error(STR_CAN_T_SELL_DESTROYED_VEHICLE);
 

	
 
	CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
 

	
 
	if (flags & DC_EXEC) {
 
		// Invalidate depot
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		DoDeleteAircraft(v);
 
	}
 

	
 
	return ret;
 
}
 

	
 
/** Start/Stop an aircraft.
 
 * @param tile unused
 
 * @param flags for command type
 
 * @param p1 aircraft ID to start/stop
 
 * @param p2 unused
 
@@ -571,193 +571,193 @@ CommandCost CmdSendAircraftToHangar(Tile
 
	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;
 

	
 
	Vehicle *v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_AIRCRAFT) return CMD_ERROR;
 

	
 
	return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));
 
}
 

	
 

	
 
/** Refits an aircraft to the specified cargo type.
 
 * @param tile unused
 
 * @param flags for command type
 
 * @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
 
 * - p2 = (bit 16) - refit only this vehicle (ignored)
 
 * @return cost of refit or error
 
 */
 
CommandCost CmdRefitAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	byte new_subtype = GB(p2, 8, 8);
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	Vehicle *v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_AIRCRAFT || !CheckOwnership(v->owner)) return CMD_ERROR;
 
	if (!v->IsStoppedInDepot()) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
 
	if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_CAN_T_REFIT_DESTROYED_VEHICLE);
 

	
 
	/* Check cargo */
 
	CargoID new_cid = GB(p2, 0, 8);
 
	if (new_cid >= NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
 

	
 
	/* Check the refit capacity callback */
 
	uint16 callback = CALLBACK_FAILED;
 
	if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_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;
 
	}
 

	
 
	const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
 

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

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

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

	
 
		Vehicle *u = v->Next();
 
		uint mail = IsCargoInClass(new_cid, CC_PASSENGERS) ? avi->mail_capacity : 0;
 
		u->cargo_cap = mail;
 
		v->cargo.Truncate(v->cargo_type == new_cid ? pass : 0);
 
		u->cargo.Truncate(v->cargo_type == new_cid ? mail : 0);
 
		v->cargo_type = new_cid;
 
		v->cargo_subtype = new_subtype;
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
 
	}
 

	
 
	return cost;
 
}
 

	
 

	
 
static void CheckIfAircraftNeedsService(Vehicle *v)
 
{
 
	if (_settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
 
	if (_settings_game.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
 
	if (v->IsInDepot()) {
 
		VehicleServiceInDepot(v);
 
		return;
 
	}
 

	
 
	const Station *st = GetStation(v->current_order.GetDestination());
 
	/* only goto depot if the target airport has terminals (eg. it is airport) */
 
	if (st->IsValid() && st->airport_tile != 0 && st->Airport()->terminals != NULL) {
 
//		printf("targetairport = %d, st->index = %d\n", v->u.air.targetairport, st->index);
 
//		v->u.air.targetairport = st->index;
 
		v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	} else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
 
		v->current_order.MakeDummy();
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	}
 
}
 

	
 
void Aircraft::OnNewDay()
 
{
 
	if (!IsNormalAircraft(this)) return;
 

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

	
 
	CheckOrders(this);
 

	
 
	CheckVehicleBreakdown(this);
 
	AgeVehicle(this);
 
	CheckIfAircraftNeedsService(this);
 

	
 
	if (this->running_ticks == 0) return;
 

	
 
	CommandCost cost(EXPENSES_AIRCRAFT_RUN, GetVehicleProperty(this, 0x0E, AircraftVehInfo(this->engine_type)->running_cost) * _price.aircraft_running * this->running_ticks / (364 * DAY_TICKS));
 

	
 
	this->profit_this_year -= cost.GetCost();
 
	this->running_ticks = 0;
 

	
 
	SubtractMoneyFromPlayerFract(this->owner, cost);
 

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

	
 
void AircraftYearlyLoop()
 
{
 
	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 {
 
		v->cargo.AgeCargo();
 
		v = v->Next();
 
	} while (v != NULL);
 
}
 

	
 
static void HelicopterTickHandler(Vehicle *v)
 
{
 
	Vehicle *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.IsType(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--;
 
	}
 

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

	
 
	SpriteID img;
 
	if (spd == 0) {
 
		u->u.air.state = HRS_ROTOR_STOPPED;
 
		img = GetRotorImage(v);
 
@@ -791,227 +791,227 @@ static void SetAircraftPosition(Vehicle 
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 

	
 
	Vehicle *u = v->Next();
 

	
 
	int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
 
	int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
 
	u->x_pos = x;
 
	u->y_pos = y - ((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)
 
{
 
	v->subspeed = 0;
 
	v->progress = 0;
 

	
 
	Vehicle *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);
 
	}
 
}
 

	
 

	
 
void UpdateAircraftCache(Vehicle *v)
 
{
 
	uint max_speed = GetVehicleProperty(v, 0x0C, 0);
 
	if (max_speed != 0) {
 
		/* Convert from original units to (approx) km/h */
 
		max_speed = (max_speed * 129) / 10;
 

	
 
		v->u.air.cached_max_speed = max_speed;
 
	} else {
 
		v->u.air.cached_max_speed = 0xFFFF;
 
	}
 
}
 

	
 

	
 
/**
 
 * Special velocities for aircraft
 
 */
 
enum AircraftSpeedLimits {
 
	SPEED_LIMIT_TAXI     =     50,  ///< Maximum speed of an aircraft while taxiing
 
	SPEED_LIMIT_APPROACH =    230,  ///< Maximum speed of an aircraft on finals
 
	SPEED_LIMIT_BROKEN   =    320,  ///< Maximum speed of an aircraft that is broken
 
	SPEED_LIMIT_HOLD     =    425,  ///< Maximum speed of an aircraft that flies the holding pattern
 
	SPEED_LIMIT_NONE     = 0xFFFF   ///< No environmental speed limit. Speed limit is type dependent
 
};
 

	
 
/**
 
 * Sets the new speed for an aircraft
 
 * @param v The vehicle for which the speed should be obtained
 
 * @param speed_limit The maximum speed the vehicle may have.
 
 * @param hard_limit If true, the limit is directly enforced, otherwise the plane is slowed down gradually
 
 * @return The number of position updates needed within the tick
 
 */
 
static int UpdateAircraftSpeed(Vehicle *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
 
{
 
	uint spd = v->acceleration * 16;
 
	byte t;
 

	
 
	/* Adjust speed limits by plane speed factor to prevent taxiing
 
	 * and take-off speeds being too low. */
 
	speed_limit *= _settings.vehicle.plane_speed;
 
	speed_limit *= _settings_game.vehicle.plane_speed;
 

	
 
	if (v->u.air.cached_max_speed < speed_limit) {
 
		if (v->cur_speed < speed_limit) hard_limit = false;
 
		speed_limit = v->u.air.cached_max_speed;
 
	}
 

	
 
	speed_limit = min(speed_limit, v->max_speed);
 

	
 
	v->subspeed = (t=v->subspeed) + (byte)spd;
 

	
 
	/* Aircraft's current speed is used twice so that very fast planes are
 
	 * forced to slow down rapidly in the short distance needed. The magic
 
	 * value 16384 was determined to give similar results to the old speed/48
 
	 * method at slower speeds. This also results in less reduction at slow
 
	 * speeds to that aircraft do not get to taxi speed straight after
 
	 * touchdown. */
 
	if (!hard_limit && v->cur_speed > speed_limit) {
 
		speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings.vehicle.plane_speed);
 
		speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
 
	}
 

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

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

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

	
 
	/* Adjust distance moved by plane speed setting */
 
	if (_settings.vehicle.plane_speed > 1) spd /= _settings.vehicle.plane_speed;
 
	if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
 

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

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

	
 
/**
 
 * Gets the cruise altitude of an aircraft.
 
 * The cruise altitude is determined by the velocity of the vehicle
 
 * and the direction it is moving
 
 * @param v The vehicle. Should be an aircraft
 
 * @returns Altitude in pixel units
 
 */
 
static byte GetAircraftFlyingAltitude(const Vehicle *v)
 
{
 
	/* Make sure Aircraft fly no lower so that they don't conduct
 
	 * CFITs (controlled flight into terrain)
 
	 */
 
	byte base_altitude = 150;
 

	
 
	/* Make sure eastbound and westbound planes do not "crash" into each
 
	 * other by providing them with vertical seperation
 
	 */
 
	switch (v->direction) {
 
		case DIR_N:
 
		case DIR_NE:
 
		case DIR_E:
 
		case DIR_SE:
 
			base_altitude += 10;
 
			break;
 

	
 
		default: break;
 
	}
 

	
 
	/* Make faster planes fly higher so that they can overtake slower ones */
 
	base_altitude += min(20 * (v->max_speed / 200), 90);
 

	
 
	return base_altitude;
 
}
 

	
 
/**
 
 * Find the entry point to an airport depending on direction which
 
 * the airport is being approached from. Each airport can have up to
 
 * four entry points for its approach system so that approaching
 
 * aircraft do not fly through each other or are forced to do 180
 
 * degree turns during the approach. The arrivals are grouped into
 
 * four sectors dependent on the DiagDirection from which the airport
 
 * is approached.
 
 *
 
 * @param v   The vehicle that is approaching the airport
 
 * @param apc The Airport Class being approached.
 
 * @returns   The index of the entry point
 
 */
 
static byte AircraftGetEntryPoint(const Vehicle *v, const AirportFTAClass *apc)
 
{
 
	assert(v != NULL);
 
	assert(apc != NULL);
 

	
 
	const Station *st = GetStation(v->u.air.targetairport);
 
	/* Make sure we don't go to 0,0 if the airport has been removed. */
 
	TileIndex tile = (st->airport_tile != 0) ? st->airport_tile : st->xy;
 

	
 
	int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
 
	int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
 

	
 
	DiagDirection dir;
 
	if (abs(delta_y) < abs(delta_x)) {
 
		/* We are northeast or southwest of the airport */
 
		dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
 
	} else {
 
		/* We are northwest or southeast of the airport */
 
		dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
 
	}
 
	return apc->entry_points[dir];
 
}
 

	
 
/**
 
 * Controls the movement of an aircraft. This function actually moves the vehicle
 
 * on the map and takes care of minor things like sound playback.
 
 * @todo    De-mystify the cur_speed values for helicopter rotors.
 
 * @param v The vehicle that is moved. Must be the first vehicle of the chain
 
 * @return  Whether the position requested by the State Machine has been reached
 
 */
 
static bool AircraftController(Vehicle *v)
 
{
 
	int count;
 
	const Station *st = GetStation(v->u.air.targetairport);
 
	const AirportFTAClass *afc = st->Airport();
 
	const AirportMovingData *amd;
 

	
 
	/* prevent going to 0,0 if airport is deleted. */
 
	TileIndex tile = st->airport_tile;
 
	if (tile == 0) {
 
		tile = st->xy;
 
@@ -1506,193 +1506,193 @@ static inline bool CheckSendAircraftToHa
 
	EngineID new_engine;
 
	Player *p = GetPlayer(v->owner);
 

	
 
	if (VehicleHasDepotOrders(v)) return false; // The aircraft will end up in the hangar eventually on it's own
 

	
 
	new_engine = EngineReplacementForPlayer(p, v->engine_type, v->group_id);
 

	
 
	if (new_engine == INVALID_ENGINE) {
 
		/* There is no autoreplace assigned to this EngineID so we will set it to renew to the same type if needed */
 
		new_engine = v->engine_type;
 

	
 
		if (!v->NeedsAutorenewing(p)) {
 
			/* No need to replace the aircraft */
 
			return false;
 
		}
 
	}
 

	
 
	if (!HasBit(GetEngine(new_engine)->player_avail, v->owner)) {
 
		/* Engine is not buildable anymore */
 
		return false;
 
	}
 

	
 
	if (p->player_money < (p->engine_renew_money + (2 * DoCommand(0, new_engine, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT).GetCost()))) {
 
		/* We lack enough money to request the replacement right away.
 
		 * We want 2*(the price of the new vehicle) and not looking at the value of the vehicle we are going to sell.
 
		 * The reason is that we don't want to send a whole lot of vehicles to the hangars when we only have enough money to replace a single one.
 
		 * Remember this happens in the background so the user can't stop this. */
 
		return false;
 
	}
 

	
 
	/* We found no reason NOT to send the aircraft to a hangar so we will send it there at once */
 
	return true;
 
}
 

	
 
////////////////////////////////////////////////////////////////////////////////
 
///////////////////   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.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
 
		v->current_order.Free();
 
		return;
 
	}
 

	
 
	if (!v->current_order.IsType(OT_GOTO_STATION) &&
 
			!v->current_order.IsType(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.GetDestination() == 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 (_settings.order.serviceathelipad) {
 
		if (_settings_game.order.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.IsValid()) 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.GetType()) {
 
		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.GetDestination() == 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.Free();
 
			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->u.air.state = ENDTAKEOFF;
 
	v->UpdateDeltaXY(INVALID_DIR);
 
}
 

	
 
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)
 
{
 
	v->u.air.state = FLYING;
 
	v->UpdateDeltaXY(INVALID_DIR);
 

	
 
	/* get the next position to go to, differs per airport */
 
	AircraftNextAirportPos_and_Order(v);
 

	
 
	/* Send the helicopter to a hangar if needed for replacement */
 
	if (CheckSendAircraftToHangarForReplacement(v)) {
 
		_current_player = v->owner;
 
		DoCommand(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
 
		_current_player = OWNER_NONE;
 
	}
 
}
 

	
 
static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *apc)
 
{
 
	Station *st = GetStation(v->u.air.targetairport);
 

	
 
	/* runway busy or not allowed to use this airstation, circle */
 
	if (apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES) &&
 
			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
 
		byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
 
		const AirportFTA *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 */