src/aircraft_cmd.cpp
branchNewGRF_ports
changeset 6719 4cc327ad39d5
parent 6715 ce1614834bc3
child 6720 35756db7e577
--- a/src/aircraft_cmd.cpp	Tue Mar 27 23:27:27 2007 +0000
+++ b/src/aircraft_cmd.cpp	Sat Jun 02 19:59:29 2007 +0000
@@ -8,6 +8,7 @@
 #include "aircraft.h"
 #include "debug.h"
 #include "functions.h"
+#include "landscape.h"
 #include "station_map.h"
 #include "table/strings.h"
 #include "map.h"
@@ -33,6 +34,34 @@
 #include "cargotype.h"
 #include "airport_states.h"
 
+void Aircraft::UpdateDeltaXY(Direction direction)
+{
+	uint32 x;
+#define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
+	switch (this->subtype) {
+		default: NOT_REACHED();
+		case AIR_AIRCRAFT:
+		case AIR_HELICOPTER:
+			switch (this->u.air.state) {
+				case ENDTAKEOFF:
+				case LANDING:
+				case HELILANDING:
+				case FLYING:     x = MKIT(24, 24, -1, -1); break;
+				default:         x = MKIT( 2,  2, -1, -1); break;
+			}
+			this->z_height = 5;
+			break;
+		case AIR_SHADOW:     this->z_height = 1; x = MKIT(2,  2,  0,  0); break;
+		case AIR_ROTOR:      this->z_height = 1; x = MKIT(2,  2, -1, -1); break;
+	}
+#undef MKIT
+
+	this->x_offs        = GB(x,  0, 8);
+	this->y_offs        = GB(x,  8, 8);
+	this->sprite_width  = GB(x, 16, 8);
+	this->sprite_height = GB(x, 24, 8);
+}
+
 static bool AirportMove(Vehicle *v, const AirportFTAClass *apc);
 static bool AirportSetBlocks(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
 static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
@@ -170,8 +199,8 @@
 
 /** Get the size of the sprite of an aircraft sprite heading west (used for lists)
  * @param engine The engine to get the sprite from
- * @param &width The width of the sprite
- * @param &height The height of the sprite
+ * @param width The width of the sprite
+ * @param height The height of the sprite
  */
 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height)
 {
@@ -193,9 +222,9 @@
 	height = spr->height;
 }
 
-static int32 EstimateAircraftCost(const AircraftVehicleInfo *avi)
+static int32 EstimateAircraftCost(EngineID engine, const AircraftVehicleInfo *avi)
 {
-	return avi->base_cost * (_price.aircraft_base >> 3) >> 5;
+	return GetEngineProperty(engine, 0x0B, avi->base_cost) * (_price.aircraft_base >> 3) >> 5;
 }
 
 
@@ -203,7 +232,7 @@
  * Calculates cargo capacity based on an aircraft's passenger
  * and mail capacities.
  * @param cid Which cargo type to calculate a capacity for.
- * @param engine Which engine to find a cargo capacity for.
+ * @param avi Which engine to find a cargo capacity for.
  * @return New cargo capacity value.
  */
 uint16 AircraftDefaultCargoCapacity(CargoID cid, const AircraftVehicleInfo *avi)
@@ -233,10 +262,10 @@
  */
 int32 CmdBuildAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
-	if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_player)) return_cmd_error(STR_ENGINE_NOT_BUILDABLE);
+	if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_player)) return_cmd_error(STR_AIRCRAFT_NOT_AVAILABLE);
 
 	const AircraftVehicleInfo *avi = AircraftVehInfo(p1);
-	int32 value = EstimateAircraftCost(avi);
+	int32 value = EstimateAircraftCost(p1, avi);
 
 	/* to just query the cost, it is not neccessary to have a valid tile (automation/AI) */
 	if (flags & DC_QUERY_COST) return value;
@@ -264,7 +293,8 @@
 		Vehicle *u = vl[1]; // shadow
 
 		v->unitnumber = unit_num;
-		v->type = u->type = VEH_AIRCRAFT;
+		v = new (v) Aircraft();
+		u = new (u) Aircraft();
 		v->direction = DIR_SE;
 
 		v->owner = u->owner = _current_player;
@@ -281,15 +311,8 @@
 		u->z_pos = GetSlopeZ(x, y);
 		v->z_pos = u->z_pos + 1;
 
-		v->x_offs = v->y_offs = -1;
 //		u->delta_x = u->delta_y = 0;
 
