changeset 2617:2c0d3643053a draft

(svn r3155) -Feature: [autoreplace] autoreplace can now remove cars from too long trains -Trains will now remember the length of stations it visits and sell cars when being autoreplaced if they became too long -If it needs to remove cars, then it starts from the front and sells all it can find until the train is short enough -This only works for trains, that knows the station length of the route so a full uninterrupted run is needed -a train needs 1-2 runs to detect if the shortest station is expanded -This feature can be turned on and off in the train replace window and each company can have it's own setting -NOTE: minor savegame version bump
author bjarni <bjarni@openttd.org>
date Mon, 07 Nov 2005 23:20:47 +0000
parents e82414cf63ce
children b1e91f4dc17f
files lang/english.txt openttd.c order_cmd.c player.h players.c saveload.c station.h station_cmd.c train_cmd.c vehicle.c vehicle.h vehicle_gui.c
diffstat 12 files changed, 129 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/lang/english.txt
+++ b/lang/english.txt
@@ -943,6 +943,7 @@
 STR_ROADVEHICLE_AUTORENEW_FAILED                                :{WHITE}Autorenew failed on road vehicle {COMMA} (money limit)
 STR_SHIP_AUTORENEW_FAILED                                       :{WHITE}Autorenew failed on ship {COMMA} (money limit)
 STR_AIRCRAFT_AUTORENEW_FAILED                                   :{WHITE}Autorenew failed on aircraft {COMMA} (money limit)
+STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT							:{WHITE}Train {COMMA} is too long after replacement
 
 STR_CONFIG_PATCHES                                              :{BLACK}Configure Patches
 STR_CONFIG_PATCHES_TIP                                          :{BLACK}Configure the patches
@@ -2752,6 +2753,8 @@
 STR_REPLACE_HELP_RAILTYPE                                       :{BLACK}Choose the railtype you want to replace engines for
 STR_REPLACE_HELP_REPLACE_INFO_TAB                               :{BLACK}Displays which engine the left selected engine is being replaced with, if any
 STR_REPLACE_HELP                                                :{BLACK}This allows you to replace one engine type with another type, when trains of the original type enter a depot
+STR_REPLACE_REMOVE_WAGON										:{BLACK}Wagon removal: {ORANGE}{SKIP}{STRING}
+STR_REPLACE_REMOVE_WAGON_HELP									:{BLACK}Setting this to "On" will make autoreplace remove wagons from trains to make them keep their length if they exceed length of the shortest station in their orders.{}It will remove as many wagons as needed starting from the front
 
 STR_SHORT_DATE                                                  :{WHITE}{DATE_TINY}
 STR_SIGN_LIST_CAPTION                                           :{WHITE}Sign List - {COMMA} Sign{P "" s}
--- a/openttd.c
+++ b/openttd.c
@@ -1300,6 +1300,23 @@
 		}
 	}
 
+	/* In version 16.1 of the savegame, trains became aware of station lengths
+		need to initialized to the invalid state
+		players needs to set renew_keep_length too */
+	if (version < 0x1001) {
+		Vehicle *v;
+		FOR_ALL_PLAYERS(p) {
+			p->renew_keep_length = false;
+		}
+
+		FOR_ALL_VEHICLES(v) {
+			if (v->type == VEH_Train) {
+				v->u.rail.shortest_platform[0] = 255;
+				v->u.rail.shortest_platform[1] = 0;
+			}
+		}
+	}
+
 	FOR_ALL_PLAYERS(p) {
 		p->avail_railtypes = GetPlayerRailtypes(p->index);
 	}
--- a/order_cmd.c
+++ b/order_cmd.c
@@ -388,6 +388,7 @@
 			}
 			/* Update any possible open window of the vehicle */
 			InvalidateVehicleOrder(u);
+			if (u->type == VEH_Train) u->u.rail.shortest_platform[1] = 0; // we changed the orders so we invalidate the station length collector
 
 			u = u->next_shared;
 		}
@@ -520,6 +521,7 @@
 		if (v->current_order.type == OT_LOADING && HASBIT(v->current_order.flags, OFB_NON_STOP))
 			v->current_order.flags = 0;
 
+		if (v->type == VEH_Train) v->u.rail.shortest_platform[1] = 0; // we changed the orders so we invalidate the station length collector
 		InvalidateVehicleOrder(v);
 	}
 
@@ -663,6 +665,7 @@
 				InvalidateVehicleOrder(src);
 
 				RebuildVehicleLists();
