src/vehicle.cpp
branchNewGRF_ports
changeset 10184 fcf5fb2548eb
parent 6878 7d1ff2f621c7
child 10192 195d7f6dcf71
--- a/src/vehicle.cpp	Mon Apr 14 20:32:36 2008 +0000
+++ b/src/vehicle.cpp	Tue Apr 15 00:47:19 2008 +0000
@@ -13,15 +13,13 @@
 #include "timetable.h"
 #include "viewport_func.h"
 #include "gfx_func.h"
-#include "news.h"
+#include "news_func.h"
 #include "command_func.h"
 #include "saveload.h"
 #include "player_func.h"
-#include "engine.h"
 #include "debug.h"
 #include "vehicle_gui.h"
 #include "depot.h"
-#include "station.h"
 #include "rail_type.h"
 #include "train.h"
 #include "aircraft.h"
@@ -34,7 +32,7 @@
 #include "newgrf_engine.h"
 #include "newgrf_sound.h"
 #include "group.h"
-#include "order.h"
+#include "order_func.h"
 #include "strings_func.h"
 #include "zoom_func.h"
 #include "functions.h"
@@ -48,6 +46,7 @@
 #include "autoreplace_gui.h"
 #include "string_func.h"
 #include "settings_type.h"
+#include "oldpool_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -119,22 +118,27 @@
 	InvalidateWindow(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
 }
 
-bool VehicleNeedsService(const Vehicle *v)
+bool Vehicle::NeedsServicing() const
 {
-	if (v->vehstatus & (VS_STOPPED | VS_CRASHED))       return false;
-	if (v->current_order.type != OT_GOTO_DEPOT || !(v->current_order.flags & OFB_PART_OF_ORDERS)) { // Don't interfere with a depot visit by the order list
-		if (_patches.gotodepot && VehicleHasDepotOrders(v)) return false;
-		if (v->current_order.type == OT_LOADING)            return false;
-		if (v->current_order.type == OT_GOTO_DEPOT && v->current_order.flags & OFB_HALT_IN_DEPOT) return false;
-	}
+	if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
 
 	if (_patches.no_servicing_if_no_breakdowns && _opt.diff.vehicle_breakdowns == 0) {
-		return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type, v->group_id);  /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */
+		/* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off.
+		 * Note: If servicing is enabled, we postpone replacement till next service. */
+		return EngineHasReplacementForPlayer(GetPlayer(this->owner), this->engine_type, this->group_id);
 	}
 
 	return _patches.servint_ispercent ?
-		(v->reliability < GetEngine(v->engine_type)->reliability * (100 - v->service_interval) / 100) :
-		(v->date_of_last_service + v->service_interval < _date);
+		(this->reliability < GetEngine(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
+		(this->date_of_last_service + this->service_interval < _date);
+}
+
+bool Vehicle::NeedsAutomaticServicing() const
+{
+	if (_patches.gotodepot && VehicleHasDepotOrders(this)) return false;
+	if (this->current_order.IsType(OT_LOADING))            return false;
+	if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotActionType() & ODATFB_HALT) return false;
+	return NeedsServicing();
 }
 
 StringID VehicleInTheWayErrMsg(const Vehicle* v)
@@ -633,7 +637,7 @@
 {
 	/* We need to set v->leave_depot_instantly as we have no control of it's contents at this time.
 	 * Vehicle should stop in the depot if it was in 'stopping' state - train intered depot while slowing down. */
-	if ((HasBit(v->current_order.flags, OF_HALT_IN_DEPOT) && !HasBit(v->current_order.flags, OF_PART_OF_ORDERS) && v->current_order.type == OT_GOTO_DEPOT) ||
+	if (((v->current_order.GetDepotActionType() & ODATFB_HALT) && !(v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) && v->current_order.IsType(OT_GOTO_DEPOT)) ||
 			(v->vehstatus & VS_STOPPED)) {
 		/* we keep the vehicle in the depot since the user ordered it to stay */
 		v->leave_depot_instantly = false;
@@ -769,7 +773,7 @@
 	}
 
 	AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
-		v->sprite_width, v->sprite_height, v->z_height, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
+		v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
 }
 
 void ViewportAddVehicles(DrawPixelInfo *dpi)
@@ -1544,7 +1548,7 @@
 
 	SetDParam(0, _vehicle_type_names[v->type]);
 	SetDParam(1, v->unitnumber);
-	AddNewsItem(msg, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
+	AddNewsItem(msg, NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0);
 }
 
 void AgeVehicle(Vehicle *v)
@@ -1687,24 +1691,28 @@
 }
 
 /** Autoreplace all vehicles in the depot
+ * Note: this command can make incorrect cost estimations
+ * Luckily the final price can only drop, not increase. This is due to the fact that
+ * estimation can't predict wagon removal so it presumes worst case which is no income from selling wagons.
  * @param tile Tile of the depot where the vehicles are
  * @param flags type of operation
  * @param p1 Type of vehicle
- * @param p2 Unused
+ * @param p2 If bit 0 is set, then either replace all or nothing (instead of replacing until money runs out)
  */
 CommandCost CmdDepotMassAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	Vehicle **vl = NULL;
 	uint16 engine_list_length = 0;
 	uint16 engine_count = 0;