-		v->sprite_width = v->sprite_height = 2;
-		v->z_height = 5;
-
-		u->sprite_width = u->sprite_height = 2;
-		u->z_height = 1;
-
 		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
 		u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
 
@@ -317,9 +340,11 @@
 		v->engine_type = p1;
 
 		v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
+		v->UpdateDeltaXY(INVALID_DIR);
 		v->value = value;
 
 		u->subtype = AIR_SHADOW;
+		u->UpdateDeltaXY(INVALID_DIR);
 
 		/* Danger, Will Robinson!
 		 * If the aircraft is refittable, but cannot be refitted to
@@ -388,6 +413,8 @@
 		v->vehicle_flags = 0;
 		if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SETBIT(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
 
+		UpdateAircraftCache(v);
+
 		VehiclePositionChanged(v);
 		VehiclePositionChanged(u);
 
@@ -397,15 +424,12 @@
 
 			u->next = w;
 
-			w->type = VEH_AIRCRAFT;
+			w = new (w) Aircraft();
 			w->direction = DIR_N;
 			w->owner = _current_player;
 			w->x_pos = v->x_pos;
 			w->y_pos = v->y_pos;
 			w->z_pos = v->z_pos + 5;
-			w->x_offs = w->y_offs = -1;
-			w->sprite_width = w->sprite_height = 2;
-			w->z_height = 1;
 			w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
 			w->spritenum = 0xFF;
 			w->subtype = AIR_ROTOR;
@@ -413,6 +437,7 @@
 			w->random_bits = VehicleRandomBits();
 			/* Use rotor's air.state to store the rotor animation frame */
 			w->u.air.state = HRS_ROTOR_STOPPED;
+			w->UpdateDeltaXY(INVALID_DIR);
 			VehiclePositionChanged(w);
 		}
 
@@ -564,6 +589,8 @@
 		}
 
 		if (flags & DC_EXEC) {
+			if (v->current_order.type == OT_LOADING) v->LeaveStation();
+
 			v->current_order.type = OT_GOTO_DEPOT;
 			v->current_order.flags = OF_NON_STOP;
 			if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
@@ -588,6 +615,7 @@
  * @param p2 various bitstuffed elements
  * - p2 = (bit 0-7) - the new cargo type to refit to
  * - p2 = (bit 8-15) - the new cargo subtype to refit to
+ * - p2 = (bit 16) - refit only this vehicle (ignored)
  * @return cost of refit or error
  */
 int32 CmdRefitAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
@@ -686,6 +714,7 @@
 	if (st->IsValid() && st->airport_tile != 0 && st->Airport()->terminals != NULL) {
 //		printf("targetairport = %d, st->index = %d\n", v->u.air.targetairport, st->index);
 //		v->u.air.targetairport = st->index;
+		if (v->current_order.type == OT_LOADING) v->LeaveStation();
 		v->current_order.type = OT_GOTO_DEPOT;
 		v->current_order.flags = OF_NON_STOP;
 		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
@@ -710,7 +739,7 @@
 
 	if (v->vehstatus & VS_STOPPED) return;
 
-	int32 cost = AircraftVehInfo(v->engine_type)->running_cost * _price.aircraft_running / 364;
+	int32 cost = GetVehicleProperty(v, 0x0E, AircraftVehInfo(v->engine_type)->running_cost) * _price.aircraft_running / 364;
 
 	v->profit_this_year -= cost >> 8;
 
@@ -857,6 +886,21 @@
 	}
 }
 
+
+void UpdateAircraftCache(Vehicle *v)
+{
+	uint max_speed = GetVehicleProperty(v, 0x0C, 0);
+	if (max_speed != 0) {
+		/* Convert from original units to (approx) km/h */
+		max_speed = (max_speed * 129) / 10;
+
+		v->u.air.cached_max_speed = max_speed;
+	} else {
+		v->u.air.cached_max_speed = 0xFFFF;
+	}
+}
+
+
 /**
  * Special velocities for aircraft
  */
@@ -880,6 +924,11 @@
 	uint spd = v->acceleration * 16;
 	byte t;
 
+	if (v->u.air.cached_max_speed < speed_limit) {
+		if (v->cur_speed < speed_limit) hard_limit = false;
+		speed_limit = v->u.air.cached_max_speed;
+	}
+
 	speed_limit = min(speed_limit, v->max_speed);
 
 	v->subspeed = (t=v->subspeed) + (byte)spd;
