src/train_cmd.cpp
branchNewGRF_ports
changeset 6720 35756db7e577
parent 6719 4cc327ad39d5
child 6725 23339968083f
--- a/src/train_cmd.cpp	Sat Jun 02 19:59:29 2007 +0000
+++ b/src/train_cmd.cpp	Sat Jul 14 19:42:58 2007 +0000
@@ -15,6 +15,7 @@
 #include "tile.h"
 #include "tunnel_map.h"
 #include "vehicle.h"
+#include "timetable.h"
 #include "articulated_vehicles.h"
 #include "command.h"
 #include "pathfind.h"
@@ -111,7 +112,7 @@
 	uint32 weight = 0;
 
 	for (Vehicle *u = v; u != NULL; u = u->next) {
-		uint32 vweight = GetCargo(u->cargo_type)->weight * u->cargo_count * FreightWagonMult(u->cargo_type) / 16;
+		uint32 vweight = GetCargo(u->cargo_type)->weight * u->cargo.Count() * FreightWagonMult(u->cargo_type) / 16;
 
 		/* Vehicle weight is not added for articulated parts. */
 		if (!IsArticulatedPart(u)) {
@@ -230,7 +231,7 @@
 		/* check the vehicle length (callback) */
 		uint16 veh_len = CALLBACK_FAILED;
 		if (HASBIT(EngInfo(u->engine_type)->callbackmask, CBM_VEHICLE_LENGTH)) {
-			veh_len = GetVehicleCallback(CBID_TRAIN_VEHICLE_LENGTH, 0, 0, u->engine_type, u);
+			veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, u->engine_type, u);
 		}
 		if (veh_len == CALLBACK_FAILED) veh_len = rvi_u->shorten_factor;
 		veh_len = clamp(veh_len, 0, u->next == NULL ? 7 : 5); // the clamp on vehicles not the last in chain is stricter, as too short wagons can break the 'follow next vehicle' code
@@ -447,22 +448,22 @@
 	v->acceleration = clamp(power / weight * 4, 1, 255);
 }
 
-int GetTrainImage(const Vehicle* v, Direction direction)
+int Train::GetImage(Direction direction) const
 {
-	int img = v->spritenum;
+	int img = this->spritenum;
 	int base;
 
-	if (HASBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION)) direction = ReverseDir(direction);
+	if (HASBIT(this->u.rail.flags, VRF_REVERSE_DIRECTION)) direction = ReverseDir(direction);
 
 	if (is_custom_sprite(img)) {
-		base = GetCustomVehicleSprite(v, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(img)));
+		base = GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(img)));
 		if (base != 0) return base;
-		img = orig_rail_vehicle_info[v->engine_type].image_index;
+		img = orig_rail_vehicle_info[this->engine_type].image_index;
 	}
 
 	base = _engine_sprite_base[img] + ((direction + _engine_sprite_add[img]) & _engine_sprite_and[img]);
 
-	if (v->cargo_count >= v->cargo_cap / 2) base += _wagon_full_adder[img];
+	if (this->cargo.Count() >= this->cargo_cap / 2U) base += _wagon_full_adder[img];
 	return base;
 }
 
@@ -502,12 +503,12 @@
 	DrawSprite(image, pal, x, y);
 }
 
-static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags)
+static CommandCost CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags)
 {
 	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 
 	const RailVehicleInfo *rvi = RailVehInfo(engine);
-	int32 value = (GetEngineProperty(engine, 0x17, rvi->base_cost) * _price.build_railwagon) >> 8;
+	CommandCost value((GetEngineProperty(engine, 0x17, rvi->base_cost) * _price.build_railwagon) >> 8);
 
 	uint num_vehicles = 1 + CountArticulatedParts(engine);
 
@@ -565,7 +566,7 @@
 			v->cargo_type = rvi->cargo_type;
 			v->cargo_subtype = 0;
 			v->cargo_cap = rvi->capacity;
-			v->value = value;
+			v->value = value.GetCost();
 //			v->day_counter = 0;
 
 			v->u.rail.railtype = rvi->railtype;
@@ -576,7 +577,7 @@
 
 			v->group_id = DEFAULT_GROUP;
 
-			AddArticulatedParts(vl);
+			AddArticulatedParts(vl, VEH_TRAIN);
 
 			_new_vehicle_id = v->index;
 
@@ -586,13 +587,13 @@
 
 			InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 			if (IsLocalPlayer()) {
-				InvalidateAutoreplaceWindow(VEH_TRAIN); // updates the replace Train window
+				InvalidateAutoreplaceWindow(VEH_TRAIN, v->group_id); // updates the replace Train window
 			}
 			GetPlayer(_current_player)->num_engines[engine]++;
 		}
 	}
 
