changeset 13962:9b98b4f050e5 draft

(svn r18498) -Fix [FS#2616]: cloning of vehicles could create vehicles with invalid cargo sub types for the build year of the vehicle. Fall back to another cargo sub type with the exact same name, otherwise fallback to cargo sub type 0.
author rubidium <rubidium@openttd.org>
date Mon, 14 Dec 2009 19:17:18 +0000
parents e0c6b22f65c7
children 62925b6feb3b
files src/vehicle_cmd.cpp src/vehicle_func.h src/vehicle_gui.cpp
diffstat 3 files changed, 68 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/vehicle_cmd.cpp
+++ b/src/vehicle_cmd.cpp
@@ -536,8 +536,10 @@
 			if (flags & DC_EXEC) {
 				assert(w != NULL);
 
-				if (w->cargo_type != v->cargo_type || w->cargo_subtype != v->cargo_subtype) {
-					CommandCost cost = DoCommand(0, w->index, v->cargo_type | (v->cargo_subtype << 8) | 1U << 16, flags, GetCmdRefitVeh(v));
+				/* Find out what's the best sub type */
+				byte subtype = GetBestFittingSubType(v, w);
+				if (w->cargo_type != v->cargo_type || w->cargo_subtype != subtype) {
+					CommandCost cost = DoCommand(0, w->index, v->cargo_type | (subtype << 8) | 1U << 16, flags, GetCmdRefitVeh(v));
 					if (CmdSucceeded(cost)) total_cost.AddCost(cost);
 				}
 
--- a/src/vehicle_func.h
+++ b/src/vehicle_func.h
@@ -42,6 +42,7 @@
 void ResetVehiclePosHash();
 void ResetVehicleColourMap();
 
+byte GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for);
 CommandCost RefitVehicle(Vehicle *v, bool only_this, CargoID new_cid, byte new_subtype, DoCommandFlag flags);
 
 void ViewportAddVehicles(DrawPixelInfo *dpi);
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -159,6 +159,69 @@
 /** Maximum number of refit cycles we try, to prevent infinite loops. */
 static const int MAX_REFIT_CYCLE = 16;
 
+/**
+ * Get the best fitting subtype when 'cloning'/'replacing' v_from with v_for.
+ * Assuming they are going to carry the same cargo ofcourse!
+ * @param v_from the vehicle to match the subtype from
+ * @param v_for  the vehicle to get the subtype for
+ * @return the best sub type
+ */
+byte GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for)
+{
+	const Engine *e_from = Engine::Get(v_from->engine_type);
+	const Engine *e_for  = Engine::Get(v_for->engine_type);
+
+	/* If one them doesn't carry cargo, there's no need to find a sub type */
+	if (!e_from->CanCarryCargo() || !e_for->CanCarryCargo()) return 0;
+
+	if (!HasBit(e_from->info.callback_mask, CBM_VEHICLE_CARGO_SUFFIX) ||
+			!HasBit(e_for->info.callback_mask,  CBM_VEHICLE_CARGO_SUFFIX)) {
+		/* One of the engines doesn't have cargo suffixes, i.e. sub types. */
+		return 0;
+	}
+
+	/* It has to be possible for v_for to carry the cargo of v_from. */
+	assert(HasBit(e_for->info.refit_mask, v_from->cargo_type));
+
+	StringID expected_string = GetCargoSubtypeText(v_from);
+
+	CargoID old_cargo_type = v_for->cargo_type;
+	byte old_cargo_subtype = v_for->cargo_subtype;
+	byte ret_refit_cyc = 0;
+
+	/* Set the 'destination' cargo */
+	v_for->cargo_type = v_from->cargo_type;
+
+	/* Cycle through the refits */
+	for (byte refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
+		v_for->cargo_subtype = refit_cyc;
+
+		/* Make sure we don't pick up anything cached. */
+		v_for->First()->InvalidateNewGRFCache();
+		v_for->InvalidateNewGRFCache();
+		uint16 callback = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v_for->engine_type, v_for);
+
+		if (callback == 0xFF) callback = CALLBACK_FAILED;
+		if (callback == CALLBACK_FAILED) break;
+
+		if (GetCargoSubtypeText(v_for) != expected_string) continue;
+
+		/* We found something matching. */
+		ret_refit_cyc = refit_cyc;
+		break;
+	}
+
+	/* Reset the vehicle's cargo type */
+	v_for->cargo_type    = old_cargo_type;
+	v_for->cargo_subtype = old_cargo_subtype;
+
+	/* Make sure we don't taint the vehicle. */
+	v_for->First()->InvalidateNewGRFCache();
+	v_for->InvalidateNewGRFCache();
+
+	return ret_refit_cyc;
+}
+
 struct RefitOption {
 	CargoID cargo;
 	byte subtype;