@@ -940,6 +989,42 @@
 }
 
 /**
+ * Find the entry point to an airport depending on direction which
+ * the airport is being approached from. Each airport can have up to
+ * four entry points for its approach system so that approaching
+ * aircraft do not fly through each other or are forced to do 180
+ * degree turns during the approach. The arrivals are grouped into
+ * four sectors dependent on the DiagDirection from which the airport
+ * is approached.
+ *
+ * @param v   The vehicle that is approaching the airport
+ * @param apc The Airport Class being approached.
+ * @returns   The index of the entry point
+ */
+static byte AircraftGetEntryPoint(const Vehicle *v, const AirportFTAClass *apc)
+{
+	assert(v != NULL);
+	assert(apc != NULL);
+
+	const Station *st = GetStation(v->u.air.targetairport);
+	/* Make sure we don't go to 0,0 if the airport has been removed. */
+	TileIndex tile = (st->airport_tile != 0) ? st->airport_tile : st->xy;
+
+	int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
+	int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
+
+	DiagDirection dir;
+	if (abs(delta_y) < abs(delta_x)) {
+		/* We are northeast or southwest of the airport */
+		dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
+	} else {
+		/* We are northwest or southeast of the airport */
+		dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
+	}
+	return apc->entry_points[dir];
+}
+
+/**
  * Controls the movement of an aircraft. This function actually moves the vehicle
  * on the map and takes care of minor things like sound playback.
  * @todo    De-mystify the cur_speed values for helicopter rotors.
@@ -950,17 +1035,24 @@
 {
 	int count;
 	const Station *st = GetStation(v->u.air.targetairport);
+	const AirportFTAClass *afc = st->Airport();
+	const AirportMovingData *amd;
 
 	/* prevent going to 0,0 if airport is deleted. */
 	TileIndex tile = st->airport_tile;
-	if (tile == 0) tile = st->xy;
+	if (tile == 0) {
+		tile = st->xy;
+
+		/* Jump into our "holding pattern" state machine if possible */
+	if (v->u.air.pos >= afc->nofelements) v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, afc);
+	}
+
+	/*  get airport moving data */
+	amd = afc->MovingData(v->u.air.pos);
+
 	int x = TileX(tile) * TILE_SIZE;
 	int y = TileY(tile) * TILE_SIZE;
 
-	/*  get airport moving data */
-	const AirportFTAClass *afc = st->Airport();
-	const AirportMovingData *amd = afc->MovingData(v->u.air.pos);
-
 	/* Helicopter raise */
 	if (amd->flag & AMED_HELI_RAISE) {
 		Vehicle *u = v->next->next;
@@ -995,6 +1087,7 @@
 				 * helicopter will circle until sign disappears, then go to next order
 				 * what to do when it is the only order left, right now it just stays in 1 place */
 				v->u.air.state = FLYING;
+				UpdateAircraftCache(v);
 				AircraftNextAirportPos_and_Order(v);
 				return false;
 			}
@@ -1109,6 +1202,7 @@
 			if (st->airport_tile == 0) {
 				/* Airport has been removed, abort the landing procedure */
 				v->u.air.state = FLYING;
+				UpdateAircraftCache(v);
 				AircraftNextAirportPos_and_Order(v);
 				/* get aircraft back on running altitude */
 				SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
@@ -1311,49 +1405,11 @@
 	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
 }
 
-/** Mark all views dirty for an aircraft.
- * @param v vehicle to be redrawn.
- */
-static void MarkAircraftDirty(Vehicle *v)
-{
-		v->cur_image = GetAircraftImage(v, v->direction);
-		if (v->subtype == AIR_HELICOPTER) v->next->next->cur_image = GetRotorImage(v);
-		MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
-}
-
-static void HandleAircraftLoading(Vehicle *v, int mode)
+void Aircraft::MarkDirty()
 {
-	switch (v->current_order.type) {
-		case OT_LOADING: {
-			if (mode != 0) return;
-			if (--v->load_unload_time_rem != 0) return;
-
-			if (CanFillVehicle(v) && (
-						v->current_order.flags & OF_FULL_LOAD ||
-						(_patches.gradual_loading && !HASBIT(v->vehicle_flags, VF_LOADING_FINISHED))
-					)) {
-				SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC);
-				if (LoadUnloadVehicle(v, false)) {
-					InvalidateWindow(WC_AIRCRAFT_LIST, v->owner);
-					MarkAircraftDirty(v);
-				}
-				return;
-			}
-
-			Order b = v->current_order;
-			v->current_order.Free();
-			MarkAircraftDirty(v);
-			if (!(b.flags & OF_NON_STOP)) return;
-			break;
-		}
-
-		case OT_DUMMY: break;
-
-		default: return;
-	}
-
-	v->cur_order_index++;
-	InvalidateVehicleOrder(v);
+		this->cur_image = GetAircraftImage(this, this->direction);
+		if (this->subtype == AIR_HELICOPTER) this->next->next->cur_image = GetRotorImage(this);
+		MarkAllViewportsDirty(this->left_coord, this->top_coord, this->right_coord + 1, this->bottom_coord + 1);
 }
 
 static void CrashAirplane(Vehicle *v)