+				if (dst->type == VEH_Train) dst->u.rail.shortest_platform[1] = 0; // we changed the orders so we invalidate the station length collector
 			}
 		} break;
 
@@ -722,6 +725,7 @@
 				InvalidateVehicleOrder(dst);
 
 				RebuildVehicleLists();
+				if (dst->type == VEH_Train) dst->u.rail.shortest_platform[1] = 0; // we changed the orders so we invalidate the station length collector
 			}
 		} break;
 
--- a/player.h
+++ b/player.h
@@ -190,6 +190,7 @@
 	PlayerEconomyEntry old_economy[24];
 	EngineID engine_replacement[TOTAL_NUM_ENGINES];
 	bool engine_renew;
+	bool renew_keep_length;
 	int16 engine_renew_months;
 	uint32 engine_renew_money;
 } Player;
--- a/players.c
+++ b/players.c
@@ -497,6 +497,7 @@
 	for (i = 0; i < TOTAL_NUM_ENGINES; i++)
 		p->engine_replacement[i] = INVALID_ENGINE;
 
+	p->renew_keep_length = false;
 	p->engine_renew = false;
 	p->engine_renew_months = -6;
 	p->engine_renew_money = 100000;
@@ -654,6 +655,7 @@
  * - p1 = 2 - change auto renew money
  * - p1 = 3 - change auto renew array
  * - p1 = 4 - change bool, months & money all together
+ * - p1 = 5 - change renew_keep_length
  * @param p2 value to set
  * if p1 = 0, then:
  * - p2 = enable engine renewal
@@ -668,6 +670,8 @@
  * - p1 bit     15 = enable engine renewal
  * - p1 bits 16-31 = months left before engine expires to replace it
  * - p2 bits  0-31 = minimum amount of money available
+ * if p1 = 5, then
+ * - p2 = enable renew_keep_length
  */
 int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 {
@@ -754,8 +758,19 @@
 				}
 			}
 			break;
+		case 5:
+			if (p->renew_keep_length == (bool)GB(p2, 0, 1))
+				return CMD_ERROR;
+
+			if (flags & DC_EXEC) {
+				p->renew_keep_length = (bool)GB(p2, 0, 1);
+				if (IsLocalPlayer()) {
+					InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train);
+				}
+			}
+		break;
+
 	}
-
 	return 0;
 }
 
@@ -1131,9 +1146,11 @@
 	SLE_CONDVAR(Player,engine_renew,         SLE_UINT8,      16, 255),
 	SLE_CONDVAR(Player,engine_renew_months,  SLE_INT16,      16, 255),
 	SLE_CONDVAR(Player,engine_renew_money,  SLE_UINT32,      16, 255),
+	SLE_CONDVAR(Player,renew_keep_length,    SLE_UINT8,       2, 255),	// added with 16.1, but was blank since 2
 
-	// reserve extra space in savegame here. (currently 64 bytes)
-	SLE_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 8, 2, 255),
+	// reserve extra space in savegame here. (currently 63 bytes)
+	SLE_CONDARR(NullStruct,null,SLE_FILE_U8  | SLE_VAR_NULL, 7, 2, 255),
+	SLE_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 7, 2, 255),
 
 	SLE_END()
 };
--- a/saveload.c
+++ b/saveload.c
@@ -30,7 +30,7 @@
 
 enum {
 	SAVEGAME_MAJOR_VERSION = 16,
-	SAVEGAME_MINOR_VERSION = 0,
+	SAVEGAME_MINOR_VERSION = 1,
 
 	SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION
 };
--- a/station.h
+++ b/station.h
@@ -320,6 +320,11 @@
 	return IsTileType(tile, MP_STATION) && _m[tile].m5 == 0x52;
 }
 
+static inline bool TileBelongsToRailStation(const Station *st, TileIndex tile)
+{
+	return IsTileType(tile, MP_STATION) && _m[tile].m2 == st->index && _m[tile].m5 < 8;
+}
+
 /* Get's the direction the station exit points towards. Ie, returns 0 for a
  * station with the exit NE. */
 static inline byte GetRoadStationDir(TileIndex tile)
--- a/station_cmd.c
+++ b/station_cmd.c
@@ -1083,11 +1083,6 @@
 	return cost;
 }
 
