src/vehicle.cpp
branchnoai
changeset 10455 22c441f5adf9
parent 10370 fa4f88090694
child 10513 33cb70ff2f5d
--- a/src/vehicle.cpp	Mon May 05 12:35:38 2008 +0000
+++ b/src/vehicle.cpp	Wed May 07 21:09:51 2008 +0000
@@ -1,6 +1,6 @@
 /* $Id$ */
 
-/** @file vehicle.cpp */
+/** @file vehicle.cpp Base implementations of all vehicles. */
 
 #include "stdafx.h"
 #include "openttd.h"
@@ -592,7 +592,7 @@
 	}
 
 	Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
-	if (WP(w, vp_d).follow_vehicle == this->index) {
+	if (w != NULL && WP(w, vp_d).follow_vehicle == this->index) {
 		ScrollMainWindowTo(this->x_pos, this->y_pos, true); // lock the main view on the vehicle's last position
 		WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
 	}
@@ -713,12 +713,7 @@
 				v->leave_depot_instantly = false;
 				v->vehstatus &= ~VS_STOPPED;
 			}
-
-			CommandCost cost = MaybeReplaceVehicle(v, 0, true);
-			if (CmdSucceeded(cost) && cost.GetCost() != 0) {
-				/* Looks like we can replace this vehicle so we go ahead and do so */
-				MaybeReplaceVehicle(v, DC_EXEC, true);
-			}
+			MaybeReplaceVehicle(v, DC_EXEC, true);
 			v = w;
 		}
 		_current_player = OWNER_NONE;
@@ -1992,9 +1987,9 @@
 	/* We can build vehicle infrastructure when we may build the vehicle type */
 	if (max > 0) {
 		/* Can we actually build the vehicle type? */
-		EngineID e;
-		FOR_ALL_ENGINEIDS_OF_TYPE(e, type) {
-			if (HasBit(GetEngine(e)->player_avail, _local_player)) return true;
+		const Engine *e;
+		FOR_ALL_ENGINES_OF_TYPE(e, type) {
+			if (HasBit(e->player_avail, _local_player)) return true;
 		}
 		return false;
 	}
@@ -2691,6 +2686,154 @@
 	}
 }
 