@@ -1435,73 +1491,19 @@
 			0);
 	}
 
-	Order old_order = v->current_order;
 	v->BeginLoading();
-	v->current_order.flags = 0;
-
-	if (old_order.type == OT_GOTO_STATION &&
-			v->current_order.dest == v->last_station_visited) {
-		v->current_order.flags =
-			(old_order.flags & (OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER)) | OF_NON_STOP;
-	}
-
-	SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC);
-	LoadUnloadVehicle(v, true);
-	MarkAircraftDirty(v);
-	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
-	InvalidateWindowClasses(WC_AIRCRAFT_LIST);
-}
-
-static void AircraftLand(Vehicle *v)
-{
-	v->sprite_width = v->sprite_height = 2;
 }
 
 static void AircraftLandAirplane(Vehicle *v)
 {
-	AircraftLand(v);
+	v->UpdateDeltaXY(INVALID_DIR);
+
 	if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
 		SndPlayVehicleFx(SND_17_SKID_PLANE, v);
 	}
 	MaybeCrashAirplane(v);
 }
 
-/**
- * Find the entry point to an airport depending on direction which
- * the airport is being approached from. Each airport can have up to
- * four entry points for its approach system so that approaching
- * aircraft do not fly through each other or are forced to do 180
- * degree turns during the approach. The arrivals are grouped into
- * four sectors dependent on the DiagDirection from which the airport
- * is approached.
- *
- * @param v   The vehicle that is approaching the airport
- * @param apc The Airport Class being approached.
- * @returns   The index of the entry point
- */
-static byte AircraftGetEntryPoint(const Vehicle *v, const AirportFTAClass *apc)
-{
-	assert(v != NULL);
-	assert(apc != NULL);
-
-	const Station *st = GetStation(v->u.air.targetairport);
-	/* Make sure we don't go to 0,0 if the airport has been removed. */
-	TileIndex tile = (st->airport_tile != 0) ? st->airport_tile : st->xy;
-
-	int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
-	int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
-
-	DiagDirection dir;
-	if (abs(delta_y) < abs(delta_x)) {
-		/* We are northeast or southwest of the airport */
-		dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
-	} else {
-		/* We are northwest or southeast of the airport */
-		dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
-	}
-	return apc->entry_points[dir];
-}
-
 
 /** set the right pos when heading to other airports after takeoff */
 static void AircraftNextAirportPos_and_Order(Vehicle *v)
@@ -1654,8 +1656,8 @@
 
 static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *apc)
 {
-	v->sprite_width = v->sprite_height = 24; // ??? no idea what this is
 	v->u.air.state = ENDTAKEOFF;
+	v->UpdateDeltaXY(INVALID_DIR);
 }
 
 static void AircraftEventHandler_EndTakeOff(Vehicle *v, const AirportFTAClass *apc)