-	return value;
+	return CommandCost(value);
 }
 
 /** Move all free vehicles in the depot to the train */
@@ -611,9 +612,9 @@
 	}
 }
 
-static int32 EstimateTrainCost(EngineID engine, const RailVehicleInfo* rvi)
+static CommandCost EstimateTrainCost(EngineID engine, const RailVehicleInfo* rvi)
 {
-	return GetEngineProperty(engine, 0x17, rvi->base_cost) * (_price.build_railvehicle >> 3) >> 5;
+	return CommandCost(GetEngineProperty(engine, 0x17, rvi->base_cost) * (_price.build_railvehicle >> 3) >> 5);
 }
 
 static void AddRearEngineToMultiheadedTrain(Vehicle* v, Vehicle* u, bool building)
@@ -651,7 +652,7 @@
  * @param p2 bit 0 when set, the train will get number 0, otherwise it will get a free number
  *           bit 1 prevents any free cars from being added to the train
  */
-int32 CmdBuildRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdBuildRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	/* Check if the engine-type is valid (for the player) */
 	if (!IsEngineBuildable(p1, VEH_TRAIN, _current_player)) return_cmd_error(STR_RAIL_VEHICLE_NOT_AVAILABLE);
@@ -673,7 +674,7 @@
 
 	if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(p1, tile, flags);
 
-	int32 value = EstimateTrainCost(p1, rvi);
+	CommandCost value = EstimateTrainCost(p1, rvi);
 
 	uint num_vehicles =
 		(rvi->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) +
@@ -712,7 +713,7 @@
 			v->cargo_subtype = 0;
 			v->cargo_cap = rvi->capacity;
 			v->max_speed = rvi->max_speed;
-			v->value = value;
+			v->value = value.GetCost();
 			v->last_station_visited = INVALID_STATION;
 			v->dest_tile = 0;
 
@@ -755,7 +756,7 @@
 				vl[0]->u.rail.other_multiheaded_part = vl[1];
 				vl[1]->u.rail.other_multiheaded_part = vl[0];
 			} else {
-				AddArticulatedParts(vl);
+				AddArticulatedParts(vl, VEH_TRAIN);
 			}
 
 			TrainConsistChanged(v);
@@ -770,7 +771,7 @@
 			RebuildVehicleLists();
 			InvalidateWindow(WC_COMPANY, v->owner);
 			if (IsLocalPlayer())
-				InvalidateAutoreplaceWindow(VEH_TRAIN); // updates the replace Train window
+				InvalidateAutoreplaceWindow(VEH_TRAIN, v->group_id); // updates the replace Train window
 
 			GetPlayer(_current_player)->num_engines[p1]++;
 		}
@@ -909,7 +910,7 @@
  * - p1 (bit 16 - 31) what wagon to put the source wagon AFTER, XXX - INVALID_VEHICLE to make a new line
  * @param p2 (bit 0) move all vehicles following the source vehicle
  */
-int32 CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	VehicleID s = GB(p1, 0, 16);
 	VehicleID d = GB(p1, 16, 16);
@@ -937,7 +938,7 @@
 	}
 
 	/* don't move the same vehicle.. */
-	if (src == dst) return 0;
+	if (src == dst) return CommandCost();
 
 	/* locate the head of the two chains */
 	Vehicle *src_head = GetFirstVehicleInChain(src);
@@ -954,7 +955,7 @@
 	if (IsMultiheaded(src) && !IsTrainEngine(src)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR);
 
 	/* when moving all wagons, we can't have the same src_head and dst_head */
-	if (HASBIT(p2, 0) && src_head == dst_head) return 0;
+	if (HASBIT(p2, 0) && src_head == dst_head) return CommandCost();
 
 	{
 		int max_len = _patches.mammoth_trains ? 100 : 9;
@@ -1153,7 +1154,7 @@
 		RebuildVehicleLists();
 	}
 