-	uint i, x = 0, y = 0, z = 0;
-	CommandCost cost;
+	uint i;
+	CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES);
 	VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
+	bool all_or_nothing = HasBit(p2, 0);
 
 	if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_player)) return CMD_ERROR;
 
 	/* Get the list of vehicles in the depot */
-	BuildDepotVehicleList(vehicle_type, tile, &vl, &engine_list_length, &engine_count, NULL, NULL, NULL);
+	BuildDepotVehicleList(vehicle_type, tile, &vl, &engine_list_length, &engine_count, &vl, &engine_list_length, &engine_count);
 
 
 	for (i = 0; i < engine_count; i++) {
@@ -1715,10 +1723,6 @@
 		/* Ensure that the vehicle completely in the depot */
 		if (!v->IsInDepot()) continue;
 
-		x = v->x_pos;
-		y = v->y_pos;
-		z = v->z_pos;
-
 		if (stopped) {
 			v->vehstatus |= VS_STOPPED; // Stop the vehicle
 			v->leave_depot_instantly = true;
@@ -1727,24 +1731,25 @@
 
 		if (CmdSucceeded(ret)) {
 			cost.AddCost(ret);
-			if (!(flags & DC_EXEC)) break;
-			/* There is a problem with autoreplace and newgrf
-			 * It's impossible to tell the length of a train after it's being replaced before it's actually done
-			 * Because of this, we can't estimate costs due to wagon removal and we will have to always return 0 and pay manually
-			 * Since we pay after each vehicle is replaced and MaybeReplaceVehicle() check if the player got enough money
-			 * we should never reach a condition where the player will end up with negative money from doing this */
-			SubtractMoneyFromPlayer(ret);
+		} else {
+			if (all_or_nothing) {
+				/* We failed to replace a vehicle even though we set all or nothing.
+				 * We should never reach this if DC_EXEC is set since then it should
+				 * have failed the estimation guess. */
+				assert(!(flags & DC_EXEC));
+				/* Now we will have to return an error.
+				 * This goto will leave the loop and it's ok to do so because
+				 * there is no point in the rest of the loop. */
+				goto error;
+			}
 		}
 	}
 
 	if (cost.GetCost() == 0) {
+error:
+		/* Either we didn't replace anything or something went wrong.
+		 * Either way we want to return an error and not execute this command. */
 		cost = CMD_ERROR;
-	} else {
-		if (flags & DC_EXEC) {
-			/* Display the cost animation now that DoCommandP() can't do it for us (see previous comments) */
-			if (IsLocalPlayer()) ShowCostOrIncomeAnimation(x, y, z, cost.GetCost());
-		}
-		cost = CommandCost();
 	}
 
 	free(vl);
@@ -2034,7 +2039,7 @@
 					const Order *order;
 
 					FOR_VEHICLE_ORDERS(v, order) {
-						if (order->type == OT_GOTO_STATION && order->dest == index) {
+						if (order->IsType(OT_GOTO_STATION) && order->GetDestination() == index) {
 							if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 50);
 							(*sort_list)[n++] = v;
 							break;
@@ -2078,7 +2083,7 @@
 					const Order *order;
 
 					FOR_VEHICLE_ORDERS(v, order) {
-						if (order->type == OT_GOTO_DEPOT && order->dest == index) {
+						if (order->IsType(OT_GOTO_DEPOT) && order->GetDestination() == index) {
 							if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 25);
 							(*sort_list)[n++] = v;
 							break;
@@ -2175,7 +2180,7 @@
 		max += v->cargo_cap;
 		if (v->cargo_cap != 0) {
 			unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
-			loading |= (u->current_order.flags & OFB_UNLOAD) == 0 && st->goods[v->cargo_type].days_since_pickup != 255;
+			loading |= !(u->current_order.GetUnloadType() & OUFB_UNLOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
 			cars++;
 		}
 	}
@@ -2236,20 +2241,19 @@
 
 	TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
 
-	if (v->current_order.type == OT_GOTO_DEPOT) {
+	if (v->current_order.IsType(OT_GOTO_DEPOT)) {
 		Order t;
 
 		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
 		t = v->current_order;
-		v->current_order.type = OT_DUMMY;
-		v->current_order.flags = 0;
-
-		if (t.refit_cargo < NUM_CARGO) {
+		v->current_order.MakeDummy();
+
+		if (t.IsRefit()) {
 			CommandCost cost;
 
 			_current_player = v->owner;
-			cost = DoCommand(v->tile, v->index, t.refit_cargo | t.refit_subtype << 8, DC_EXEC, GetCmdRefitVeh(v));
+			cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
 
 			if (CmdFailed(cost)) {
 				v->leave_depot_instantly = false; // We ensure that the vehicle stays in the depot
@@ -2257,18 +2261,18 @@
 					/* Notify the user that we stopped the vehicle */
 					SetDParam(0, _vehicle_type_names[v->type]);
 					SetDParam(1, v->unitnumber);
-					AddNewsItem(STR_ORDER_REFIT_FAILED, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
+					AddNewsItem(STR_ORDER_REFIT_FAILED, NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0);
 				}
 			} else if (v->owner == _local_player && cost.GetCost() != 0) {
 				ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
 			}
 		}
 
-		if (HasBit(t.flags, OF_PART_OF_ORDERS)) {
+		if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
 			/* Part of orders */
 			UpdateVehicleTimetable(v, true);
 			v->cur_order_index++;
-		} else if (HasBit(t.flags, OF_HALT_IN_DEPOT)) {
+		} else if (t.GetDepotActionType() & ODATFB_HALT) {
 			/* Force depot visit */
 			v->vehstatus |= VS_STOPPED;
 			if (v->owner == _local_player) {
@@ -2283,7 +2287,7 @@
 				}
 
 				SetDParam(0, v->unitnumber);
-				AddNewsItem(string, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
+				AddNewsItem(string, NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0);
 			}
 		}
 	}
@@ -3057,7 +3061,7 @@
 }
 
 /** Will be called when vehicles need to be loaded. */
-static void Load_VEHS()
+void Load_VEHS()
 {
 	int index;
 	Vehicle *v;
@@ -3100,7 +3104,7 @@
 		if (CheckSavegameVersion(5)) {
 			/* Convert the current_order.type (which is a mix of type and flags, because
 			 *  in those versions, they both were 4 bits big) to type and flags */
-			v->current_order.flags = (v->current_order.type & 0xF0) >> 4;
+			v->current_order.flags = GB(v->current_order.type, 4, 4);
 			v->current_order.type.m_val &= 0x0F;
 		}
 
@@ -3134,25 +3138,22 @@
 {
 	assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
 
-	if (this->current_order.type == OT_GOTO_STATION &&
-			this->current_order.dest == this->last_station_visited) {
-		/* Arriving at the ordered station.
-		 * Keep the load/unload flags, as we (obviously) still need them. */
-		this->current_order.flags &= OFB_FULL_LOAD | OFB_UNLOAD | OFB_TRANSFER;
-
+	if (this->current_order.IsType(OT_GOTO_STATION) &&
+			this->current_order.GetDestination() == this->last_station_visited) {
 		/* Furthermore add the Non Stop flag to mark that this station
 		 * is the actual destination of the vehicle, which is (for example)
 		 * necessary to be known for HandleTrainLoading to determine
 		 * whether the train is lost or not; not marking a train lost
 		 * that arrives at random stations is bad. */
-		this->current_order.flags |= OFB_NON_STOP;
+		this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
+
+		current_order.MakeLoading(true);
 		UpdateVehicleTimetable(this, true);
 	} else {
-		/* This is just an unordered intermediate stop */
-		this->current_order.flags = 0;
+		this->current_order.SetNonStopType(ONSF_STOP_EVERYWHERE);
+		current_order.MakeLoading(false);
 	}
 
-	current_order.type = OT_LOADING;
 	GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
 
 	VehiclePayment(this);
@@ -3168,13 +3169,12 @@
 
 void Vehicle::LeaveStation()
 {
-	assert(current_order.type == OT_LOADING);
+	assert(current_order.IsType(OT_LOADING));
 
 	/* Only update the timetable if the vehicle was supposed to stop here. */
-	if (current_order.flags & OFB_NON_STOP) UpdateVehicleTimetable(this, false);
-
-	current_order.type = OT_LEAVESTATION;
-	current_order.flags = 0;
+	if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
+
+	current_order.MakeLeaveStation();
 	GetStation(this->last_station_visited)->loading_vehicles.remove(this);
 
 	HideFillingPercent(this->fill_percent_te_id);
@@ -3184,7 +3184,7 @@
 
 void Vehicle::HandleLoading(bool mode)
 {
-	switch (this->current_order.type) {
+	switch (this->current_order.GetType()) {
 		case OT_LOADING: {
 			uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
 
@@ -3194,11 +3194,11 @@
 
 			this->PlayLeaveStationSound();
 
-			Order b = this->current_order;
+			bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
 			this->LeaveStation();
 
 			/* If this was not the final order, don't remove it from the list. */
-			if (!(b.flags & OFB_NON_STOP)) return;
+			if (!at_destination_station) return;
 			break;
 		}
 
@@ -3237,9 +3237,9 @@
 {
 	this->x_offs        = 0;
 	this->y_offs        = 0;
-	this->sprite_width  = 1;
-	this->sprite_height = 1;
-	this->z_height      = 1;
+	this->x_extent      = 1;
+	this->y_extent      = 1;
+	this->z_extent      = 1;
 }
 
 void StopAllVehicles()