@@ -1668,15 +1670,16 @@
 static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *apc)
 {
 	const Player* p = GetPlayer(v->owner);
-	v->sprite_width = v->sprite_height = 24; // ??? no idea what this is
 	v->u.air.state = FLYING;
+	v->UpdateDeltaXY(INVALID_DIR);
+
 	/* get the next position to go to, differs per airport */
 	AircraftNextAirportPos_and_Order(v);
 
 	/* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
 	 * unless it is due for renewal but the engine is no longer available */
 	if (v->owner == _local_player && (
-				EngineHasReplacementForPlayer(p, v->engine_type) ||
+				EngineHasReplacementForPlayer(p, v->engine_type, v->group_id) ||
 				((p->engine_renew && v->age - v->max_age > p->engine_renew_months * 30) &&
 				HASBIT(GetEngine(v->engine_type)->player_avail, _local_player))
 			)) {
@@ -1727,13 +1730,14 @@
 
 static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *apc)
 {
+	v->u.air.state = ENDLANDING;
 	AircraftLandAirplane(v);  // maybe crash airplane
-	v->u.air.state = ENDLANDING;
+
 	/* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
 	if (v->current_order.type != OT_GOTO_DEPOT && v->owner == _local_player) {
 		/* only the vehicle owner needs to calculate the rest (locally) */
 		const Player* p = GetPlayer(v->owner);
-		if (EngineHasReplacementForPlayer(p, v->engine_type) ||
+		if (EngineHasReplacementForPlayer(p, v->engine_type, v->group_id) ||
 			(p->engine_renew && v->age - v->max_age > (p->engine_renew_months * 30))) {
 			/* send the aircraft to the hangar at next airport */
 			_current_player = _local_player;
@@ -1745,8 +1749,8 @@
 
 static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *apc)
 {
-	AircraftLand(v); // helicopters don't crash
 	v->u.air.state = HELIENDLANDING;
+	v->UpdateDeltaXY(INVALID_DIR);
 }
 
 static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *apc)
@@ -1844,8 +1848,10 @@
 	/* we have arrived in an important state (eg terminal, hangar, etc.) */
 	if (current->heading == v->u.air.state) {
 		byte prev_pos = v->u.air.pos; // location could be changed in state, so save it before-hand
+		byte prev_state = v->u.air.state;
 		_aircraft_state_handlers[v->u.air.state](v, apc);
 		if (v->u.air.state != FLYING) v->u.air.previous_pos = prev_pos;
+		if (v->u.air.state != prev_state || v->u.air.pos != prev_pos) UpdateAircraftCache(v);
 		return true;
 	}
 
@@ -1855,6 +1861,7 @@
 	if (current->next == NULL) {
 		if (AirportSetBlocks(v, current, apc)) {
 			v->u.air.pos = current->next_position;
+			UpdateAircraftCache(v);
 		} // move to next position
 		return false;
 	}
@@ -1865,6 +1872,7 @@
 		if (v->u.air.state == current->heading || current->heading == TO_ALL) {
 			if (AirportSetBlocks(v, current, apc)) {
 				v->u.air.pos = current->next_position;
+				UpdateAircraftCache(v);
 			} // move to next position
 			return false;
 		}
@@ -1902,9 +1910,9 @@
 }
 
 /**
- * ...
+ * "reserve" a block for the plane
  * @param v airplane that requires the operation
- * @param currentpos of the vehicle in the list of blocks
+ * @param current_pos of the vehicle in the list of blocks
  * @param apc airport on which block is requsted to be set
  * @returns true on success. Eg, next block was free and we have occupied it
  */
@@ -2091,7 +2099,7 @@
 
 	HandleAircraftSmoke(v);
 	ProcessAircraftOrder(v);
-	HandleAircraftLoading(v, loop);
+	v->HandleLoading(loop != 0);
 
 	if (v->current_order.type >= OT_LOADING) return;
 
@@ -2150,6 +2158,10 @@
 	}
 }
 
+/**
+ * Updates the status of the Aircraft heading or in the station
+ * @param st Station been updated
+ */
 void UpdateAirplanesOnNewStation(const Station *st)
 {
 	/* only 1 station is updated per function call, so it is enough to get entry_point once */
@@ -2160,10 +2172,11 @@
 		if (v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) {
 			if (v->u.air.targetairport == st->index) { // if heading to this airport
 				/* update position of airplane. If plane is not flying, landing, or taking off
-				 *you cannot delete airport, so it doesn't matter */
+				 * you cannot delete airport, so it doesn't matter */
 				if (v->u.air.state >= FLYING) { // circle around
 					v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, ap);
 					v->u.air.state = FLYING;
+					UpdateAircraftCache(v);
 					/* landing plane needs to be reset to flying height (only if in pause mode upgrade,
 					 * in normal mode, plane is reset in AircraftController. It doesn't hurt for FLYING */
 					GetNewVehiclePosResult gp = GetNewVehiclePos(v);
@@ -2177,6 +2190,7 @@
 					for (uint cnt = 0; cnt < ap->nofelements; cnt++) {
 						if (ap->layout[cnt].heading == takeofftype) {
 							v->u.air.pos = ap->layout[cnt].position;
+							UpdateAircraftCache(v);
 							break;
 						}
 					}