-	return 0;
+	return CommandCost();
 }
 
 /** Start/Stop a train.
@@ -1162,7 +1163,7 @@
  * @param p1 train to start/stop
  * @param p2 unused
  */
-int32 CmdStartStopTrain(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdStartStopTrain(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 
@@ -1189,7 +1190,7 @@
 		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 	}
-	return 0;
+	return CommandCost();
 }
 
 /** Sell a (single) train wagon/engine.
@@ -1203,8 +1204,11 @@
  * - p2 = 2: when selling attached locos, rearrange all vehicles after it to separate lines;
  *           all wagons of the same type will go on the same line. Used by the AI currently
  */
-int32 CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
+	/* Check if we deleted a vehicle window */
+	Window *w = NULL;
+
 	if (!IsValidVehicleID(p1) || p2 > 2) return CMD_ERROR;
 
 	Vehicle *v = GetVehicle(p1);
@@ -1225,13 +1229,14 @@
 
 	if (flags & DC_EXEC) {
 		if (v == first && IsFrontEngine(first)) {
-			DeleteWindowById(WC_VEHICLE_VIEW, first->index);
+			w = FindWindowById(WC_VEHICLE_VIEW, first->index);
+			if (w != NULL) DeleteWindow(w);
 		}
 		InvalidateWindow(WC_VEHICLE_DEPOT, first->tile);
 		RebuildVehicleLists();
 	}
 
-	int32 cost = 0;
+	CommandCost cost;
 	switch (p2) {
 		case 0: case 2: { /* Delete given wagon */
 			bool switch_engine = false;    // update second wagon to engine?
@@ -1243,7 +1248,7 @@
 				IsTrainEngine(v)) ? v->u.rail.other_multiheaded_part : NULL;
 
 			if (rear != NULL) {
-				cost -= rear->value;
+				cost.AddCost(-rear->value);
 				if (flags & DC_EXEC) {
 					UnlinkWagon(rear, first);
 					DeleteDepotHighlightOfVehicle(rear);
@@ -1292,12 +1297,13 @@
 					first->prev_shared = NULL;
 					first->next_shared = NULL;
 
-					if (IsLocalPlayer()) ShowTrainViewWindow(new_f);
+					/* If we deleted a window then open a new one for the 'new' train */
+					if (IsLocalPlayer() && w != NULL) ShowTrainViewWindow(new_f);
 				}
 			}
 
 			/* 3. Delete the requested wagon */
-			cost -= v->value;
+			cost.AddCost(-v->value);
 			if (flags & DC_EXEC) {
 				first = UnlinkWagon(v, first);
 				DeleteDepotHighlightOfVehicle(v);
@@ -1347,7 +1353,7 @@
 						Vehicle *rear = v->u.rail.other_multiheaded_part;
 
 						if (rear != NULL) {
-							cost -= rear->value;
+							cost.AddCost(-rear->value);
 
 							/* If this is a multiheaded vehicle with nothing
 							 * between the parts, tmp will be pointing to the
@@ -1369,7 +1375,7 @@
 					}
 				}
 
-				cost -= v->value;
+				cost.AddCost(-v->value);
 				if (flags & DC_EXEC) {
 					first = UnlinkWagon(v, first);
 					DeleteDepotHighlightOfVehicle(v);
@@ -1418,7 +1424,7 @@
 static void UpdateVarsAfterSwap(Vehicle *v)
 {
 	v->UpdateDeltaXY(v->direction);
-	v->cur_image = GetTrainImage(v, v->direction);
+	v->cur_image = v->GetImage(v->direction);
 	BeginVehicleMove(v);
 	VehiclePositionChanged(v);
 	EndVehicleMove(v);
@@ -1611,7 +1617,7 @@
  * @param p1 train to reverse
  * @param p2 if true, reverse a unit in a train (needs to be in a depot)
  */
-int32 CmdReverseTrainDirection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdReverseTrainDirection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 
@@ -1651,7 +1657,7 @@
 			}
 		}
 	}
-	return 0;
+	return CommandCost();
 }
 
 /** Force a train through a red signal
@@ -1660,7 +1666,7 @@
  * @param p1 train to ignore the red signal
  * @param p2 unused
  */