-static bool TileBelongsToRailStation(const Station *st, TileIndex tile)
-{
-	return IsTileType(tile, MP_STATION) && _m[tile].m2 == st->index && _m[tile].m5 < 8;
-}
-
 static void MakeRailwayStationAreaSmaller(Station *st)
 {
 	uint w = st->trainst_w;
--- a/train_cmd.c
+++ b/train_cmd.c
@@ -752,6 +752,9 @@
 			v->type = VEH_Train;
 			v->cur_image = 0xAC2;
 
+			v->u.rail.shortest_platform[0] = 255;
+			v->u.rail.shortest_platform[1] = 0;
+
 			VehiclePositionChanged(v);
 
 			if (rvi->flags & RVI_MULTIHEAD && !HASBIT(p2, 0)) {
@@ -2349,6 +2352,27 @@
 
 	v->dest_tile = 0;
 
+	// store the station length if no shorter station was visited this order round
+	if (v->cur_order_index == 0) {
+		if (v->u.rail.shortest_platform[1] != 0 && v->u.rail.shortest_platform[1] != 255) {
+			// we went though a whole round of orders without interruptions, so we store the length of the shortest station
+			v->u.rail.shortest_platform[0] = v->u.rail.shortest_platform[1];
+		}
+		// all platforms are shorter than 255, so now we can find the shortest in the next order round. They might have changed size
+		v->u.rail.shortest_platform[1] = 255;
+	}
+
+	if (v->last_station_visited != INVALID_STATION) {
+		Station *st = GetStation(v->last_station_visited);
+		if (TileBelongsToRailStation(st, v->tile)) {
+			byte length = GetStationPlatforms(st, v->tile);
+
+			if (length < v->u.rail.shortest_platform[1]) {
+				v->u.rail.shortest_platform[1] = length;
+			}
+		}
+	}
+
 	result = false;
 	switch (order->type) {
 		case OT_GOTO_STATION:
--- a/vehicle.c
+++ b/vehicle.c
@@ -1693,6 +1693,8 @@
 			if (old_v->type == VEH_Train){
 				// move the entire train to the new engine, including the old engine. It will be sold in a moment anyway
 				DoCommand(0, 0, (new_v->index << 16) | old_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
+				new_v->u.rail.shortest_platform[0] = old_v->u.rail.shortest_platform[0];
+				new_v->u.rail.shortest_platform[1] = old_v->u.rail.shortest_platform[1];
 			}
 		}
 	}
@@ -1720,6 +1722,7 @@
 	byte flags = 0;
 	int32 cost, temp_cost = 0;
 	bool stopped = false;
+	bool train_fits_in_station = false;
 
 	_current_player = v->owner;
 
@@ -1733,6 +1736,11 @@
 		stopped = true;
 	}
 
+	if (v->type == VEH_Train && v->u.rail.shortest_platform[0]*16 <= v->u.rail.cached_total_length && GetPlayer(v->owner)->renew_keep_length) {
+		// the train is not too long for the stations it visits. We should try to keep it that way if we change anything
+		train_fits_in_station = true;
+	}
+
 	while (true) {
 		cost = 0;
 		w = v;
@@ -1794,6 +1802,27 @@
 		flags |= DC_EXEC;
 	}
 
+	if (train_fits_in_station) {
+		// the train fitted in the stations it got in it's orders, so we should make sure that it still do
+		Vehicle *temp;
+		w = v;
+		while (v->u.rail.shortest_platform[0]*16 < v->u.rail.cached_total_length) {
+			// the train is too long. We will remove cars one by one from the start of the train until it's short enough
+			while (w != NULL && !(RailVehInfo(w->engine_type)->flags&RVI_WAGON) ) {
+				w = GetNextVehicle(w);
+			}
+			if (w == NULL) {
+				// we failed to make the train short enough
+				SetDParam(0, v->unitnumber);
+				AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
+				break;
+			}
+			temp = w;
+			w = GetNextVehicle(w);
+			cost += DoCommand(0, 0, temp->index, 0, flags, CMD_SELL_VEH(temp->type));
+		}
+	}
+
 	if (IsLocalPlayer()) ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost);
 
 	if (stopped)
@@ -2085,8 +2114,10 @@
 	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,pbs_status), SLE_UINT8, 2, 255),
 	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,pbs_end_tile), SLE_UINT32, 2, 255),
 	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,pbs_end_trackdir), SLE_UINT8, 2, 255),