+/** Backs up a chain of vehicles
+ * @param v The vehicle to back up
+ */
+void BackuppedVehicle::BackupVehicle(Vehicle *v)
+{
+	int length = CountVehiclesInChain(v);
+
+	uint cargo_packages_count = 1;
+	for (const Vehicle *v_count = v; v_count != NULL; v_count=v_count->Next()) {
+		/* Now we count how many cargo packets we need to store.
+		 * We started with an offset by one because we also need an end of array marker. */
+		cargo_packages_count += v_count->cargo.packets.size();
+	}
+
+	vehicles = MallocT<Vehicle>(length);
+	cargo_packets = MallocT<CargoPacket>(cargo_packages_count);
+
+	/* Now we make some pointers to iterate over the arrays. */
+	Vehicle *copy = vehicles;
+	CargoPacket *cargo = cargo_packets;
+
+	Vehicle *original = v;
+
+	for (; 0 < length; original = original->Next(), copy++, length--) {
+		/* First we need to copy the vehicle itself.
+		 * However there is an issue as the cargo list isn't copied.
+		 * To avoid restoring invalid pointers we start by swapping the cargo list with an empty one. */
+		CargoList::List empty_packets;
+		original->cargo.packets.swap(empty_packets);
+		memcpy(copy, original, sizeof(Vehicle));
+
+		/* No need to do anything else if the cargo list is empty.
+		 * It really doesn't matter if we swap an empty list with an empty list. */
+		if (original->cargo.Empty()) continue;
+
+		/* And now we swap the cargo lists back. The vehicle now has it's cargo again. */
+		original->cargo.packets.swap(empty_packets);
+
+		/* The vehicle contains some cargo so we will back up the cargo as well.
+		 * We only need to store the packets and not which vehicle they came from.
+		 * We will still be able to put them together with the right vehicle when restoring. */
+		const CargoList::List *packets = original->cargo.Packets();
+		for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
+			memcpy(cargo, (*it), sizeof(CargoPacket));
+			cargo++;
+		}
+	}
+	/* We should end with a 0 packet so restoring can detect the end of the array. */
+	memset(cargo, 0, sizeof(CargoPacket));
+}
+
+/** Restore a backed up row of vehicles
+ * @param *v The array of vehicles to restore
+ * @param *p The owner of the vehicle
+ */
+Vehicle* BackuppedVehicle::RestoreBackupVehicle(Vehicle *v, Player *p)
+{
+	Vehicle *backup = v;
+	CargoPacket *cargo = cargo_packets;
+
+	assert(v->owner == p->index);
+
+	/* Cache the result of the vehicle type check since it will not change
+	 * and we need this check once for every run though the loop. */
+	bool is_road_veh = v->type == VEH_ROAD;
+
+	while (true) {
+		Vehicle *dest = GetVehicle(backup->index);
+		/* The vehicle should be free since we are restoring something we just sold. */
+		assert(!dest->IsValid());
+		memcpy(dest, backup, sizeof(Vehicle));
+
+		/* We decreased the engine count when we sold the engines so we will increase it again. */
+		if (IsEngineCountable(backup)) p->num_engines[backup->engine_type]++;
+
+		/* Update hash. */
+		Vehicle *dummy = dest;
+		dest->old_new_hash = &dummy;
+		dest->left_coord = INVALID_COORD;
+		UpdateVehiclePosHash(dest, INVALID_COORD, 0);
+
+		if (is_road_veh) {
+			/* Removed the slot in the road vehicles as the slot is gone.
+			 * We don't want a pointer to a slot that's gone.              */
+			dest->u.road.slot = NULL;
+		}
+
+		if (!dest->cargo.Empty()) {
+			/* The vehicle in question contains some cargo.
+			 * However we lost the list so we will have to recreate it.
+			 * We know that the packets are stored in the same order as the vehicles so
+			 * the one cargo_packets points to and maybe some following ones belongs to
+			 * the current vehicle.
+			 * Now all we have to do is to add the packets to a list and keep track of how
+			 * much cargo we restore and once we reached the cached cargo hold we recovered
+			 * everything for this vehicle. */
+			uint cargo_count = 0;
+			for(; cargo_count < dest->cargo.Count(); cargo++) {
+				dest->cargo.packets.push_back(GetCargoPacket(cargo->index));
+				cargo_count += cargo->count;
+			}
+			/* This design should always end up with the right amount of cargo. */
+			assert(cargo_count == dest->cargo.Count());
+		}
+
+		if (backup->Next() == NULL) break;
+		backup++;
+	}
+	return GetVehicle(v->index);
+}
+
+/** Restores a backed up vehicle
+ * @param *v A vehicle we should sell and take the windows from (NULL for not using this)
+ * @param *p The owner of the vehicle
+ * @return The vehicle we restored (front for trains) or v if we didn't have anything to restore
+ */
+Vehicle *BackuppedVehicle::Restore(Vehicle *v, Player *p)
+{
+	if (!ContainsBackup()) return v;
+	if (v != NULL) {
+		ChangeVehicleViewWindow(v, INVALID_VEHICLE);
+		DoCommand(0, v->index, 1, DC_EXEC, GetCmdSellVeh(v));
+	}
+	v = RestoreBackupVehicle(this->vehicles, p);
+	ChangeVehicleViewWindow(INVALID_VEHICLE, v);
+	if (orders != NULL) RestoreVehicleOrdersBruteForce(v, orders);
+	if (economy != NULL) economy->Restore();
+	/* If we stored cargo as well then we should restore it. */
+	cargo_packets->RestoreBackup();
+	return v;
+}
+
+/** Backs up a vehicle
+ * This should never be called when the object already contains a backup
+ * @param v the vehicle to backup
+ * @param p If it's set to the vehicle's owner then economy is backed up. If NULL then economy backup will be skipped.
+ */
+void BackuppedVehicle::Backup(Vehicle *v, Player *p)
+{
+	assert(!ContainsBackup());
+	if (p != NULL) {
+		assert(p->index == v->owner);
+		economy = new PlayerMoneyBackup(p);
+	}
+	BackupVehicle(v);
+	if (orders != NULL) BackupVehicleOrders(v, orders);
+}
+
 void StopAllVehicles()
 {
 	Vehicle *v;