-int32 CmdForceTrainProceed(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdForceTrainProceed(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 
@@ -1670,7 +1676,7 @@
 
 	if (flags & DC_EXEC) v->u.rail.force_proceed = 0x50;
 
-	return 0;
+	return CommandCost();
 }
 
 /** Refits a train to the specified cargo type.
@@ -1683,7 +1689,7 @@
  * - p2 = (bit 16) - refit only this vehicle
  * @return cost of refit or error
  */
-int32 CmdRefitRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdRefitRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	CargoID new_cid = GB(p2, 0, 8);
 	byte new_subtype = GB(p2, 8, 8);
@@ -1701,7 +1707,7 @@
 
 	SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN);
 
-	int32 cost = 0;
+	CommandCost cost;
 	uint num = 0;
 
 	do {
@@ -1750,12 +1756,12 @@
 
 			if (amount != 0) {
 				if (new_cid != v->cargo_type) {
-					cost += GetRefitCost(v->engine_type);
+					cost.AddCost(GetRefitCost(v->engine_type));
 				}
 
 				num += amount;
 				if (flags & DC_EXEC) {
-					v->cargo_count = (v->cargo_type == new_cid) ? min(amount, v->cargo_count) : 0;
+					v->cargo.Truncate((v->cargo_type == new_cid) ? amount : 0);
 					v->cargo_type = new_cid;
 					v->cargo_cap = amount;
 					v->cargo_subtype = new_subtype;
@@ -1867,7 +1873,7 @@
  * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h)
  * - p2 bit 8-10 - VLW flag (for mass goto depot)
  */
-int32 CmdSendTrainToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+CommandCost CmdSendTrainToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	if (p2 & DEPOT_MASS_SEND) {
 		/* Mass goto depot requested */
@@ -1892,7 +1898,7 @@
 				TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 			}
-			return 0;
+			return CommandCost();
 		}
 
 		if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
@@ -1905,7 +1911,7 @@
 			v->current_order.flags = 0;
 			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 		}
-		return 0;
+		return CommandCost();
 	}
 
 	/* check if at a standstill (not stopped only) in a depot
@@ -1929,7 +1935,7 @@
 		if (tfdd.reverse) DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
 	}
 
-	return 0;
+	return CommandCost();
 }
 
 
@@ -2075,7 +2081,7 @@
 	v->cur_speed = 0;
 
 	v->UpdateDeltaXY(v->direction);
-	v->cur_image = GetTrainImage(v, v->direction);
+	v->cur_image = v->GetImage(v->direction);
 	VehiclePositionChanged(v);
 	UpdateSignalsOnSegment(v->tile, DirToDiagDir(v->direction));
 	UpdateTrainAcceleration(v);
@@ -2374,6 +2380,7 @@
 			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return false;
 			if ((v->current_order.flags & OF_SERVICE_IF_NEEDED) &&
 					!VehicleNeedsService(v)) {
+				UpdateVehicleTimetable(v, true);
 				v->cur_order_index++;
 			}
 			break;
@@ -2385,11 +2392,19 @@
 		default: break;
 	}
 
+	/**
+	 * Reversing because of order change is allowed only just after leaving a
+	 * station (and the difficulty setting to allowed, of course)
+	 * this can be detected because only after OT_LEAVESTATION, current_order
+	 * will be reset to nothing. (That also happens if no order, but in that case
+	 * it won't hit the point in code where may_reverse is checked)
+	 */
+	bool may_reverse = v->current_order.type == OT_NOTHING;
+
 	/* check if we've reached the waypoint? */
-	bool at_waypoint = false;
 	if (v->current_order.type == OT_GOTO_WAYPOINT && v->tile == v->dest_tile) {
+		UpdateVehicleTimetable(v, true);
 		v->cur_order_index++;
-		at_waypoint = true;
 	}
 
 	/* check if we've reached a non-stop station while TTDPatch nonstop is enabled.. */
@@ -2397,6 +2412,7 @@
 			v->current_order.flags & OF_NON_STOP &&
 			IsTileType(v->tile, MP_STATION) &&
 			v->current_order.dest == GetStationIndex(v->tile)) {
+		UpdateVehicleTimetable(v, true);
 		v->cur_order_index++;
 	}
 