-	// reserve extra space in savegame here. (currently 7 bytes)
-	SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 7, 2, 255),
+	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,shortest_platform[0]), SLE_UINT8, 2, 255),	// added with 16.1, but was blank since 2
+	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,shortest_platform[1]), SLE_UINT8, 2, 255),	// added with 16.1, but was blank since 2
+	// reserve extra space in savegame here. (currently 5 bytes)
+	SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 5, 2, 255),
 
 	SLE_END()
 };
--- a/vehicle.h
+++ b/vehicle.h
@@ -85,6 +85,14 @@
 	byte pbs_status;
 	TileIndex pbs_end_tile;
 	Trackdir pbs_end_trackdir;
+
+	/**
+	  * stuff to figure out how long a train should be. Used by autoreplace
+	  * first byte holds the length of the shortest station. Updated each time order 0 is reached
+	  * last byte is the shortest station reached this round though the orders. It can be invalidated by
+	  *   skip station and alike by setting it to 0. That way we will ensure that a complete loop is used to find the shortest station
+	  */
+	byte shortest_platform[2];
 } VehicleRail;
 
 enum {
@@ -177,7 +185,6 @@
 	int32 x_pos;			// coordinates
 	int32 y_pos;
 
-	bool leave_depot_instantly;	// NOSAVE: stores if the vehicle needs to leave the depot it just entered. Used by autoreplace
 	byte z_pos;
 	byte direction;		// facing
 
@@ -248,6 +255,8 @@
 	byte breakdown_chance;
 	byte build_year;
 
+	bool leave_depot_instantly;	// NOSAVE: stores if the vehicle needs to leave the depot it just entered. Used by autoreplace
+
 	uint16 load_unload_time_rem;
 
 	int32 profit_this_year;
--- a/vehicle_gui.c
+++ b/vehicle_gui.c
@@ -748,10 +748,10 @@
 static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
 {
 	static const StringID _vehicle_type_names[4] = {STR_019F_TRAIN, STR_019C_ROAD_VEHICLE, STR_019E_SHIP,STR_019D_AIRCRAFT};
+	const Player *p = GetPlayer(_local_player);
 
 	switch (e->event) {
 		case WE_PAINT: {
-				const Player *p = GetPlayer(_local_player);
 				int pos = w->vscroll.pos;
 				int selected_id[2] = {-1,-1};
 				int x = 1;
@@ -839,6 +839,12 @@
 
 				// now the actual drawing of the window itself takes place
 				SetDParam(0, _vehicle_type_names[WP(w, replaceveh_d).vehicletype - VEH_Train]);
+
+				if (WP(w, replaceveh_d).vehicletype == VEH_Train) {
+					// set on/off for renew_keep_length
+					SetDParam(1, p->renew_keep_length ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
+				}
+
 				DrawWindowWidgets(w);
 
 				// sets up the string for the vehicle that is being replaced to
@@ -925,6 +931,9 @@
 					ShowDropDownMenu(w, _rail_types_list, _railtype_selected_in_replace_gui, 15, 0, ~GetPlayer(_local_player)->avail_railtypes);
 					break;
 				}
+				case 17: { /* toggle renew_keep_length */
+					DoCommandP(0, 5, p->renew_keep_length ? 0 : 1, NULL, CMD_REPLACE_VEHICLE);
+				} break;
 				case 4: { /* Start replacing */
 					EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
 					EngineID veh_to = WP(w, replaceveh_d).sel_engine[1];
@@ -991,7 +1000,7 @@
 {      WWT_PANEL,     RESIZE_TB,    14,   154,   277,   210,   221, STR_NULL,       STR_REPLACE_HELP_RAILTYPE},
 {   WWT_CLOSEBOX,     RESIZE_TB,    14,   278,   289,   210,   221, STR_0225,       STR_REPLACE_HELP_RAILTYPE},
 {      WWT_PANEL,     RESIZE_TB,    14,   290,   305,   210,   221, STR_NULL,       STR_NULL},
-{      WWT_PANEL,     RESIZE_TB,    14,   317,   455,   198,   209, STR_NULL,       STR_NULL},
+{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   317,   455,   198,   209, STR_REPLACE_REMOVE_WAGON,       STR_REPLACE_REMOVE_WAGON_HELP},
 // end of train specific stuff
 {  WWT_RESIZEBOX,     RESIZE_TB,    14,   444,   455,   210,   221, STR_NULL,       STR_RESIZE_BUTTON},
 {   WIDGETS_END},