src/aircraft_cmd.cpp
branchcustombridgeheads
changeset 5649 55c8267c933f
parent 5643 3778051e8095
child 5650 aefc131bf5ce
equal deleted inserted replaced
5648:1608018c5ff2 5649:55c8267c933f
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "aircraft.h"
       
     6 #include "debug.h"
       
     7 #include "functions.h"
       
     8 #include "station_map.h"
       
     9 #include "table/strings.h"
       
    10 #include "map.h"
       
    11 #include "tile.h"
       
    12 #include "vehicle.h"
       
    13 #include "depot.h"
       
    14 #include "engine.h"
       
    15 #include "command.h"
       
    16 #include "station.h"
       
    17 #include "news.h"
       
    18 #include "sound.h"
       
    19 #include "player.h"
       
    20 #include "airport.h"
       
    21 #include "vehicle_gui.h"
       
    22 #include "table/sprites.h"
       
    23 #include "newgrf_engine.h"
       
    24 #include "newgrf_callbacks.h"
       
    25 #include "newgrf_text.h"
       
    26 #include "newgrf_sound.h"
       
    27 #include "date.h"
       
    28 
       
    29 static bool AirportMove(Vehicle *v, const AirportFTAClass *apc);
       
    30 static bool AirportSetBlocks(Vehicle *v, AirportFTA *current_pos, const AirportFTAClass *apc);
       
    31 static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
       
    32 static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc);
       
    33 static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc);
       
    34 static void AirportGoToNextPosition(Vehicle *v, const AirportFTAClass *apc);
       
    35 static void CrashAirplane(Vehicle *v);
       
    36 
       
    37 static void AircraftNextAirportPos_and_Order(Vehicle *v);
       
    38 static byte GetAircraftFlyingAltitude(const Vehicle *v);
       
    39 
       
    40 static const SpriteID _aircraft_sprite[] = {
       
    41 	0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD,
       
    42 	0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5,
       
    43 	0x0EAD, 0x0EE5, 0x0F05, 0x0F0D,
       
    44 	0x0F15, 0x0F1D, 0x0F25, 0x0F2D,
       
    45 	0x0EED, 0x0EF5, 0x0EFD, 0x0F35,
       
    46 	0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5,
       
    47 	0x0EBD, 0x0EC5
       
    48 };
       
    49 
       
    50 /* Helicopter rotor animation states */
       
    51 enum HelicopterRotorStates {
       
    52 	HRS_ROTOR_STOPPED,
       
    53 	HRS_ROTOR_MOVING_1,
       
    54 	HRS_ROTOR_MOVING_2,
       
    55 	HRS_ROTOR_MOVING_3,
       
    56 };
       
    57 
       
    58 /* Find the nearest hangar to v
       
    59  * INVALID_STATION is returned, if the player does not have any suitable
       
    60  * airports (like helipads only)
       
    61  */
       
    62 static StationID FindNearestHangar(const Vehicle *v)
       
    63 {
       
    64 	const Station *st;
       
    65 	uint best = 0;
       
    66 	StationID index = INVALID_STATION;
       
    67 	TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
       
    68 
       
    69 	FOR_ALL_STATIONS(st) {
       
    70 		if (st->owner == v->owner && st->facilities & FACIL_AIRPORT &&
       
    71 				GetAirport(st->airport_type)->nof_depots > 0) {
       
    72 			uint distance;
       
    73 
       
    74 			// don't crash the plane if we know it can't land at the airport
       
    75 			if ((AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
       
    76 					(st->airport_type == AT_SMALL || st->airport_type == AT_COMMUTER) &&
       
    77 					!_cheats.no_jetcrash.value)
       
    78 				continue;
       
    79 
       
    80 			// v->tile can't be used here, when aircraft is flying v->tile is set to 0
       
    81 			distance = DistanceSquare(vtile, st->airport_tile);
       
    82 			if (distance < best || index == INVALID_STATION) {
       
    83 				best = distance;
       
    84 				index = st->index;
       
    85 			}
       
    86 		}
       
    87 	}
       
    88 	return index;
       
    89 }
       
    90 
       
    91 #if 0
       
    92 // returns true if vehicle v have an airport in the schedule, that has a hangar
       
    93 static bool HaveHangarInOrderList(Vehicle *v)
       
    94 {
       
    95 	const Order *order;
       
    96 
       
    97 	FOR_VEHICLE_ORDERS(v, order) {
       
    98 		const Station *st = GetStation(order->station);
       
    99 		if (st->owner == v->owner && st->facilities & FACIL_AIRPORT) {
       
   100 			// If an airport doesn't have a hangar, skip it
       
   101 			if (GetAirport(st->airport_type)->nof_depots != 0)
       
   102 				return true;
       
   103 		}
       
   104 	}
       
   105 
       
   106 	return false;
       
   107 }
       
   108 #endif
       
   109 
       
   110 int GetAircraftImage(const Vehicle* v, Direction direction)
       
   111 {
       
   112 	int spritenum = v->spritenum;
       
   113 
       
   114 	if (is_custom_sprite(spritenum)) {
       
   115 		int sprite = GetCustomVehicleSprite(v, direction);
       
   116 
       
   117 		if (sprite != 0) return sprite;
       
   118 		spritenum = orig_aircraft_vehicle_info[v->engine_type - AIRCRAFT_ENGINES_INDEX].image_index;
       
   119 	}
       
   120 	return direction + _aircraft_sprite[spritenum];
       
   121 }
       
   122 
       
   123 SpriteID GetRotorImage(const Vehicle *v)
       
   124 {
       
   125 	const Vehicle *w;
       
   126 
       
   127 	assert((v->subtype & 1) == 0);
       
   128 
       
   129 	w = v->next->next;
       
   130 	if (is_custom_sprite(v->spritenum)) {
       
   131 		SpriteID spritenum = GetCustomRotorSprite(v, false);
       
   132 		if (spritenum != 0) return spritenum;
       
   133 	}
       
   134 
       
   135 	/* Return standard rotor sprites if there are no custom sprites for this helicopter */
       
   136 	return SPR_ROTOR_STOPPED + w->u.air.state;
       
   137 }
       
   138 
       
   139 void DrawAircraftEngine(int x, int y, EngineID engine, uint32 image_ormod)
       
   140 {
       
   141 	const AircraftVehicleInfo* avi = AircraftVehInfo(engine);
       
   142 	int spritenum = avi->image_index;
       
   143 	int sprite = (6 + _aircraft_sprite[spritenum]);
       
   144 
       
   145 	if (is_custom_sprite(spritenum)) {
       
   146 		sprite = GetCustomVehicleIcon(engine, DIR_W);
       
   147 		if (sprite == 0) {
       
   148 			spritenum = orig_aircraft_vehicle_info[engine - AIRCRAFT_ENGINES_INDEX].image_index;
       
   149 			sprite = (6 + _aircraft_sprite[spritenum]);
       
   150 		}
       
   151 	}
       
   152 
       
   153 	DrawSprite(sprite | image_ormod, x, y);
       
   154 
       
   155 	if (!(avi->subtype & AIR_CTOL)) {
       
   156 		SpriteID rotor_sprite = GetCustomRotorIcon(engine);
       
   157 		if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
       
   158 		DrawSprite(rotor_sprite, x, y - 5);
       
   159 	}
       
   160 }
       
   161 
       
   162 static int32 EstimateAircraftCost(EngineID engine_type)
       
   163 {
       
   164 	return AircraftVehInfo(engine_type)->base_cost * (_price.aircraft_base>>3)>>5;
       
   165 }
       
   166 
       
   167 
       
   168 /**
       
   169  * Calculates cargo capacity based on an aircraft's passenger
       
   170  * and mail capacities.
       
   171  * @param cid Which cargo type to calculate a capacity for.
       
   172  * @param engine Which engine to find a cargo capacity for.
       
   173  * @return New cargo capacity value.
       
   174  */
       
   175 uint16 AircraftDefaultCargoCapacity(CargoID cid, EngineID engine_type)
       
   176 {
       
   177 	const AircraftVehicleInfo *avi = AircraftVehInfo(engine_type);
       
   178 
       
   179 	assert(cid != CT_INVALID);
       
   180 
       
   181 	/* An aircraft can carry twice as much goods as normal cargo,
       
   182 	 * and four times as many passengers. */
       
   183 	switch (cid) {
       
   184 		case CT_PASSENGERS:
       
   185 			return avi->passenger_capacity;
       
   186 		case CT_MAIL:
       
   187 			return avi->passenger_capacity + avi->mail_capacity;
       
   188 		case CT_GOODS:
       
   189 			return (avi->passenger_capacity + avi->mail_capacity) / 2;
       
   190 		default:
       
   191 			return (avi->passenger_capacity + avi->mail_capacity) / 4;
       
   192 	}
       
   193 }
       
   194 
       
   195 
       
   196 /** Build an aircraft.
       
   197  * @param tile tile of depot where aircraft is built
       
   198  * @param p1 aircraft type being built (engine)
       
   199  * @param p2 bit 0 when set, the unitnumber will be 0, otherwise it will be a free number
       
   200  */
       
   201 int32 CmdBuildAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   202 {
       
   203 	int32 value;
       
   204 	Vehicle *vl[3], *v, *u, *w;
       
   205 	UnitID unit_num;
       
   206 	const AircraftVehicleInfo *avi;
       
   207 	const AirportFTAClass* ap;
       
   208 	Engine *e;
       
   209 
       
   210 	if (!IsEngineBuildable(p1, VEH_Aircraft, _current_player)) return_cmd_error(STR_ENGINE_NOT_BUILDABLE);
       
   211 
       
   212 	value = EstimateAircraftCost(p1);
       
   213 
       
   214 	// to just query the cost, it is not neccessary to have a valid tile (automation/AI)
       
   215 	if (flags & DC_QUERY_COST) return value;
       
   216 
       
   217 	if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_player)) return CMD_ERROR;
       
   218 
       
   219 	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
       
   220 
       
   221 	avi = AircraftVehInfo(p1);
       
   222 
       
   223 	// Prevent building aircraft types at places which can't handle them
       
   224 	ap = GetAirport(GetStationByTile(tile)->airport_type);
       
   225 	if ((avi->subtype & AIR_CTOL ? HELICOPTERS_ONLY : AIRCRAFT_ONLY) == ap->acc_planes) {
       
   226 		return CMD_ERROR;
       
   227 	}
       
   228 
       
   229 	// allocate 2 or 3 vehicle structs, depending on type
       
   230 	if (!AllocateVehicles(vl, avi->subtype & AIR_CTOL ? 2 : 3) ||
       
   231 			IsOrderPoolFull()) {
       
   232 		return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
       
   233 	}
       
   234 
       
   235 	unit_num = HASBIT(p2, 0) ? 0 : GetFreeUnitNumber(VEH_Aircraft);
       
   236 	if (unit_num > _patches.max_aircraft)
       
   237 		return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
       
   238 
       
   239 	if (flags & DC_EXEC) {
       
   240 		CargoID cargo;
       
   241 		uint x;
       
   242 		uint y;
       
   243 
       
   244 		v = vl[0];
       
   245 		u = vl[1];
       
   246 
       
   247 		v->unitnumber = unit_num;
       
   248 		v->type = u->type = VEH_Aircraft;
       
   249 		v->direction = 3;
       
   250 
       
   251 		v->owner = u->owner = _current_player;
       
   252 
       
   253 		v->tile = tile;
       
   254 //		u->tile = 0;
       
   255 
       
   256 		x = TileX(tile) * TILE_SIZE + 5;
       
   257 		y = TileY(tile) * TILE_SIZE + 3;
       
   258 
       
   259 		v->x_pos = u->x_pos = x;
       
   260 		v->y_pos = u->y_pos = y;
       
   261 
       
   262 		u->z_pos = GetSlopeZ(x, y);
       
   263 		v->z_pos = u->z_pos + 1;
       
   264 
       
   265 		v->x_offs = v->y_offs = -1;
       
   266 //		u->delta_x = u->delta_y = 0;
       
   267 
       
   268 		v->sprite_width = v->sprite_height = 2;
       
   269 		v->z_height = 5;
       
   270 
       
   271 		u->sprite_width = u->sprite_height = 2;
       
   272 		u->z_height = 1;
       
   273 
       
   274 		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
       
   275 		u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
       
   276 
       
   277 		v->spritenum = avi->image_index;
       
   278 //		v->cargo_count = u->number_of_pieces = 0;
       
   279 
       
   280 		v->cargo_cap = avi->passenger_capacity;
       
   281 		u->cargo_cap = avi->mail_capacity;
       
   282 
       
   283 		v->cargo_type = CT_PASSENGERS;
       
   284 		u->cargo_type = CT_MAIL;
       
   285 
       
   286 		v->cargo_subtype = 0;
       
   287 
       
   288 		v->string_id = STR_SV_AIRCRAFT_NAME;
       
   289 //		v->next_order_param = v->next_order = 0;
       
   290 
       
   291 //		v->load_unload_time_rem = 0;
       
   292 //		v->progress = 0;
       
   293 		v->last_station_visited = INVALID_STATION;
       
   294 //		v->destination_coords = 0;
       
   295 
       
   296 		v->max_speed = avi->max_speed;
       
   297 		v->acceleration = avi->acceleration;
       
   298 		v->engine_type = p1;
       
   299 
       
   300 		v->subtype = (avi->subtype & AIR_CTOL ? 2 : 0);
       
   301 		v->value = value;
       
   302 
       
   303 		u->subtype = 4;
       
   304 
       
   305 		/* Danger, Will Robinson!
       
   306 		 * If the aircraft is refittable, but cannot be refitted to
       
   307 		 * passengers, we select the cargo type from the refit mask.
       
   308 		 * This is a fairly nasty hack to get around the fact that TTD
       
   309 		 * has no default cargo type specifier for planes... */
       
   310 		cargo = FindFirstRefittableCargo(p1);
       
   311 		if (cargo != CT_INVALID && cargo != CT_PASSENGERS) {
       
   312 			uint16 callback = CALLBACK_FAILED;
       
   313 
       
   314 			v->cargo_type = cargo;
       
   315 
       
   316 			if (HASBIT(EngInfo(p1)->callbackmask, CBM_REFIT_CAPACITY)) {
       
   317 				callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
       
   318 			}
       
   319 
       
   320 			if (callback == CALLBACK_FAILED) {
       
   321 				/* Callback failed, or not executed; use the default cargo capacity */
       
   322 				v->cargo_cap = AircraftDefaultCargoCapacity(v->cargo_type, v->engine_type);
       
   323 			} else {
       
   324 				v->cargo_cap = callback;
       
   325 			}
       
   326 
       
   327 			/* Set the 'second compartent' capacity to none */
       
   328 			u->cargo_cap = 0;
       
   329 		}
       
   330 
       
   331 		e = GetEngine(p1);
       
   332 		v->reliability = e->reliability;
       
   333 		v->reliability_spd_dec = e->reliability_spd_dec;
       
   334 		v->max_age = e->lifelength * 366;
       
   335 
       
   336 		_new_vehicle_id = v->index;
       
   337 
       
   338 		v->u.air.pos = MAX_ELEMENTS;
       
   339 
       
   340 		/* When we click on hangar we know the tile it is on. By that we know
       
   341 		 * its position in the array of depots the airport has.....we can search
       
   342 		 * layout for #th position of depot. Since layout must start with a listing
       
   343 		 * of all depots, it is simple */
       
   344 		{
       
   345 			const Station* st = GetStationByTile(tile);
       
   346 			const AirportFTAClass* apc = GetAirport(st->airport_type);
       
   347 			uint i;
       
   348 
       
   349 			for (i = 0; i < apc->nof_depots; i++) {
       
   350 				if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == tile) {
       
   351 					assert(apc->layout[i].heading == HANGAR);
       
   352 					v->u.air.pos = apc->layout[i].position;
       
   353 					break;
       
   354 				}
       
   355 			}
       
   356 			// to ensure v->u.air.pos has been given a value
       
   357 			assert(v->u.air.pos != MAX_ELEMENTS);
       
   358 		}
       
   359 
       
   360 		v->u.air.state = HANGAR;
       
   361 		v->u.air.previous_pos = v->u.air.pos;
       
   362 		v->u.air.targetairport = GetStationIndex(tile);
       
   363 		v->next = u;
       
   364 
       
   365 		v->service_interval = _patches.servint_aircraft;
       
   366 
       
   367 		v->date_of_last_service = _date;
       
   368 		v->build_year = u->build_year = _cur_year;
       
   369 
       
   370 		v->cur_image = u->cur_image = 0xEA0;
       
   371 
       
   372 		v->random_bits = VehicleRandomBits();
       
   373 		u->random_bits = VehicleRandomBits();
       
   374 
       
   375 		VehiclePositionChanged(v);
       
   376 		VehiclePositionChanged(u);
       
   377 
       
   378 		// Aircraft with 3 vehicles (chopper)?
       
   379 		if (v->subtype == 0) {
       
   380 			w = vl[2];
       
   381 
       
   382 			u->next = w;
       
   383 
       
   384 			w->type = VEH_Aircraft;
       
   385 			w->direction = 0;
       
   386 			w->owner = _current_player;
       
   387 			w->x_pos = v->x_pos;
       
   388 			w->y_pos = v->y_pos;
       
   389 			w->z_pos = v->z_pos + 5;
       
   390 			w->x_offs = w->y_offs = -1;
       
   391 			w->sprite_width = w->sprite_height = 2;
       
   392 			w->z_height = 1;
       
   393 			w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
       
   394 			w->spritenum = 0xFF;
       
   395 			w->subtype = 6;
       
   396 			w->cur_image = SPR_ROTOR_STOPPED;
       
   397 			w->random_bits = VehicleRandomBits();
       
   398 			/* Use rotor's air.state to store the rotor animation frame */
       
   399 			w->u.air.state = HRS_ROTOR_STOPPED;
       
   400 			VehiclePositionChanged(w);
       
   401 		}
       
   402 		GetPlayer(_current_player)->num_engines[p1]++;
       
   403 
       
   404 		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
       
   405 		RebuildVehicleLists();
       
   406 		InvalidateWindow(WC_COMPANY, v->owner);
       
   407 		if (IsLocalPlayer())
       
   408 			InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Aircraft); //updates the replace Aircraft window
       
   409 	}
       
   410 
       
   411 	return value;
       
   412 }
       
   413 
       
   414 
       
   415 static void DoDeleteAircraft(Vehicle *v)
       
   416 {
       
   417 	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
       
   418 	RebuildVehicleLists();
       
   419 	InvalidateWindow(WC_COMPANY, v->owner);
       
   420 	DeleteDepotHighlightOfVehicle(v);
       
   421 	DeleteVehicleChain(v);
       
   422 	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
       
   423 }
       
   424 
       
   425 /** Sell an aircraft.
       
   426  * @param tile unused
       
   427  * @param p1 vehicle ID to be sold
       
   428  * @param p2 unused
       
   429  */
       
   430 int32 CmdSellAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   431 {
       
   432 	Vehicle *v;
       
   433 
       
   434 	if (!IsValidVehicleID(p1)) return CMD_ERROR;
       
   435 
       
   436 	v = GetVehicle(p1);
       
   437 
       
   438 	if (v->type != VEH_Aircraft || !CheckOwnership(v->owner)) return CMD_ERROR;
       
   439 	if (!IsAircraftInHangarStopped(v)) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
       
   440 
       
   441 	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
       
   442 
       
   443 	if (flags & DC_EXEC) {
       
   444 		// Invalidate depot
       
   445 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
       
   446 		DoDeleteAircraft(v);
       
   447 		if (IsLocalPlayer())
       
   448 			InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Aircraft); // updates the replace Aircraft window
       
   449 	}
       
   450 
       
   451 	return -(int32)v->value;
       
   452 }
       
   453 
       
   454 /** Start/Stop an aircraft.
       
   455  * @param tile unused
       
   456  * @param p1 aircraft ID to start/stop
       
   457  * @param p2 unused
       
   458  */
       
   459 int32 CmdStartStopAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   460 {
       
   461 	Vehicle *v;
       
   462 	uint16 callback;
       
   463 
       
   464 	if (!IsValidVehicleID(p1)) return CMD_ERROR;
       
   465 
       
   466 	v = GetVehicle(p1);
       
   467 
       
   468 	if (v->type != VEH_Aircraft || !CheckOwnership(v->owner)) return CMD_ERROR;
       
   469 
       
   470 	// cannot stop airplane when in flight, or when taking off / landing
       
   471 	if (v->u.air.state >= STARTTAKEOFF && v->u.air.state < TERM7)
       
   472 		return_cmd_error(STR_A017_AIRCRAFT_IS_IN_FLIGHT);
       
   473 
       
   474 	/* Check if this aircraft can be started/stopped. The callback will fail or
       
   475 	 * return 0xFF if it can. */
       
   476 	callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
       
   477 	if (callback != CALLBACK_FAILED && callback != 0xFF) {
       
   478 		StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
       
   479 		return_cmd_error(error);
       
   480 	}
       
   481 
       
   482 	if (flags & DC_EXEC) {
       
   483 		if (IsAircraftInHangarStopped(v)) {
       
   484 			DeleteVehicleNews(p1, STR_A014_AIRCRAFT_IS_WAITING_IN);
       
   485 		}
       
   486 
       
   487 		v->vehstatus ^= VS_STOPPED;
       
   488 		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
       
   489 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
       
   490 		InvalidateWindowClasses(WC_AIRCRAFT_LIST);
       
   491 	}
       
   492 
       
   493 	return 0;
       
   494 }
       
   495 
       
   496 /** Send an aircraft to the hangar.
       
   497  * @param tile unused
       
   498  * @param p1 vehicle ID to send to the hangar
       
   499  * @param p2 various bitmasked elements
       
   500  * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h)
       
   501  * - p2 bit 8-10 - VLW flag (for mass goto depot)
       
   502  */
       
   503 int32 CmdSendAircraftToHangar(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   504 {
       
   505 	Vehicle *v;
       
   506 
       
   507 	if (p2 & DEPOT_MASS_SEND) {
       
   508 		/* Mass goto depot requested */
       
   509 		if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
       
   510 		return SendAllVehiclesToDepot(VEH_Aircraft, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1);
       
   511 	}
       
   512 
       
   513 	if (!IsValidVehicleID(p1)) return CMD_ERROR;
       
   514 
       
   515 	v = GetVehicle(p1);
       
   516 
       
   517 	if (v->type != VEH_Aircraft || !CheckOwnership(v->owner) || IsAircraftInHangar(v)) return CMD_ERROR;
       
   518 
       
   519 	if (v->current_order.type == OT_GOTO_DEPOT && !(p2 & DEPOT_LOCATE_HANGAR)) {
       
   520 		if (!!(p2 & DEPOT_SERVICE) == HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT)) {
       
   521 			/* We called with a different DEPOT_SERVICE setting.
       
   522 			 * Now we change the setting to apply the new one and let the vehicle head for the same hangar.
       
   523 			 * Note: the if is (true for requesting service == true for ordered to stop in hangar) */
       
   524 			if (flags & DC_EXEC) {
       
   525 				TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
       
   526 				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
       
   527 			}
       
   528 			return 0;
       
   529 		}
       
   530 
       
   531 		if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of hangar orders
       
   532 		if (flags & DC_EXEC) {
       
   533 			if (v->current_order.flags & OF_UNLOAD) v->cur_order_index++;
       
   534 			v->current_order.type = OT_DUMMY;
       
   535 			v->current_order.flags = 0;
       
   536 			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
       
   537 		}
       
   538 	} else {
       
   539 		bool next_airport_has_hangar = true;
       
   540 		StationID next_airport_index = v->u.air.targetairport;
       
   541 		const Station *st = GetStation(next_airport_index);
       
   542 		/* If the station is not a valid airport or if it has no hangars */
       
   543 		if (!IsValidStation(st) || st->airport_tile == 0 || GetAirport(st->airport_type)->nof_depots == 0) {
       
   544 			StationID station;
       
   545 
       
   546 			// the aircraft has to search for a hangar on its own
       
   547 			station = FindNearestHangar(v);
       
   548 
       
   549 			next_airport_has_hangar = false;
       
   550 			if (station == INVALID_STATION) return CMD_ERROR;
       
   551 			st = GetStation(station);
       
   552 			next_airport_index = station;
       
   553 
       
   554 		}
       
   555 
       
   556 		if (flags & DC_EXEC) {
       
   557 			v->current_order.type = OT_GOTO_DEPOT;
       
   558 			v->current_order.flags = OF_NON_STOP;
       
   559 			if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
       
   560 			v->current_order.refit_cargo = CT_INVALID;
       
   561 			v->current_order.dest = next_airport_index;
       
   562 			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
       
   563 			if (v->u.air.state == FLYING && !next_airport_has_hangar) {
       
   564 				/* The aircraft is now heading for a different hangar than the next in the orders */
       
   565 				AircraftNextAirportPos_and_Order(v);
       
   566 				v->u.air.targetairport = next_airport_index;
       
   567 			}
       
   568 		}
       
   569 	}
       
   570 
       
   571 	return 0;
       
   572 }
       
   573 
       
   574 
       
   575 /** Refits an aircraft to the specified cargo type.
       
   576  * @param tile unused
       
   577  * @param p1 vehicle ID of the aircraft to refit
       
   578  * @param p2 various bitstuffed elements
       
   579  * - p2 = (bit 0-7) - the new cargo type to refit to
       
   580  * - p2 = (bit 8-15) - the new cargo subtype to refit to
       
   581  */
       
   582 int32 CmdRefitAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   583 {
       
   584 	Vehicle *v;
       
   585 	int pass, mail;
       
   586 	int32 cost;
       
   587 	CargoID new_cid = GB(p2, 0, 8);
       
   588 	byte new_subtype = GB(p2, 8, 8);
       
   589 	const AircraftVehicleInfo *avi;
       
   590 	uint16 callback = CALLBACK_FAILED;
       
   591 
       
   592 	if (!IsValidVehicleID(p1)) return CMD_ERROR;
       
   593 
       
   594 	v = GetVehicle(p1);
       
   595 
       
   596 	if (v->type != VEH_Aircraft || !CheckOwnership(v->owner)) return CMD_ERROR;
       
   597 	if (!IsAircraftInHangarStopped(v)) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
       
   598 
       
   599 	avi = AircraftVehInfo(v->engine_type);
       
   600 
       
   601 	/* Check cargo */
       
   602 	if (new_cid > NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
       
   603 
       
   604 	SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_RUN);
       
   605 
       
   606 	/* Check the refit capacity callback */
       
   607 	if (HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_REFIT_CAPACITY)) {
       
   608 		/* Back up the existing cargo type */
       
   609 		CargoID temp_cid = v->cargo_type;
       
   610 		byte temp_subtype = v->cargo_subtype;
       
   611 		v->cargo_type = new_cid;
       
   612 		v->cargo_subtype = new_subtype;
       
   613 
       
   614 		callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
       
   615 
       
   616 		/* Restore the cargo type */
       
   617 		v->cargo_type = temp_cid;
       
   618 		v->cargo_subtype = temp_subtype;
       
   619 	}
       
   620 
       
   621 	if (callback == CALLBACK_FAILED) {
       
   622 		/* If the callback failed, or wasn't executed, use the aircraft's
       
   623 		 * default cargo capacity */
       
   624 		pass = AircraftDefaultCargoCapacity(new_cid, v->engine_type);
       
   625 	} else {
       
   626 		pass = callback;
       
   627 	}
       
   628 	_returned_refit_capacity = pass;
       
   629 
       
   630 	cost = 0;
       
   631 	if (IsHumanPlayer(v->owner) && new_cid != v->cargo_type) {
       
   632 		cost = GetRefitCost(v->engine_type);
       
   633 	}
       
   634 
       
   635 	if (flags & DC_EXEC) {
       
   636 		Vehicle *u;
       
   637 		v->cargo_cap = pass;
       
   638 
       
   639 		u = v->next;
       
   640 		mail = (new_cid != CT_PASSENGERS) ? 0 : avi->mail_capacity;
       
   641 		u->cargo_cap = mail;
       
   642 		if (v->cargo_type == new_cid) {
       
   643 			v->cargo_count = min(pass, v->cargo_count);
       
   644 			u->cargo_count = min(mail, u->cargo_count);
       
   645 		} else {
       
   646 			v->cargo_count = 0;
       
   647 			u->cargo_count = 0;
       
   648 		}
       
   649 		v->cargo_type = new_cid;
       
   650 		v->cargo_subtype = new_subtype;
       
   651 		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
       
   652 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
       
   653 		RebuildVehicleLists();
       
   654 	}
       
   655 
       
   656 	return cost;
       
   657 }
       
   658 
       
   659 
       
   660 static void CheckIfAircraftNeedsService(Vehicle *v)
       
   661 {
       
   662 	const Station* st;
       
   663 
       
   664 	if (_patches.servint_aircraft == 0) return;
       
   665 	if (!VehicleNeedsService(v)) return;
       
   666 	if (v->vehstatus & VS_STOPPED) return;
       
   667 
       
   668 	if (v->current_order.type == OT_GOTO_DEPOT &&
       
   669 			v->current_order.flags & OF_HALT_IN_DEPOT)
       
   670 		return;
       
   671 
       
   672 	if (_patches.gotodepot && VehicleHasDepotOrders(v)) return;
       
   673 
       
   674 	if (IsAircraftInHangar(v)) {
       
   675 		VehicleServiceInDepot(v);
       
   676 		return;
       
   677 	}
       
   678 
       
   679 	st = GetStation(v->current_order.dest);
       
   680 	// only goto depot if the target airport has terminals (eg. it is airport)
       
   681 	if (IsValidStation(st) && st->airport_tile != 0 && GetAirport(st->airport_type)->terminals != NULL) {
       
   682 //		printf("targetairport = %d, st->index = %d\n", v->u.air.targetairport, st->index);
       
   683 //		v->u.air.targetairport = st->index;
       
   684 		v->current_order.type = OT_GOTO_DEPOT;
       
   685 		v->current_order.flags = OF_NON_STOP;
       
   686 		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
       
   687 	} else if (v->current_order.type == OT_GOTO_DEPOT) {
       
   688 		v->current_order.type = OT_DUMMY;
       
   689 		v->current_order.flags = 0;
       
   690 		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
       
   691 	}
       
   692 }
       
   693 
       
   694 void OnNewDay_Aircraft(Vehicle *v)
       
   695 {
       
   696 	int32 cost;
       
   697 
       
   698 	if (v->subtype > 2) return;
       
   699 
       
   700 	if ((++v->day_counter & 7) == 0) DecreaseVehicleValue(v);
       
   701 
       
   702 	CheckOrders(v);
       
   703 
       
   704 	CheckVehicleBreakdown(v);
       
   705 	AgeVehicle(v);
       
   706 	CheckIfAircraftNeedsService(v);
       
   707 
       
   708 	if (v->vehstatus & VS_STOPPED) return;
       
   709 
       
   710 	cost = AircraftVehInfo(v->engine_type)->running_cost * _price.aircraft_running / 364;
       
   711 
       
   712 	v->profit_this_year -= cost >> 8;
       
   713 
       
   714 	SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_RUN);
       
   715 	SubtractMoneyFromPlayerFract(v->owner, cost);
       
   716 
       
   717 	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
       
   718 	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
       
   719 }
       
   720 
       
   721 void AircraftYearlyLoop(void)
       
   722 {
       
   723 	Vehicle *v;
       
   724 
       
   725 	FOR_ALL_VEHICLES(v) {
       
   726 		if (v->type == VEH_Aircraft && v->subtype <= 2) {
       
   727 			v->profit_last_year = v->profit_this_year;
       
   728 			v->profit_this_year = 0;
       
   729 			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
       
   730 		}
       
   731 	}
       
   732 }
       
   733 
       
   734 static void AgeAircraftCargo(Vehicle *v)
       
   735 {
       
   736 	if (_age_cargo_skip_counter != 0) return;
       
   737 
       
   738 	do {
       
   739 		if (v->cargo_days != 0xFF) v->cargo_days++;
       
   740 		v = v->next;
       
   741 	} while (v != NULL);
       
   742 }
       
   743 
       
   744 static void HelicopterTickHandler(Vehicle *v)
       
   745 {
       
   746 	Vehicle *u;
       
   747 	int tick,spd;
       
   748 	SpriteID img;
       
   749 
       
   750 	u = v->next->next;
       
   751 
       
   752 	if (u->vehstatus & VS_HIDDEN) return;
       
   753 
       
   754 	// if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
       
   755 	// loading/unloading at a terminal or stopped
       
   756 	if (v->current_order.type == OT_LOADING || (v->vehstatus & VS_STOPPED)) {
       
   757 		if (u->cur_speed != 0) {
       
   758 			u->cur_speed++;
       
   759 			if (u->cur_speed >= 0x80 && u->u.air.state == HRS_ROTOR_MOVING_3) {
       
   760 				u->cur_speed = 0;
       
   761 			}
       
   762 		}
       
   763 	} else {
       
   764 		if (u->cur_speed == 0)
       
   765 			u->cur_speed = 0x70;
       
   766 
       
   767 		if (u->cur_speed >= 0x50)
       
   768 			u->cur_speed--;
       
   769 	}
       
   770 
       
   771 	tick = ++u->tick_counter;
       
   772 	spd = u->cur_speed >> 4;
       
   773 
       
   774 	if (spd == 0) {
       
   775 		u->u.air.state = HRS_ROTOR_STOPPED;
       
   776 		img = GetRotorImage(v);
       
   777 		if (u->cur_image == img) return;
       
   778 	} else if (tick >= spd) {
       
   779 		u->tick_counter = 0;
       
   780 		u->u.air.state++;
       
   781 		if (u->u.air.state > HRS_ROTOR_MOVING_3) u->u.air.state = HRS_ROTOR_MOVING_1;
       
   782 		img = GetRotorImage(v);
       
   783 	} else {
       
   784 		return;
       
   785 	}
       
   786 
       
   787 	u->cur_image = img;
       
   788 
       
   789 	BeginVehicleMove(u);
       
   790 	VehiclePositionChanged(u);
       
   791 	EndVehicleMove(u);
       
   792 }
       
   793 
       
   794 static void SetAircraftPosition(Vehicle *v, int x, int y, int z)
       
   795 {
       
   796 	Vehicle *u;
       
   797 	int safe_x;
       
   798 	int safe_y;
       
   799 
       
   800 	v->x_pos = x;
       
   801 	v->y_pos = y;
       
   802 	v->z_pos = z;
       
   803 
       
   804 	v->cur_image = GetAircraftImage(v, v->direction);
       
   805 	if (v->subtype == 0) v->next->next->cur_image = GetRotorImage(v);
       
   806 
       
   807 	BeginVehicleMove(v);
       
   808 	VehiclePositionChanged(v);
       
   809 	EndVehicleMove(v);
       
   810 
       
   811 	u = v->next;
       
   812 
       
   813 	safe_x = clamp(x, 0, MapMaxX() * TILE_SIZE);
       
   814 	safe_y = clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
       
   815 	u->x_pos = x;
       
   816 	u->y_pos = y - ((v->z_pos-GetSlopeZ(safe_x, safe_y)) >> 3);;
       
   817 
       
   818 	safe_y = clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
       
   819 	u->z_pos = GetSlopeZ(safe_x, safe_y);
       
   820 	u->cur_image = v->cur_image;
       
   821 
       
   822 	BeginVehicleMove(u);
       
   823 	VehiclePositionChanged(u);
       
   824 	EndVehicleMove(u);
       
   825 
       
   826 	u = u->next;
       
   827 	if (u != NULL) {
       
   828 		u->x_pos = x;
       
   829 		u->y_pos = y;
       
   830 		u->z_pos = z + 5;
       
   831 
       
   832 		BeginVehicleMove(u);
       
   833 		VehiclePositionChanged(u);
       
   834 		EndVehicleMove(u);
       
   835 	}
       
   836 }
       
   837 
       
   838 /** Handle Aircraft specific tasks when a an Aircraft enters a hangar
       
   839  * @param *v Vehicle that enters the hangar
       
   840  */
       
   841 void HandleAircraftEnterHangar(Vehicle *v)
       
   842 {
       
   843 	Vehicle *u;
       
   844 
       
   845 	v->subspeed = 0;
       
   846 	v->progress = 0;
       
   847 
       
   848 	u = v->next;
       
   849 	u->vehstatus |= VS_HIDDEN;
       
   850 	u = u->next;
       
   851 	if (u != NULL) {
       
   852 		u->vehstatus |= VS_HIDDEN;
       
   853 		u->cur_speed = 0;
       
   854 	}
       
   855 
       
   856 	SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
       
   857 }
       
   858 
       
   859 static void PlayAircraftSound(const Vehicle* v)
       
   860 {
       
   861 	if (!PlayVehicleSound(v, VSE_START)) {
       
   862 		SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
       
   863 	}
       
   864 }
       
   865 
       
   866 static bool UpdateAircraftSpeed(Vehicle *v)
       
   867 {
       
   868 	uint spd = v->acceleration * 2;
       
   869 	byte t;
       
   870 
       
   871 	v->subspeed = (t=v->subspeed) + (byte)spd;
       
   872 	spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), v->max_speed);
       
   873 
       
   874 	// adjust speed for broken vehicles
       
   875 	if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, 27);
       
   876 
       
   877 	//updates statusbar only if speed have changed to save CPU time
       
   878 	if (spd != v->cur_speed) {
       
   879 		v->cur_speed = spd;
       
   880 		if (_patches.vehicle_speed)
       
   881 			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
       
   882 	}
       
   883 
       
   884 	if (!(v->direction & 1)) spd = spd * 3 / 4;
       
   885 
       
   886 	if (spd == 0) return false;
       
   887 
       
   888 	if ((byte)++spd == 0) return true;
       
   889 
       
   890 	v->progress = (t = v->progress) - (byte)spd;
       
   891 
       
   892 	return t < v->progress;
       
   893 }
       
   894 
       
   895 // get Aircraft running altitude
       
   896 static byte GetAircraftFlyingAltitude(const Vehicle *v)
       
   897 {
       
   898 	switch (v->max_speed) {
       
   899 		case 37: return 162;
       
   900 		case 74: return 171;
       
   901 		default: return 180;
       
   902 	}
       
   903 }
       
   904 
       
   905 static bool AircraftController(Vehicle *v)
       
   906 {
       
   907 	Station *st;
       
   908 	const AirportMovingData *amd;
       
   909 	Vehicle *u;
       
   910 	byte z,newdir,maxz,curz;
       
   911 	GetNewVehiclePosResult gp;
       
   912 	uint dist;
       
   913 	int x,y;
       
   914 
       
   915 	st = GetStation(v->u.air.targetairport);
       
   916 
       
   917 	// prevent going to 0,0 if airport is deleted.
       
   918 	{
       
   919 		TileIndex tile = st->airport_tile;
       
   920 
       
   921 		if (tile == 0) tile = st->xy;
       
   922 		// xy of destination
       
   923 		x = TileX(tile) * TILE_SIZE;
       
   924 		y = TileY(tile) * TILE_SIZE;
       
   925 	}
       
   926 
       
   927 	// get airport moving data
       
   928 	amd = GetAirportMovingData(st->airport_type, v->u.air.pos);
       
   929 
       
   930 	// Helicopter raise
       
   931 	if (amd->flag & AMED_HELI_RAISE) {
       
   932 		u = v->next->next;
       
   933 
       
   934 		// Make sure the rotors don't rotate too fast
       
   935 		if (u->cur_speed > 32) {
       
   936 			v->cur_speed = 0;
       
   937 			if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
       
   938 		} else {
       
   939 			u->cur_speed = 32;
       
   940 			if (UpdateAircraftSpeed(v)) {
       
   941 				v->tile = 0;
       
   942 
       
   943 				// Reached altitude?
       
   944 				if (v->z_pos >= 184) {
       
   945 					v->cur_speed = 0;
       
   946 					return true;
       
   947 				}
       
   948 				SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos+1);
       
   949 			}
       
   950 		}
       
   951 		return false;
       
   952 	}
       
   953 
       
   954 	// Helicopter landing.
       
   955 	if (amd->flag & AMED_HELI_LOWER) {
       
   956 		if (UpdateAircraftSpeed(v)) {
       
   957 			if (st->airport_tile == 0) {
       
   958 				// FIXME - AircraftController -> if station no longer exists, do not land
       
   959 				// helicopter will circle until sign disappears, then go to next order
       
   960 				// * what to do when it is the only order left, right now it just stays in 1 place
       
   961 				v->u.air.state = FLYING;
       
   962 				AircraftNextAirportPos_and_Order(v);
       
   963 				return false;
       
   964 			}
       
   965 
       
   966 			// Vehicle is now at the airport.
       
   967 			v->tile = st->airport_tile;
       
   968 
       
   969 			// Find altitude of landing position.
       
   970 			z = GetSlopeZ(x, y) + 1;
       
   971 			if (st->airport_type == AT_OILRIG) z += 54;
       
   972 			if (st->airport_type == AT_HELIPORT) z += 60;
       
   973 
       
   974 			if (z == v->z_pos) {
       
   975 				u = v->next->next;
       
   976 
       
   977 				// Increase speed of rotors. When speed is 80, we've landed.
       
   978 				if (u->cur_speed >= 80) return true;
       
   979 				u->cur_speed += 4;
       
   980 			} else if (v->z_pos > z) {
       
   981 				SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos-1);
       
   982 			} else {
       
   983 				SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos+1);
       
   984 			}
       
   985 		}
       
   986 		return false;
       
   987 	}
       
   988 
       
   989 	// Get distance from destination pos to current pos.
       
   990 	dist = myabs(x + amd->x - v->x_pos) +  myabs(y + amd->y - v->y_pos);
       
   991 
       
   992 	// Need exact position?
       
   993 	if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U))
       
   994 		return true;
       
   995 
       
   996 	// At final pos?
       
   997 	if (dist == 0) {
       
   998 		DirDiff dirdiff;
       
   999 
       
  1000 		if (v->cur_speed > 12) v->cur_speed = 12;
       
  1001 
       
  1002 		// Change direction smoothly to final direction.
       
  1003 		dirdiff = DirDifference(amd->direction, v->direction);
       
  1004 		// if distance is 0, and plane points in right direction, no point in calling
       
  1005 		// UpdateAircraftSpeed(). So do it only afterwards
       
  1006 		if (dirdiff == DIRDIFF_SAME) {
       
  1007 			v->cur_speed = 0;
       
  1008 			return true;
       
  1009 		}
       
  1010 
       
  1011 		if (!UpdateAircraftSpeed(v)) return false;
       
  1012 
       
  1013 		v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
       
  1014 		v->cur_speed >>= 1;
       
  1015 
       
  1016 		SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
       
  1017 		return false;
       
  1018 	}
       
  1019 
       
  1020 	if (!(amd->flag & AMED_NOSPDCLAMP) && v->cur_speed > 12) v->cur_speed = 12;
       
  1021 
       
  1022 	if (!UpdateAircraftSpeed(v)) return false;
       
  1023 
       
  1024 	if (v->load_unload_time_rem != 0) v->load_unload_time_rem--;
       
  1025 
       
  1026 	// Turn. Do it slowly if in the air.
       
  1027 	newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
       
  1028 	if (newdir != v->direction) {
       
  1029 		if (amd->flag & AMED_SLOWTURN) {
       
  1030 			if (v->load_unload_time_rem == 0) v->load_unload_time_rem = 8;
       
  1031 			v->direction = newdir;
       
  1032 		} else {
       
  1033 			v->cur_speed >>= 1;
       
  1034 			v->direction = newdir;
       
  1035 		}
       
  1036 	}
       
  1037 
       
  1038 	// Move vehicle.
       
  1039 	GetNewVehiclePos(v, &gp);
       
  1040 	v->tile = gp.new_tile;
       
  1041 
       
  1042 	// If vehicle is in the air, use tile coordinate 0.
       
  1043 	if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
       
  1044 
       
  1045 	// Adjust Z for land or takeoff?
       
  1046 	z = v->z_pos;
       
  1047 
       
  1048 	if (amd->flag & AMED_TAKEOFF) {
       
  1049 		z += 2;
       
  1050 		maxz = GetAircraftFlyingAltitude(v);
       
  1051 		if (z > maxz) z = maxz;
       
  1052 	}
       
  1053 
       
  1054 	if (amd->flag & AMED_LAND) {
       
  1055 		if (st->airport_tile == 0) {
       
  1056 			v->u.air.state = FLYING;
       
  1057 			AircraftNextAirportPos_and_Order(v);
       
  1058 			// get aircraft back on running altitude
       
  1059 			SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
       
  1060 			return false;
       
  1061 		}
       
  1062 
       
  1063 		curz = GetSlopeZ(x, y) + 1;
       
  1064 
       
  1065 		if (curz > z) {
       
  1066 			z++;
       
  1067 		} else {
       
  1068 			int t = max(1, dist - 4);
       
  1069 
       
  1070 			z -= ((z - curz) + t - 1) / t;
       
  1071 			if (z < curz) z = curz;
       
  1072 		}
       
  1073 	}
       
  1074 
       
  1075 	// We've landed. Decrase speed when we're reaching end of runway.
       
  1076 	if (amd->flag & AMED_BRAKE) {
       
  1077 		curz = GetSlopeZ(x, y) + 1;
       
  1078 
       
  1079 		if (z > curz) {
       
  1080 			z--;
       
  1081 		} else if (z < curz) {
       
  1082 			z++;
       
  1083 		}
       
  1084 
       
  1085 		if (dist < 64 && v->cur_speed > 12) v->cur_speed -= 4;
       
  1086 	}
       
  1087 
       
  1088 	SetAircraftPosition(v, gp.x, gp.y, z);
       
  1089 	return false;
       
  1090 }
       
  1091 
       
  1092 
       
  1093 static void HandleCrashedAircraft(Vehicle *v)
       
  1094 {
       
  1095 	uint32 r;
       
  1096 	Station *st;
       
  1097 	int z;
       
  1098 
       
  1099 	v->u.air.crashed_counter++;
       
  1100 
       
  1101 	st = GetStation(v->u.air.targetairport);
       
  1102 
       
  1103 	// make aircraft crash down to the ground
       
  1104 	if (v->u.air.crashed_counter < 500 && st->airport_tile==0 && ((v->u.air.crashed_counter % 3) == 0) ) {
       
  1105 		z = GetSlopeZ(v->x_pos, v->y_pos);
       
  1106 		v->z_pos -= 1;
       
  1107 		if (v->z_pos == z) {
       
  1108 			v->u.air.crashed_counter = 500;
       
  1109 			v->z_pos++;
       
  1110 		}
       
  1111 	}
       
  1112 
       
  1113 	if (v->u.air.crashed_counter < 650) {
       
  1114 		if (CHANCE16R(1,32,r)) {
       
  1115 			static const DirDiff delta[] = {
       
  1116 				DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
       
  1117 			};
       
  1118 
       
  1119 			v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
       
  1120 			SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
       
  1121 			r = Random();
       
  1122 			CreateEffectVehicleRel(v,
       
  1123 				GB(r, 0, 4) + 4,
       
  1124 				GB(r, 4, 4) + 4,
       
  1125 				GB(r, 8, 4),
       
  1126 				EV_EXPLOSION_SMALL);
       
  1127 		}
       
  1128 	} else if (v->u.air.crashed_counter >= 10000) {
       
  1129 		// remove rubble of crashed airplane
       
  1130 
       
  1131 		// clear runway-in on all airports, set by crashing plane
       
  1132 		// small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
       
  1133 		// but they all share the same number
       
  1134 		CLRBITS(st->airport_flags, RUNWAY_IN_block);
       
  1135 		CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block); // commuter airport
       
  1136 		CLRBITS(st->airport_flags, RUNWAY_IN2_block); // intercontinental
       
  1137 
       
  1138 		BeginVehicleMove(v);
       
  1139 		EndVehicleMove(v);
       
  1140 
       
  1141 		DoDeleteAircraft(v);
       
  1142 	}
       
  1143 }
       
  1144 
       
  1145 static void HandleBrokenAircraft(Vehicle *v)
       
  1146 {
       
  1147 	if (v->breakdown_ctr != 1) {
       
  1148 		v->breakdown_ctr = 1;
       
  1149 		v->vehstatus |= VS_AIRCRAFT_BROKEN;
       
  1150 
       
  1151 		if (v->breakdowns_since_last_service != 255)
       
  1152 			v->breakdowns_since_last_service++;
       
  1153 		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
       
  1154 		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
       
  1155 	}
       
  1156 }
       
  1157 
       
  1158 
       
  1159 static void HandleAircraftSmoke(Vehicle *v)
       
  1160 {
       
  1161 	static const struct {
       
  1162 		int8 x;
       
  1163 		int8 y;
       
  1164 	} smoke_pos[] = {
       
  1165 		{  5,  5 },
       
  1166 		{  6,  0 },
       
  1167 		{  5, -5 },
       
  1168 		{  0, -6 },
       
  1169 		{ -5, -5 },
       
  1170 		{ -6,  0 },
       
  1171 		{ -5,  5 },
       
  1172 		{  0,  6 }
       
  1173 	};
       
  1174 
       
  1175 	if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
       
  1176 
       
  1177 	if (v->cur_speed < 10) {
       
  1178 		v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
       
  1179 		v->breakdown_ctr = 0;
       
  1180 		return;
       
  1181 	}
       
  1182 
       
  1183 	if ((v->tick_counter & 0x1F) == 0) {
       
  1184 		CreateEffectVehicleRel(v,
       
  1185 			smoke_pos[v->direction].x,
       
  1186 			smoke_pos[v->direction].y,
       
  1187 			2,
       
  1188 			EV_SMOKE
       
  1189 		);
       
  1190 	}
       
  1191 }
       
  1192 
       
  1193 static void ProcessAircraftOrder(Vehicle *v)
       
  1194 {
       
  1195 	const Order *order;
       
  1196 
       
  1197 	switch (v->current_order.type) {
       
  1198 		case OT_GOTO_DEPOT:
       
  1199 			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return;
       
  1200 			if (v->current_order.flags & OF_SERVICE_IF_NEEDED &&
       
  1201 					!VehicleNeedsService(v)) {
       
  1202 				v->cur_order_index++;
       
  1203 			}
       
  1204 			break;
       
  1205 
       
  1206 		case OT_LOADING: return;
       
  1207 
       
  1208 		default: break;
       
  1209 	}
       
  1210 
       
  1211 	if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
       
  1212 
       
  1213 	order = GetVehicleOrder(v, v->cur_order_index);
       
  1214 
       
  1215 	if (order == NULL) {
       
  1216 		v->current_order.type = OT_NOTHING;
       
  1217 		v->current_order.flags = 0;
       
  1218 		return;
       
  1219 	}
       
  1220 
       
  1221 	if (order->type == OT_DUMMY && !CheckForValidOrders(v)) CrashAirplane(v);
       
  1222 
       
  1223 	if (order->type  == v->current_order.type  &&
       
  1224 			order->flags == v->current_order.flags &&
       
  1225 			order->dest  == v->current_order.dest)
       
  1226 		return;
       
  1227 
       
  1228 	v->current_order = *order;
       
  1229 
       
  1230 	// orders are changed in flight, ensure going to the right station
       
  1231 	if (order->type == OT_GOTO_STATION && v->u.air.state == FLYING) {
       
  1232 		AircraftNextAirportPos_and_Order(v);
       
  1233 		v->u.air.targetairport = order->dest;
       
  1234 	}
       
  1235 
       
  1236 	InvalidateVehicleOrder(v);
       
  1237 
       
  1238 	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
       
  1239 }
       
  1240 
       
  1241 /** Mark all views dirty for an aircraft.
       
  1242  * @param v vehicle to be redrawn.
       
  1243  */
       
  1244 static void MarkAircraftDirty(Vehicle *v)
       
  1245 {
       
  1246 		v->cur_image = GetAircraftImage(v, v->direction);
       
  1247 		if (v->subtype == 0) {
       
  1248 			v->next->next->cur_image = GetRotorImage(v);
       
  1249 		}
       
  1250 		MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
       
  1251 }
       
  1252 
       
  1253 static void HandleAircraftLoading(Vehicle *v, int mode)
       
  1254 {
       
  1255 	if (v->current_order.type == OT_NOTHING) return;
       
  1256 
       
  1257 	if (v->current_order.type != OT_DUMMY) {
       
  1258 		if (v->current_order.type != OT_LOADING) return;
       
  1259 		if (mode != 0) return;
       
  1260 		if (--v->load_unload_time_rem != 0) return;
       
  1261 
       
  1262 		if (CanFillVehicle(v) && (v->current_order.flags & OF_FULL_LOAD ||
       
  1263 				(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED)))) {
       
  1264 			SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC);
       
  1265 			if (LoadUnloadVehicle(v, false)) {
       
  1266 				InvalidateWindow(WC_AIRCRAFT_LIST, v->owner);
       
  1267 				MarkAircraftDirty(v);
       
  1268 			}
       
  1269 			return;
       
  1270 		}
       
  1271 
       
  1272 		{
       
  1273 			Order b = v->current_order;
       
  1274 			v->current_order.type = OT_NOTHING;
       
  1275 			v->current_order.flags = 0;
       
  1276 			MarkAircraftDirty(v);
       
  1277 			if (!(b.flags & OF_NON_STOP)) return;
       
  1278 		}
       
  1279 	}
       
  1280 	v->cur_order_index++;
       
  1281 	InvalidateVehicleOrder(v);
       
  1282 }
       
  1283 
       
  1284 static void CrashAirplane(Vehicle *v)
       
  1285 {
       
  1286 	uint16 amt;
       
  1287 	Station *st;
       
  1288 	StringID newsitem;
       
  1289 
       
  1290 	v->vehstatus |= VS_CRASHED;
       
  1291 	v->u.air.crashed_counter = 0;
       
  1292 
       
  1293 	CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
       
  1294 
       
  1295 	InvalidateWindow(WC_VEHICLE_VIEW, v->index);
       
  1296 
       
  1297 	amt = 2;
       
  1298 	if (v->cargo_type == CT_PASSENGERS) amt += v->cargo_count;
       
  1299 	SetDParam(0, amt);
       
  1300 
       
  1301 	v->cargo_count = 0;
       
  1302 	v->next->cargo_count = 0,
       
  1303 	st = GetStation(v->u.air.targetairport);
       
  1304 	if (st->airport_tile == 0) {
       
  1305 		newsitem = STR_PLANE_CRASH_OUT_OF_FUEL;
       
  1306 	} else {
       
  1307 		SetDParam(1, st->index);
       
  1308 		newsitem = STR_A034_PLANE_CRASH_DIE_IN_FIREBALL;
       
  1309 	}
       
  1310 
       
  1311 	SetDParam(1, st->index);
       
  1312 	AddNewsItem(newsitem,
       
  1313 		NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
       
  1314 		v->index,
       
  1315 		0);
       
  1316 
       
  1317 	SndPlayVehicleFx(SND_12_EXPLOSION, v);
       
  1318 }
       
  1319 
       
  1320 static void MaybeCrashAirplane(Vehicle *v)
       
  1321 {
       
  1322 	Station *st;
       
  1323 	uint16 prob;
       
  1324 	uint i;
       
  1325 
       
  1326 	st = GetStation(v->u.air.targetairport);
       
  1327 
       
  1328 	//FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports
       
  1329 	prob = 0x10000 / 1500;
       
  1330 	if (((st->airport_type == AT_SMALL) || (st->airport_type == AT_COMMUTER)) && (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) {
       
  1331 		prob = 0x10000 / 20;
       
  1332 	}
       
  1333 
       
  1334 	if (GB(Random(), 0, 16) > prob) return;
       
  1335 
       
  1336 	// Crash the airplane. Remove all goods stored at the station.
       
  1337 	for (i = 0; i != NUM_CARGO; i++) {
       
  1338 		st->goods[i].rating = 1;
       
  1339 		SB(st->goods[i].waiting_acceptance, 0, 12, 0);
       
  1340 	}
       
  1341 
       
  1342 	CrashAirplane(v);
       
  1343 }
       
  1344 
       
  1345 // we've landed and just arrived at a terminal
       
  1346 static void AircraftEntersTerminal(Vehicle *v)
       
  1347 {
       
  1348 	Station *st;
       
  1349 	Order old_order;
       
  1350 
       
  1351 	if (v->current_order.type == OT_GOTO_DEPOT) return;
       
  1352 
       
  1353 	st = GetStation(v->u.air.targetairport);
       
  1354 	v->last_station_visited = v->u.air.targetairport;
       
  1355 
       
  1356 	/* Check if station was ever visited before */
       
  1357 	if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
       
  1358 		uint32 flags;
       
  1359 
       
  1360 		st->had_vehicle_of_type |= HVOT_AIRCRAFT;
       
  1361 		SetDParam(0, st->index);
       
  1362 		// show newsitem of celebrating citizens
       
  1363 		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);
       
  1364 		AddNewsItem(
       
  1365 			STR_A033_CITIZENS_CELEBRATE_FIRST,
       
  1366 			flags,
       
  1367 			v->index,
       
  1368 			0);
       
  1369 	}
       
  1370 
       
  1371 	old_order = v->current_order;
       
  1372 	v->current_order.type = OT_LOADING;
       
  1373 	v->current_order.flags = 0;
       
  1374 
       
  1375 	if (old_order.type == OT_GOTO_STATION &&
       
  1376 			v->current_order.dest == v->last_station_visited) {
       
  1377 		v->current_order.flags =
       
  1378 			(old_order.flags & (OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER)) | OF_NON_STOP;
       
  1379 	}
       
  1380 
       
  1381 	SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC);
       
  1382 	LoadUnloadVehicle(v, true);
       
  1383 	MarkAircraftDirty(v);
       
  1384 	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
       
  1385 	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
       
  1386 }
       
  1387 
       
  1388 static void AircraftLand(Vehicle *v)
       
  1389 {
       
  1390 	v->sprite_width = v->sprite_height = 2;
       
  1391 }
       
  1392 
       
  1393 static void AircraftLandAirplane(Vehicle *v)
       
  1394 {
       
  1395 	AircraftLand(v);
       
  1396 	if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
       
  1397 		SndPlayVehicleFx(SND_17_SKID_PLANE, v);
       
  1398 	}
       
  1399 	MaybeCrashAirplane(v);
       
  1400 }
       
  1401 
       
  1402 // set the right pos when heading to other airports after takeoff
       
  1403 static void AircraftNextAirportPos_and_Order(Vehicle *v)
       
  1404 {
       
  1405 	const Station* st;
       
  1406 	const AirportFTAClass *apc;
       
  1407 
       
  1408 	if (v->current_order.type == OT_GOTO_STATION ||
       
  1409 			v->current_order.type == OT_GOTO_DEPOT)
       
  1410 		v->u.air.targetairport = v->current_order.dest;
       
  1411 
       
  1412 	st = GetStation(v->u.air.targetairport);
       
  1413 	apc = GetAirport(st->airport_type);
       
  1414 	v->u.air.pos = v->u.air.previous_pos = apc->entry_point;
       
  1415 }
       
  1416 
       
  1417 static void AircraftLeaveHangar(Vehicle *v)
       
  1418 {
       
  1419 	v->cur_speed = 0;
       
  1420 	v->subspeed = 0;
       
  1421 	v->progress = 0;
       
  1422 	v->direction = 3;
       
  1423 	v->vehstatus &= ~VS_HIDDEN;
       
  1424 	{
       
  1425 		Vehicle *u = v->next;
       
  1426 		u->vehstatus &= ~VS_HIDDEN;
       
  1427 
       
  1428 		// Rotor blades
       
  1429 		u = u->next;
       
  1430 		if (u != NULL) {
       
  1431 			u->vehstatus &= ~VS_HIDDEN;
       
  1432 			u->cur_speed = 80;
       
  1433 		}
       
  1434 	}
       
  1435 
       
  1436 	VehicleServiceInDepot(v);
       
  1437 	SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
       
  1438 	InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
       
  1439 	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
       
  1440 }
       
  1441 
       
  1442 
       
  1443 ////////////////////////////////////////////////////////////////////////////////
       
  1444 ///////////////////   AIRCRAFT MOVEMENT SCHEME  ////////////////////////////////
       
  1445 ////////////////////////////////////////////////////////////////////////////////
       
  1446 static void AircraftEventHandler_EnterTerminal(Vehicle *v, const AirportFTAClass *apc)
       
  1447 {
       
  1448 	AircraftEntersTerminal(v);
       
  1449 	v->u.air.state = apc->layout[v->u.air.pos].heading;
       
  1450 }
       
  1451 
       
  1452 static void AircraftEventHandler_EnterHangar(Vehicle *v, const AirportFTAClass *apc)
       
  1453 {
       
  1454 	VehicleEnterDepot(v);
       
  1455 	v->u.air.state = apc->layout[v->u.air.pos].heading;
       
  1456 }
       
  1457 
       
  1458 // In an Airport Hangar
       
  1459 static void AircraftEventHandler_InHangar(Vehicle *v, const AirportFTAClass *apc)
       
  1460 {
       
  1461 	// if we just arrived, execute EnterHangar first
       
  1462 	if (v->u.air.previous_pos != v->u.air.pos) {
       
  1463 		AircraftEventHandler_EnterHangar(v, apc);
       
  1464 		return;
       
  1465 	}
       
  1466 
       
  1467 	// if we were sent to the depot, stay there
       
  1468 	if (v->current_order.type == OT_GOTO_DEPOT && (v->vehstatus & VS_STOPPED)) {
       
  1469 		v->current_order.type = OT_NOTHING;
       
  1470 		v->current_order.flags = 0;
       
  1471 		return;
       
  1472 	}
       
  1473 
       
  1474 	if (v->current_order.type != OT_GOTO_STATION &&
       
  1475 			v->current_order.type != OT_GOTO_DEPOT)
       
  1476 		return;
       
  1477 
       
  1478 	// if the block of the next position is busy, stay put
       
  1479 	if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
       
  1480 
       
  1481 	// We are already at the target airport, we need to find a terminal
       
  1482 	if (v->current_order.dest == v->u.air.targetairport) {
       
  1483 		// FindFreeTerminal:
       
  1484 		// 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal
       
  1485 		if (v->subtype != 0) {
       
  1486 			if (!AirportFindFreeTerminal(v, apc)) return; // airplane
       
  1487 		} else {
       
  1488 			if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
       
  1489 		}
       
  1490 	} else { // Else prepare for launch.
       
  1491 		// airplane goto state takeoff, helicopter to helitakeoff
       
  1492 		v->u.air.state = (v->subtype != 0) ? TAKEOFF : HELITAKEOFF;
       
  1493 	}
       
  1494 	AircraftLeaveHangar(v);
       
  1495 	AirportMove(v, apc);
       
  1496 }
       
  1497 
       
  1498 // At one of the Airport's Terminals
       
  1499 static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *apc)
       
  1500 {
       
  1501 	// if we just arrived, execute EnterTerminal first
       
  1502 	if (v->u.air.previous_pos != v->u.air.pos) {
       
  1503 		AircraftEventHandler_EnterTerminal(v, apc);
       
  1504 		// on an airport with helipads, a helicopter will always land there
       
  1505 		// and get serviced at the same time - patch setting
       
  1506 		if (_patches.serviceathelipad) {
       
  1507 			if (v->subtype == 0 && apc->helipads != NULL) {
       
  1508 				// an exerpt of ServiceAircraft, without the invisibility stuff
       
  1509 				v->date_of_last_service = _date;
       
  1510 				v->breakdowns_since_last_service = 0;
       
  1511 				v->reliability = GetEngine(v->engine_type)->reliability;
       
  1512 				InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
       
  1513 			}
       
  1514 		}
       
  1515 		return;
       
  1516 	}
       
  1517 
       
  1518 	if (v->current_order.type == OT_NOTHING) return;
       
  1519 
       
  1520 	// if the block of the next position is busy, stay put
       
  1521 	if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
       
  1522 
       
  1523 	// airport-road is free. We either have to go to another airport, or to the hangar
       
  1524 	// ---> start moving
       
  1525 
       
  1526 	switch (v->current_order.type) {
       
  1527 		case OT_GOTO_STATION: // ready to fly to another airport
       
  1528 			// airplane goto state takeoff, helicopter to helitakeoff
       
  1529 			v->u.air.state = (v->subtype != 0) ? TAKEOFF : HELITAKEOFF;
       
  1530 			break;
       
  1531 		case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
       
  1532 			if (v->current_order.dest == v->u.air.targetairport) {
       
  1533 				v->u.air.state = HANGAR;
       
  1534 			} else {
       
  1535 				v->u.air.state = (v->subtype != 0) ? TAKEOFF : HELITAKEOFF;
       
  1536 			}
       
  1537 			break;
       
  1538 		default:  // orders have been deleted (no orders), goto depot and don't bother us
       
  1539 			v->current_order.type = OT_NOTHING;
       
  1540 			v->current_order.flags = 0;
       
  1541 			v->u.air.state = HANGAR;
       
  1542 	}
       
  1543 	AirportMove(v, apc);
       
  1544 }
       
  1545 
       
  1546 static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *apc)
       
  1547 {
       
  1548 	assert("OK, you shouldn't be here, check your Airport Scheme!" && 0);
       
  1549 }
       
  1550 
       
  1551 static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *apc) {
       
  1552 	PlayAircraftSound(v); // play takeoffsound for airplanes
       
  1553 	v->u.air.state = STARTTAKEOFF;
       
  1554 }
       
  1555 
       
  1556 static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *apc)
       
  1557 {
       
  1558 	v->sprite_width = v->sprite_height = 24; // ??? no idea what this is
       
  1559 	v->u.air.state = ENDTAKEOFF;
       
  1560 }
       
  1561 
       
  1562 static void AircraftEventHandler_EndTakeOff(Vehicle *v, const AirportFTAClass *apc)
       
  1563 {
       
  1564 	v->u.air.state = FLYING;
       
  1565 	// get the next position to go to, differs per airport
       
  1566 	AircraftNextAirportPos_and_Order(v);
       
  1567 }
       
  1568 
       
  1569 static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *apc)
       
  1570 {
       
  1571 	const Player* p = GetPlayer(v->owner);
       
  1572 	v->sprite_width = v->sprite_height = 24; // ??? no idea what this is
       
  1573 	v->u.air.state = FLYING;
       
  1574 	// get the next position to go to, differs per airport
       
  1575 	AircraftNextAirportPos_and_Order(v);
       
  1576 
       
  1577 	// check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
       
  1578 	// unless it is due for renewal but the engine is no longer available
       
  1579 	if (v->owner == _local_player && (
       
  1580 				EngineHasReplacementForPlayer(p, v->engine_type) ||
       
  1581 				((p->engine_renew && v->age - v->max_age > p->engine_renew_months * 30) &&
       
  1582 				HASBIT(GetEngine(v->engine_type)->player_avail, _local_player))
       
  1583 			)) {
       
  1584 		_current_player = _local_player;
       
  1585 		DoCommandP(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_SHOW_NO_ERROR);
       
  1586 		_current_player = OWNER_NONE;
       
  1587 	}
       
  1588 }
       
  1589 
       
  1590 static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *apc)
       
  1591 {
       
  1592 	Station *st;
       
  1593 	byte landingtype;
       
  1594 	AirportFTA *current;
       
  1595 	uint16 tcur_speed, tsubspeed;
       
  1596 
       
  1597 	st = GetStation(v->u.air.targetairport);
       
  1598 	// flying device is accepted at this station
       
  1599 	// small airport --> no helicopters (AIRCRAFT_ONLY)
       
  1600 	// all other airports --> all types of flying devices (ALL)
       
  1601 	// heliport/oilrig, etc --> no airplanes (HELICOPTERS_ONLY)
       
  1602 	// runway busy or not allowed to use this airstation, circle
       
  1603 	if (v->subtype != apc->acc_planes &&
       
  1604 			st->airport_tile != 0 &&
       
  1605 			(st->owner == OWNER_NONE || st->owner == v->owner)) {
       
  1606 		// {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
       
  1607 		// if it is an airplane, look for LANDING, for helicopter HELILANDING
       
  1608 		// it is possible to choose from multiple landing runways, so loop until a free one is found
       
  1609 		landingtype = (v->subtype != 0) ? LANDING : HELILANDING;
       
  1610 		current = apc->layout[v->u.air.pos].next;
       
  1611 		while (current != NULL) {
       
  1612 			if (current->heading == landingtype) {
       
  1613 				// save speed before, since if AirportHasBlock is false, it resets them to 0
       
  1614 				// we don't want that for plane in air
       
  1615 				// hack for speed thingie
       
  1616 				tcur_speed = v->cur_speed;
       
  1617 				tsubspeed = v->subspeed;
       
  1618 				if (!AirportHasBlock(v, current, apc)) {
       
  1619 					v->u.air.state = landingtype; // LANDING / HELILANDING
       
  1620 					// it's a bit dirty, but I need to set position to next position, otherwise
       
  1621 					// if there are multiple runways, plane won't know which one it took (because
       
  1622 					// they all have heading LANDING). And also occupy that block!
       
  1623 					v->u.air.pos = current->next_position;
       
  1624 					SETBITS(st->airport_flags, apc->layout[v->u.air.pos].block);
       
  1625 					return;
       
  1626 				}
       
  1627 				v->cur_speed = tcur_speed;
       
  1628 				v->subspeed = tsubspeed;
       
  1629 			}
       
  1630 			current = current->next;
       
  1631 		}
       
  1632 	}
       
  1633 	v->u.air.state = FLYING;
       
  1634 	v->u.air.pos = apc->layout[v->u.air.pos].next_position;
       
  1635 }
       
  1636 
       
  1637 static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *apc)
       
  1638 {
       
  1639 	const Player* p = GetPlayer(v->owner);
       
  1640 	AircraftLandAirplane(v);  // maybe crash airplane
       
  1641 	v->u.air.state = ENDLANDING;
       
  1642 	// check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
       
  1643 	if (v->current_order.type != OT_GOTO_DEPOT && v->owner == _local_player) {
       
  1644 		// only the vehicle owner needs to calculate the rest (locally)
       
  1645 		if (EngineHasReplacementForPlayer(p, v->engine_type) ||
       
  1646 			(p->engine_renew && v->age - v->max_age > (p->engine_renew_months * 30))) {
       
  1647 			// send the aircraft to the hangar at next airport
       
  1648 			_current_player = _local_player;
       
  1649 			DoCommandP(v->tile, v->index, DEPOT_SERVICE, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_SHOW_NO_ERROR);
       
  1650 			_current_player = OWNER_NONE;
       
  1651 		}
       
  1652 	}
       
  1653 }
       
  1654 
       
  1655 static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *apc)
       
  1656 {
       
  1657 	AircraftLand(v); // helicopters don't crash
       
  1658 	v->u.air.state = HELIENDLANDING;
       
  1659 }
       
  1660 
       
  1661 static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *apc)
       
  1662 {
       
  1663 	// next block busy, don't do a thing, just wait
       
  1664 	if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
       
  1665 
       
  1666 	// if going to terminal (OT_GOTO_STATION) choose one
       
  1667 	// 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
       
  1668 	// 2. not going for terminal (but depot, no order),
       
  1669 	// --> get out of the way to the hangar.
       
  1670 	if (v->current_order.type == OT_GOTO_STATION) {
       
  1671 		if (AirportFindFreeTerminal(v, apc)) return;
       
  1672 	}
       
  1673 	v->u.air.state = HANGAR;
       
  1674 
       
  1675 }
       
  1676 
       
  1677 static void AircraftEventHandler_HeliEndLanding(Vehicle *v, const AirportFTAClass *apc)
       
  1678 {
       
  1679 	// next block busy, don't do a thing, just wait
       
  1680 	if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
       
  1681 
       
  1682 	// if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
       
  1683 	// 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
       
  1684 	// 2. not going for terminal (but depot, no order),
       
  1685 	// --> get out of the way to the hangar IF there are terminals on the airport.
       
  1686 	// --> else TAKEOFF
       
  1687 	// the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
       
  1688 	// must go to a hangar.
       
  1689 	if (v->current_order.type == OT_GOTO_STATION) {
       
  1690 		if (AirportFindFreeHelipad(v, apc)) return;
       
  1691 	}
       
  1692 	v->u.air.state = (apc->nof_depots != 0) ? HANGAR : HELITAKEOFF;
       
  1693 }
       
  1694 
       
  1695 typedef void AircraftStateHandler(Vehicle *v, const AirportFTAClass *apc);
       
  1696 static AircraftStateHandler * const _aircraft_state_handlers[] = {
       
  1697 	AircraftEventHandler_General,        // TO_ALL         =  0
       
  1698 	AircraftEventHandler_InHangar,       // HANGAR         =  1
       
  1699 	AircraftEventHandler_AtTerminal,     // TERM1          =  2
       
  1700 	AircraftEventHandler_AtTerminal,     // TERM2          =  3
       
  1701 	AircraftEventHandler_AtTerminal,     // TERM3          =  4
       
  1702 	AircraftEventHandler_AtTerminal,     // TERM4          =  5
       
  1703 	AircraftEventHandler_AtTerminal,     // TERM5          =  6
       
  1704 	AircraftEventHandler_AtTerminal,     // TERM6          =  7
       
  1705 	AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
       
  1706 	AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
       
  1707 	AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
       
  1708 	AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
       
  1709 	AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
       
  1710 	AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
       
  1711 	AircraftEventHandler_Flying,         // FLYING         = 14
       
  1712 	AircraftEventHandler_Landing,        // LANDING        = 15
       
  1713 	AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
       
  1714 	AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
       
  1715 	AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
       
  1716 	AircraftEventHandler_AtTerminal,     // TERM7          = 19
       
  1717 	AircraftEventHandler_AtTerminal,     // TERM8          = 20
       
  1718 	AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
       
  1719 	AircraftEventHandler_AtTerminal,     // HELIPAD4       = 22
       
  1720 };
       
  1721 
       
  1722 static void AirportClearBlock(const Vehicle *v, const AirportFTAClass *apc)
       
  1723 {
       
  1724 	// we have left the previous block, and entered the new one. Free the previous block
       
  1725 	if (apc->layout[v->u.air.previous_pos].block != apc->layout[v->u.air.pos].block) {
       
  1726 		Station *st = GetStation(v->u.air.targetairport);
       
  1727 
       
  1728 		CLRBITS(st->airport_flags, apc->layout[v->u.air.previous_pos].block);
       
  1729 	}
       
  1730 }
       
  1731 
       
  1732 static void AirportGoToNextPosition(Vehicle *v, const AirportFTAClass *apc)
       
  1733 {
       
  1734 	// if aircraft is not in position, wait until it is
       
  1735 	if (!AircraftController(v)) return;
       
  1736 
       
  1737 	AirportClearBlock(v, apc);
       
  1738 	AirportMove(v, apc); // move aircraft to next position
       
  1739 }
       
  1740 
       
  1741 // gets pos from vehicle and next orders
       
  1742 static bool AirportMove(Vehicle *v, const AirportFTAClass *apc)
       
  1743 {
       
  1744 	AirportFTA *current;
       
  1745 	byte prev_pos;
       
  1746 
       
  1747 	// error handling
       
  1748 	if (v->u.air.pos >= apc->nofelements) {
       
  1749 		DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->u.air.pos, apc->nofelements-1);
       
  1750 		assert(v->u.air.pos < apc->nofelements);
       
  1751 	}
       
  1752 
       
  1753 	current = &apc->layout[v->u.air.pos];
       
  1754 	// we have arrived in an important state (eg terminal, hangar, etc.)
       
  1755 	if (current->heading == v->u.air.state) {
       
  1756 		prev_pos = v->u.air.pos; // location could be changed in state, so save it before-hand
       
  1757 		_aircraft_state_handlers[v->u.air.state](v, apc);
       
  1758 		if (v->u.air.state != FLYING) v->u.air.previous_pos = prev_pos;
       
  1759 		return true;
       
  1760 	}
       
  1761 
       
  1762 	v->u.air.previous_pos = v->u.air.pos; // save previous location
       
  1763 
       
  1764 	// there is only one choice to move to
       
  1765 	if (current->next == NULL) {
       
  1766 		if (AirportSetBlocks(v, current, apc)) {
       
  1767 			v->u.air.pos = current->next_position;
       
  1768 		} // move to next position
       
  1769 		return false;
       
  1770 	}
       
  1771 
       
  1772 	// there are more choices to choose from, choose the one that
       
  1773 	// matches our heading
       
  1774 	do {
       
  1775 		if (v->u.air.state == current->heading || current->heading == TO_ALL) {
       
  1776 			if (AirportSetBlocks(v, current, apc)) {
       
  1777 				v->u.air.pos = current->next_position;
       
  1778 			} // move to next position
       
  1779 			return false;
       
  1780 		}
       
  1781 		current = current->next;
       
  1782 	} while (current != NULL);
       
  1783 
       
  1784 	DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d)", v->u.air.pos, v->u.air.state);
       
  1785 	DEBUG(misc, 0, "[Ap] airport entry point: %d, Vehicle: %d", apc->entry_point, v->index);
       
  1786 	assert(0);
       
  1787 	return false;
       
  1788 }
       
  1789 
       
  1790 // returns true if the road ahead is busy, eg. you must wait before proceeding
       
  1791 static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
       
  1792 {
       
  1793 	const AirportFTA *reference = &apc->layout[v->u.air.pos];
       
  1794 	const AirportFTA *next = &apc->layout[current_pos->next_position];
       
  1795 
       
  1796 	// same block, then of course we can move
       
  1797 	if (apc->layout[current_pos->position].block != next->block) {
       
  1798 		const Station *st = GetStation(v->u.air.targetairport);
       
  1799 		uint32 airport_flags = next->block;
       
  1800 
       
  1801 		// check additional possible extra blocks
       
  1802 		if (current_pos != reference && current_pos->block != NOTHING_block) {
       
  1803 			airport_flags |= current_pos->block;
       
  1804 		}
       
  1805 
       
  1806 		if (HASBITS(st->airport_flags, airport_flags)) {
       
  1807 			v->cur_speed = 0;
       
  1808 			v->subspeed = 0;
       
  1809 			return true;
       
  1810 		}
       
  1811 	}
       
  1812 	return false;
       
  1813 }
       
  1814 
       
  1815 // returns true on success. Eg, next block was free and we have occupied it
       
  1816 static bool AirportSetBlocks(Vehicle *v, AirportFTA *current_pos, const AirportFTAClass *apc)
       
  1817 {
       
  1818 	AirportFTA *next = &apc->layout[current_pos->next_position];
       
  1819 	AirportFTA *reference = &apc->layout[v->u.air.pos];
       
  1820 
       
  1821 	// if the next position is in another block, check it and wait until it is free
       
  1822 	if ((apc->layout[current_pos->position].block & next->block) != next->block) {
       
  1823 		uint32 airport_flags = next->block;
       
  1824 		Station* st = GetStation(v->u.air.targetairport);
       
  1825 		//search for all all elements in the list with the same state, and blocks != N
       
  1826 		// this means more blocks should be checked/set
       
  1827 			AirportFTA *current = current_pos;
       
  1828 		if (current == reference) current = current->next;
       
  1829 		while (current != NULL) {
       
  1830 			if (current->heading == current_pos->heading && current->block != 0) {
       
  1831 				airport_flags |= current->block;
       
  1832 				break;
       
  1833 			}
       
  1834 			current = current->next;
       
  1835 		};
       
  1836 
       
  1837 		// if the block to be checked is in the next position, then exclude that from
       
  1838 		// checking, because it has been set by the airplane before
       
  1839 		if (current_pos->block == next->block) airport_flags ^= next->block;
       
  1840 
       
  1841 		if (HASBITS(st->airport_flags, airport_flags)) {
       
  1842 			v->cur_speed = 0;
       
  1843 			v->subspeed = 0;
       
  1844 			return false;
       
  1845 		}
       
  1846 
       
  1847 		if (next->block != NOTHING_block) {
       
  1848 			SETBITS(st->airport_flags, airport_flags); // occupy next block
       
  1849 		}
       
  1850 	}
       
  1851 	return true;
       
  1852 }
       
  1853 
       
  1854 static bool FreeTerminal(Vehicle *v, byte i, byte last_terminal)
       
  1855 {
       
  1856 	Station *st = GetStation(v->u.air.targetairport);
       
  1857 	for (; i < last_terminal; i++) {
       
  1858 		if (!HASBIT(st->airport_flags, _airport_terminal_flag[i])) {
       
  1859 			// TERMINAL# HELIPAD#
       
  1860 			v->u.air.state = _airport_terminal_state[i]; // start moving to that terminal/helipad
       
  1861 			SETBIT(st->airport_flags, _airport_terminal_flag[i]); // occupy terminal/helipad
       
  1862 			return true;
       
  1863 		}
       
  1864 	}
       
  1865 	return false;
       
  1866 }
       
  1867 
       
  1868 static uint GetNumTerminals(const AirportFTAClass *apc)
       
  1869 {
       
  1870 	uint num = 0;
       
  1871 	uint i;
       
  1872 
       
  1873 	for (i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
       
  1874 
       
  1875 	return num;
       
  1876 }
       
  1877 
       
  1878 static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc)
       
  1879 {
       
  1880 	/* example of more terminalgroups
       
  1881 	 * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
       
  1882 	 * Heading 255 denotes a group. We see 2 groups here:
       
  1883 	 * 1. group 0 -- TERM_GROUP1_block (check block)
       
  1884 	 * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
       
  1885 	 * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
       
  1886 	 * looks at the corresponding terminals of that group. If no free ones are found, other
       
  1887 	 * possible groups are checked (in this case group 1, since that is after group 0). If that
       
  1888 	 * fails, then attempt fails and plane waits
       
  1889 	 */
       
  1890 	if (apc->terminals[0] > 1) {
       
  1891 		Station *st = GetStation(v->u.air.targetairport);
       
  1892 		AirportFTA *temp = apc->layout[v->u.air.pos].next;
       
  1893 
       
  1894 		while (temp != NULL) {
       
  1895 			if (temp->heading == 255) {
       
  1896 				if (!HASBITS(st->airport_flags, temp->block)) {
       
  1897 					int target_group;
       
  1898 					int i;
       
  1899 					int group_start = 0;
       
  1900 					int group_end;
       
  1901 
       
  1902 					//read which group do we want to go to?
       
  1903 					//(the first free group)
       
  1904 					target_group = temp->next_position + 1;
       
  1905 
       
  1906 					//at what terminal does the group start?
       
  1907 					//that means, sum up all terminals of
       
  1908 					//groups with lower number
       
  1909 					for (i = 1; i < target_group; i++)
       
  1910 						group_start += apc->terminals[i];
       
  1911 
       
  1912 					group_end = group_start + apc->terminals[target_group];
       
  1913 					if (FreeTerminal(v, group_start, group_end)) return true;
       
  1914 				}
       
  1915 			} else {
       
  1916 				/* once the heading isn't 255, we've exhausted the possible blocks.
       
  1917 				 * So we cannot move */
       
  1918 				return false;
       
  1919 			}
       
  1920 			temp = temp->next;
       
  1921 		}
       
  1922 	}
       
  1923 
       
  1924 	// if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max)
       
  1925 	return FreeTerminal(v, 0, GetNumTerminals(apc));
       
  1926 }
       
  1927 
       
  1928 static uint GetNumHelipads(const AirportFTAClass *apc)
       
  1929 {
       
  1930 	uint num = 0;
       
  1931 	uint i;
       
  1932 
       
  1933 	for (i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i];
       
  1934 
       
  1935 	return num;
       
  1936 }
       
  1937 
       
  1938 
       
  1939 static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc)
       
  1940 {
       
  1941 	// if an airport doesn't have helipads, use terminals
       
  1942 	if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc);
       
  1943 
       
  1944 	// if there are more helicoptergroups, pick one, just as in AirportFindFreeTerminal()
       
  1945 	if (apc->helipads[0] > 1) {
       
  1946 		const Station* st = GetStation(v->u.air.targetairport);
       
  1947 		const AirportFTA* temp = apc->layout[v->u.air.pos].next;
       
  1948 
       
  1949 		while (temp != NULL) {
       
  1950 			if (temp->heading == 255) {
       
  1951 				if (!HASBITS(st->airport_flags, temp->block)) {
       
  1952 					int target_group;
       
  1953 					int i;
       
  1954 					int group_start = 0;
       
  1955 					int group_end;
       
  1956 
       
  1957 					//read which group do we want to go to?
       
  1958 					//(the first free group)
       
  1959 					target_group = temp->next_position + 1;
       
  1960 
       
  1961 					//at what terminal does the group start?
       
  1962 					//that means, sum up all terminals of
       
  1963 					//groups with lower number
       
  1964 					for (i = 1; i < target_group; i++)
       
  1965 						group_start += apc->helipads[i];
       
  1966 
       
  1967 					group_end = group_start + apc->helipads[target_group];
       
  1968 					if (FreeTerminal(v, group_start, group_end)) return true;
       
  1969 				}
       
  1970 			} else {
       
  1971 				/* once the heading isn't 255, we've exhausted the possible blocks.
       
  1972 				 * So we cannot move */
       
  1973 				return false;
       
  1974 			}
       
  1975 			temp = temp->next;
       
  1976 		}
       
  1977 	} else {
       
  1978 		// only 1 helicoptergroup, check all helipads
       
  1979 		// The blocks for helipads start after the last terminal (MAX_TERMINALS)
       
  1980 		return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS);
       
  1981 	}
       
  1982 	return false; // it shouldn't get here anytime, but just to be sure
       
  1983 }
       
  1984 
       
  1985 static void AircraftEventHandler(Vehicle *v, int loop)
       
  1986 {
       
  1987 	v->tick_counter++;
       
  1988 
       
  1989 	if (v->vehstatus & VS_CRASHED) {
       
  1990 		HandleCrashedAircraft(v);
       
  1991 		return;
       
  1992 	}
       
  1993 
       
  1994 	if (v->vehstatus & VS_STOPPED) return;
       
  1995 
       
  1996 	/* aircraft is broken down? */
       
  1997 	if (v->breakdown_ctr != 0) {
       
  1998 		if (v->breakdown_ctr <= 2) {
       
  1999 			HandleBrokenAircraft(v);
       
  2000 		} else {
       
  2001 			v->breakdown_ctr--;
       
  2002 		}
       
  2003 	}
       
  2004 
       
  2005 	HandleAircraftSmoke(v);
       
  2006 	ProcessAircraftOrder(v);
       
  2007 	HandleAircraftLoading(v, loop);
       
  2008 
       
  2009 	if (v->current_order.type >= OT_LOADING) return;
       
  2010 
       
  2011 	// pass the right airport structure to the functions
       
  2012 	// DEREF_STATION gets target airport (Station *st), its type is passed to GetAirport
       
  2013 	// that returns the correct layout depending on type
       
  2014 	AirportGoToNextPosition(v, GetAirport(GetStation(v->u.air.targetairport)->airport_type));
       
  2015 }
       
  2016 
       
  2017 void Aircraft_Tick(Vehicle *v)
       
  2018 {
       
  2019 	int i;
       
  2020 
       
  2021 	if (v->subtype > 2) return;
       
  2022 
       
  2023 	if (v->subtype == 0) HelicopterTickHandler(v);
       
  2024 
       
  2025 	AgeAircraftCargo(v);
       
  2026 
       
  2027 	for (i = 0; i != 6; i++) {
       
  2028 		AircraftEventHandler(v, i);
       
  2029 		if (v->type != VEH_Aircraft) // In case it was deleted
       
  2030 			break;
       
  2031 	}
       
  2032 }
       
  2033 
       
  2034 void UpdateOilRig(void)
       
  2035 {
       
  2036 	Station* st;
       
  2037 
       
  2038 	FOR_ALL_STATIONS(st) {
       
  2039 		if (st->airport_type == 5) st->airport_type = AT_OILRIG;
       
  2040 	}
       
  2041 }
       
  2042 
       
  2043 // need to be called to load aircraft from old version
       
  2044 void UpdateOldAircraft(void)
       
  2045 {
       
  2046 	Station *st;
       
  2047 	Vehicle *v_oldstyle;
       
  2048 	GetNewVehiclePosResult gp;
       
  2049 
       
  2050 	// set airport_flags to 0 for all airports just to be sure
       
  2051 	FOR_ALL_STATIONS(st) {
       
  2052 		st->airport_flags = 0; // reset airport
       
  2053 		// type of oilrig has been moved, update it (3-5)
       
  2054 		if (st->airport_type == 3) st->airport_type = AT_OILRIG;
       
  2055 	}
       
  2056 
       
  2057 	FOR_ALL_VEHICLES(v_oldstyle) {
       
  2058 	// airplane has another vehicle with subtype 4 (shadow), helicopter also has 3 (rotor)
       
  2059 	// skip those
       
  2060 		if (v_oldstyle->type == VEH_Aircraft && v_oldstyle->subtype <= 2) {
       
  2061 			// airplane in terminal stopped doesn't hurt anyone, so goto next
       
  2062 			if (v_oldstyle->vehstatus & VS_STOPPED && v_oldstyle->u.air.state == 0) {
       
  2063 				v_oldstyle->u.air.state = HANGAR;
       
  2064 				continue;
       
  2065 			}
       
  2066 
       
  2067 			AircraftLeaveHangar(v_oldstyle); // make airplane visible if it was in a depot for example
       
  2068 			v_oldstyle->vehstatus &= ~VS_STOPPED; // make airplane moving
       
  2069 			v_oldstyle->u.air.state = FLYING;
       
  2070 			AircraftNextAirportPos_and_Order(v_oldstyle); // move it to the entry point of the airport
       
  2071 			GetNewVehiclePos(v_oldstyle, &gp); // get the position of the plane (to be used for setting)
       
  2072 			v_oldstyle->tile = 0; // aircraft in air is tile=0
       
  2073 
       
  2074 			// correct speed of helicopter-rotors
       
  2075 			if (v_oldstyle->subtype == 0) v_oldstyle->next->next->cur_speed = 32;
       
  2076 
       
  2077 			// set new position x,y,z
       
  2078 			SetAircraftPosition(v_oldstyle, gp.x, gp.y, GetAircraftFlyingAltitude(v_oldstyle));
       
  2079 		}
       
  2080 	}
       
  2081 }
       
  2082 
       
  2083 void UpdateAirplanesOnNewStation(Station *st)
       
  2084 {
       
  2085 	GetNewVehiclePosResult gp;
       
  2086 	Vehicle *v;
       
  2087 	byte takeofftype;
       
  2088 	uint16 cnt;
       
  2089 	// only 1 station is updated per function call, so it is enough to get entry_point once
       
  2090 	const AirportFTAClass *ap = GetAirport(st->airport_type);
       
  2091 	FOR_ALL_VEHICLES(v) {
       
  2092 		if (v->type == VEH_Aircraft && v->subtype <= 2) {
       
  2093 			if (v->u.air.targetairport == st->index) { // if heading to this airport
       
  2094 				/* update position of airplane. If plane is not flying, landing, or taking off
       
  2095 				 *you cannot delete airport, so it doesn't matter
       
  2096 				 */
       
  2097 				if (v->u.air.state >= FLYING) { // circle around
       
  2098 					v->u.air.pos = v->u.air.previous_pos = ap->entry_point;
       
  2099 					v->u.air.state = FLYING;
       
  2100 					// landing plane needs to be reset to flying height (only if in pause mode upgrade,
       
  2101 					// in normal mode, plane is reset in AircraftController. It doesn't hurt for FLYING
       
  2102 					GetNewVehiclePos(v, &gp);
       
  2103 					// set new position x,y,z
       
  2104 					SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
       
  2105 				} else {
       
  2106 					assert(v->u.air.state == ENDTAKEOFF || v->u.air.state == HELITAKEOFF);
       
  2107 					takeofftype = (v->subtype == 0) ? HELITAKEOFF : ENDTAKEOFF;
       
  2108 					// search in airportdata for that heading
       
  2109 					// easiest to do, since this doesn't happen a lot
       
  2110 					for (cnt = 0; cnt < ap->nofelements; cnt++) {
       
  2111 						if (ap->layout[cnt].heading == takeofftype) {
       
  2112 							v->u.air.pos = ap->layout[cnt].position;
       
  2113 							break;
       
  2114 						}
       
  2115 					}
       
  2116 				}
       
  2117 			}
       
  2118 		}
       
  2119 	}
       
  2120 }