@@ -2444,14 +2460,14 @@
 			return false;
 	}
 
-	return !at_waypoint && CheckReverseTrain(v);
+	return may_reverse && CheckReverseTrain(v);
 }
 
 void Train::MarkDirty()
 {
 	Vehicle *v = this;
 	do {
-		v->cur_image = GetTrainImage(v, v->direction);
+		v->cur_image = v->GetImage(v->direction);
 		MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
 	} while ((v = v->next) != NULL);
 
@@ -2520,22 +2536,28 @@
 
 static byte AfterSetTrainPos(Vehicle *v, bool new_tile)
 {
-	/* need this hint so it returns the right z coordinate on bridges. */
-	byte new_z = GetSlopeZ(v->x_pos, v->y_pos);
-
 	byte old_z = v->z_pos;
-	v->z_pos = new_z;
+	v->z_pos = GetSlopeZ(v->x_pos, v->y_pos);
 
 	if (new_tile) {
 		CLRBIT(v->u.rail.flags, VRF_GOINGUP);
 		CLRBIT(v->u.rail.flags, VRF_GOINGDOWN);
 
-		if (new_z != old_z) {
-			TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
-
-			/* XXX workaround, whole UP/DOWN detection needs overhaul */
-			if (!IsTunnelTile(tile)) {
-				SETBIT(v->u.rail.flags, (new_z > old_z) ? VRF_GOINGUP : VRF_GOINGDOWN);
+		if (v->u.rail.track == TRACK_BIT_X || v->u.rail.track == TRACK_BIT_Y) {
+			/* Any track that isn't TRACK_BIT_X or TRACK_BIT_Y cannot be sloped.
+			 * To check whether the current tile is sloped, and in which
+			 * direction it is sloped, we get the 'z' at the center of
+			 * the tile (middle_z) and the edge of the tile (old_z),
+			 * which we then can compare. */
+			static const int HALF_TILE_SIZE = TILE_SIZE / 2;
+			static const int INV_TILE_SIZE_MASK = ~(TILE_SIZE - 1);
+
+			byte middle_z = GetSlopeZ((v->x_pos & INV_TILE_SIZE_MASK) | HALF_TILE_SIZE, (v->y_pos & INV_TILE_SIZE_MASK) | HALF_TILE_SIZE);
+
+			/* For some reason tunnel tiles are always given as sloped :(
+			 * But they are not sloped... */
+			if (middle_z != v->z_pos && !IsTunnelTile(TileVirtXY(v->x_pos, v->y_pos))) {
+				SETBIT(v->u.rail.flags, (middle_z > old_z) ? VRF_GOINGUP : VRF_GOINGDOWN);
 			}
 		}
 	}
@@ -2654,28 +2676,6 @@
 }
 
 
-struct TrainCollideChecker {
-	const Vehicle *v;
-	const Vehicle *v_skip;
-};
-
-static void *FindTrainCollideEnum(Vehicle *v, void *data)
-{
-	const TrainCollideChecker* tcc = (TrainCollideChecker*)data;
-
-	if (v != tcc->v &&
-			v != tcc->v_skip &&
-			v->type == VEH_TRAIN &&
-			v->u.rail.track != TRACK_BIT_DEPOT &&
-			myabs(v->z_pos - tcc->v->z_pos) < 6 &&
-			myabs(v->x_pos - tcc->v->x_pos) < 6 &&
-			myabs(v->y_pos - tcc->v->y_pos) < 6) {
-		return v;
-	} else {
-		return NULL;
-	}
-}
-
 static void SetVehicleCrashed(Vehicle *v)
 {
 	if (v->u.rail.crash_anim_pos != 0) return;
@@ -2694,11 +2694,52 @@
 {
 	uint num = 0;
 	BEGIN_ENUM_WAGONS(v)
-		if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo_count;
+		if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo.Count();
 	END_ENUM_WAGONS(v)
 	return num;
 }
 
+struct TrainCollideChecker {
+	Vehicle *v;
+	const Vehicle *v_skip;
+	uint num;
+};
+
+static void *FindTrainCollideEnum(Vehicle *v, void *data)
+{
+	TrainCollideChecker* tcc = (TrainCollideChecker*)data;
+
+	if (v != tcc->v &&
+			v != tcc->v_skip &&
+			v->type == VEH_TRAIN &&
+			v->u.rail.track != TRACK_BIT_DEPOT &&
+			myabs(v->z_pos - tcc->v->z_pos) < 6 &&
+			myabs(v->x_pos - tcc->v->x_pos) < 6 &&
+			myabs(v->y_pos - tcc->v->y_pos) < 6 ) {
+
+		Vehicle *coll = GetFirstVehicleInChain(v);
+
+		/* it can't collide with its own wagons */
+		if (tcc->v == coll ||
+			(tcc->v->u.rail.track == TRACK_BIT_WORMHOLE && (tcc->v->direction & 2) != (v->direction & 2)))
+			return NULL;
+
+		/* two drivers + passengers killed in train tcc->v (if it was not crashed already) */
+		if (!(tcc->v->vehstatus & VS_CRASHED)) {
+			tcc->num += 2 + CountPassengersInTrain(tcc->v);
+			SetVehicleCrashed(tcc->v);
+		}
+
+		if (!(coll->vehstatus & VS_CRASHED)) {
+			/* two drivers + passengers killed in train coll (if it was not crashed already) */
+			tcc->num += 2 + CountPassengersInTrain(coll);
+			SetVehicleCrashed(coll);
+		}
+	}
+
+	return NULL;
+}
+
 /**
  * Checks whether the specified train has a collision with another vehicle. If
  * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front.
@@ -2715,28 +2756,15 @@
 	TrainCollideChecker tcc;
 	tcc.v = v;
 	tcc.v_skip = v->next;
-
-	/* find colliding vehicle */
-	Vehicle *realcoll = (Vehicle*)VehicleFromPos(TileVirtXY(v->x_pos, v->y_pos), &tcc, FindTrainCollideEnum);
-	if (realcoll == NULL) return;
-
-	Vehicle *coll = GetFirstVehicleInChain(realcoll);
-
-	/* it can't collide with its own wagons */
-	if (v == coll ||
-			(v->u.rail.track == TRACK_BIT_WORMHOLE && (v->direction & 2) != (realcoll->direction & 2)))
-		return;
-
-	/* two drivers + passangers killed in train v */
-	uint num = 2 + CountPassengersInTrain(v);
-	if (!(coll->vehstatus & VS_CRASHED))
-		/* two drivers + passangers killed in train coll (if it was not crashed already) */
-		num += 2 + CountPassengersInTrain(coll);
-
-	SetVehicleCrashed(v);
-	if (IsFrontEngine(coll)) SetVehicleCrashed(coll);
-
-	SetDParam(0, num);
+	tcc.num = 0;
+
+	/* find colliding vehicles */
+	VehicleFromPosXY(v->x_pos, v->y_pos, &tcc, FindTrainCollideEnum);
+
+	/* any dead -> no crash */
+	if (tcc.num == 0) return;
+
+	SetDParam(0, tcc.num);
 	AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL,
 		NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, 0),
 		v->index,
@@ -2770,6 +2798,8 @@
 
 	/* For every vehicle after and including the given vehicle */
 	for (prev = GetPrevVehicleInChain(v); v != NULL; prev = v, v = v->next) {
+		DiagDirection enterdir = DIAGDIR_BEGIN;
+		bool update_signals = false;
 		BeginVehicleMove(v);
 
 		GetNewVehiclePosResult gp = GetNewVehiclePos(v);
@@ -2805,7 +2835,7 @@
 
 				/* Determine what direction we're entering the new tile from */
 				Direction dir = GetNewVehicleDirectionByTile(gp.new_tile, gp.old_tile);
-				DiagDirection enterdir = DirToDiagDir(dir);
+				enterdir = DirToDiagDir(dir);
 				assert(IsValidDiagDirection(enterdir));
 
 				/* Get the status of the tracks in the new tile and mask
@@ -2912,11 +2942,9 @@
 					assert(v->u.rail.track);
 				}
 
-				if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
-
-				/* Signals can only change when the first
-				 * (above) or the last vehicle moves. */
-				if (v->next == NULL) TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
+				/* We need to update signal status, but after the vehicle position hash
+				 * has been updated by AfterSetTrainPos() */
+				update_signals = true;
 
 				if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir);
 
@@ -2941,7 +2969,7 @@
 		/* update image of train, as well as delta XY */
 		Direction newdir = GetNewVehicleDirection(v, gp.x, gp.y);
 		v->UpdateDeltaXY(newdir);
-		if (update_image) v->cur_image = GetTrainImage(v, newdir);
+		if (update_image) v->cur_image = v->GetImage(newdir);
 
 		v->x_pos = gp.x;
 		v->y_pos = gp.y;
@@ -2953,6 +2981,14 @@
 			/* This is the first vehicle in the train */
 			AffectSpeedByZChange(v, old_z);
 		}
+
+		if (update_signals) {
+			if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
+
+			/* Signals can only change when the first
+			 * (above) or the last vehicle moves. */
+			if (v->next == NULL) TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
+		}
 	}
 	return;
 
@@ -2993,8 +3029,6 @@
 	BeginVehicleMove(v);
 	EndVehicleMove(v);
 
-	if (IsFrontEngine(v)) RemoveVehicleFromGroup(v);
-
 	DeleteVehicle(v);
 
 	if (v->u.rail.track != TRACK_BIT_DEPOT && v->u.rail.track != TRACK_BIT_WORMHOLE)
@@ -3040,7 +3074,7 @@
 			v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
 			BeginVehicleMove(v);
 			v->UpdateDeltaXY(v->direction);
-			v->cur_image = GetTrainImage(v, v->direction);
+			v->cur_image = v->GetImage(v->direction);
 			/* Refrain from updating the z position of the vehicle when on
 			   a bridge, because AfterSetTrainPos will put the vehicle under
 			   the bridge in that case */
@@ -3269,23 +3303,24 @@
 }
 
 
-void Train_Tick(Vehicle *v)
+void Train::Tick()
 {
-	if (_age_cargo_skip_counter == 0 && v->cargo_days != 0xff)
-		v->cargo_days++;
-
-	v->tick_counter++;
-
-	if (IsFrontEngine(v)) {
-		TrainLocoHandler(v, false);
+	if (_age_cargo_skip_counter == 0) this->cargo.AgeCargo();
+
+	this->tick_counter++;
+
+	if (IsFrontEngine(this)) {
+		this->current_order_time++;
+
+		TrainLocoHandler(this, false);
 
 		/* make sure vehicle wasn't deleted. */
-		if (v->type == VEH_TRAIN && IsFrontEngine(v))
-			TrainLocoHandler(v, true);
-	} else if (IsFreeWagon(v) && HASBITS(v->vehstatus, VS_CRASHED)) {
+		if (this->type == VEH_TRAIN && IsFrontEngine(this))
+			TrainLocoHandler(this, true);
+	} else if (IsFreeWagon(this) && HASBITS(this->vehstatus, VS_CRASHED)) {
 		/* Delete flooded standalone wagon */
-		if (++v->u.rail.crash_anim_pos >= 4400)
-			DeleteVehicle(v);
+		if (++this->u.rail.crash_anim_pos >= 4400)
+			DeleteVehicle(this);
 	}
 }
 
@@ -3340,9 +3375,9 @@
 	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 }
 
-int32 GetTrainRunningCost(const Vehicle *v)
+Money GetTrainRunningCost(const Vehicle *v)
 {
-	int32 cost = 0;
+	Money cost = 0;
 
 	do {
 		const RailVehicleInfo *rvi = RailVehInfo(v->engine_type);
@@ -3376,9 +3411,9 @@
 
 		if ((v->vehstatus & VS_STOPPED) == 0) {
 			/* running costs */
-			int32 cost = GetTrainRunningCost(v) / 364;
-
-			v->profit_this_year -= cost >> 8;
+			CommandCost cost(GetTrainRunningCost(v) / 364);
+
+			v->profit_this_year -= cost.GetCost() >> 8;
 
 			SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN);
 			SubtractMoneyFromPlayerFract(v->owner, cost);
@@ -3386,6 +3421,9 @@
 			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 			InvalidateWindowClasses(WC_TRAINS_LIST);
 		}
+	} else if (IsTrainEngine(v)) {
+		/* Also age engines that aren't front engines */
+		AgeVehicle(v);
 	}
 }