changeset 11657:ff1084d0d8a0 draft

(svn r16037) -Feature: allow (per order) to let a train stop at the near end, middle or far end of a platform from the point of view of the driver of the train that enters the station.
author rubidium <rubidium@openttd.org>
date Sun, 12 Apr 2009 14:11:14 +0000
parents 7f8c950a95b4
children 4734b9d6584c
files src/lang/english.txt src/order_base.h src/order_cmd.cpp src/order_gui.cpp src/order_type.h src/saveload/afterload.cpp src/saveload/order_sl.cpp src/saveload/saveload.cpp src/settings_gui.cpp src/settings_type.h src/station_cmd.cpp src/table/settings.h src/train.h src/train_cmd.cpp
diffstat 14 files changed, 168 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -938,6 +938,10 @@
 STR_CONFIG_SETTING_SIGNALSIDE                                   :{LTBLUE}Show signals on the drive side: {ORANGE}{STRING1}
 STR_CONFIG_SETTING_SHOWFINANCES                                 :{LTBLUE}Show finances window at the end of the year: {ORANGE}{STRING1}
 STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT                           :{LTBLUE}New orders are 'non-stop' by default: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_STOP_LOCATION                                :{LTBLUE}New train orders stop by default at the {ORANGE}{STRING1}{LTBLUE} of the platform
+STR_CONFIG_SETTING_STOP_LOCATION_NEAR_END                       :near end
+STR_CONFIG_SETTING_STOP_LOCATION_MIDDLE                         :middle
+STR_CONFIG_SETTING_STOP_LOCATION_FAR_END                        :far end
 STR_CONFIG_SETTING_ROADVEH_QUEUE                                :{LTBLUE}Road vehicle queueing (with quantum effects): {ORANGE}{STRING1}
 STR_CONFIG_SETTING_AUTOSCROLL                                   :{LTBLUE}Pan window when mouse is at the edge: {ORANGE}{STRING1}
 STR_CONFIG_SETTING_BRIBE                                        :{LTBLUE}Allow bribing of the local authority: {ORANGE}{STRING1}
@@ -2615,6 +2619,9 @@
 STR_ORDER_GO_NON_STOP_TO                                        :Go non-stop to
 STR_ORDER_GO_VIA                                                :Go via
 STR_ORDER_GO_NON_STOP_VIA                                       :Go non-stop via
+STR_ORDER_STOP_LOCATION_NEAR_END                                :[near end]
+STR_ORDER_STOP_LOCATION_MIDDLE                                  :[middle]
+STR_ORDER_STOP_LOCATION_FAR_END                                 :[far end]
 STR_ORDER_TOGGLE_FULL_LOAD                                      :{BLACK}Full load any cargo
 STR_ORDER_DROP_LOAD_IF_POSSIBLE                                 :Load if available
 STR_ORDER_DROP_FULL_LOAD_ALL                                    :Full load all cargo
--- a/src/order_base.h
+++ b/src/order_base.h
@@ -166,8 +166,10 @@
 	inline OrderLoadFlags GetLoadType() const { return (OrderLoadFlags)GB(this->flags, 4, 4); }
 	/** How must the consist be unloaded? */
 	inline OrderUnloadFlags GetUnloadType() const { return (OrderUnloadFlags)GB(this->flags, 0, 4); }
-	/** Where must we stop? */
+	/** At which stations must we stop? */
 	inline OrderNonStopFlags GetNonStopType() const { return (OrderNonStopFlags)GB(this->type, 6, 2); }
+	/** Where must we stop at the platform? */
+	inline OrderStopLocation GetStopLocation() const { return (OrderStopLocation)GB(this->type, 4, 2); }
 	/** What caused us going to the depot? */
 	inline OrderDepotTypeFlags GetDepotOrderType() const { return (OrderDepotTypeFlags)GB(this->flags, 0, 4); }
 	/** What are we going to do when in the depot. */
@@ -187,6 +189,8 @@
 	inline void SetUnloadType(OrderUnloadFlags unload_type) { SB(this->flags, 0, 4, unload_type); }
 	/** Set whether we must stop at stations or not. */
 	inline void SetNonStopType(OrderNonStopFlags non_stop_type) { SB(this->type, 6, 2, non_stop_type); }
+	/** Set where we must stop at the platform. */
+	inline void SetStopLocation(OrderStopLocation stop_location) { SB(this->type, 4, 2, stop_location); }
 	/** Set the cause to go to the depot. */
 	inline void SetDepotOrderType(OrderDepotTypeFlags depot_order_type) { SB(this->flags, 0, 4, depot_order_type); }
 	/** Set what we are going to do in the depot. */
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -454,6 +454,20 @@
 				case OUF_UNLOAD_IF_POSSIBLE: case OUFB_UNLOAD: case OUFB_TRANSFER: case OUFB_NO_UNLOAD: break;
 				default: return CMD_ERROR;
 			}
+
+			/* Filter invalid stop locations */
+			switch (new_order.GetStopLocation()) {
+				case OSL_PLATFORM_NEAR_END:
+				case OSL_PLATFORM_MIDDLE:
+					if (v->type != VEH_TRAIN) return CMD_ERROR;
+					/* FALL THROUGH */
+				case OSL_PLATFORM_FAR_END:
+					break;
+
+				default:
+					return CMD_ERROR;
+			}
+
 			break;
 		}
 
@@ -878,6 +892,11 @@
 			if (data == order->GetNonStopType()) return CMD_ERROR;
 			break;
 
+		case MOF_STOP_LOCATION:
+			if (v->type != VEH_TRAIN) return CMD_ERROR;
+			if (data >= OSL_END) return CMD_ERROR;
+			break;
+
 		case MOF_UNLOAD:
 			if ((data & ~(OUFB_UNLOAD | OUFB_TRANSFER | OUFB_NO_UNLOAD)) != 0) return CMD_ERROR;
 			/* Unload and no-unload are mutual exclusive and so are transfer and no unload. */
@@ -939,6 +958,10 @@
 				order->SetNonStopType((OrderNonStopFlags)data);
 				break;
 
+			case MOF_STOP_LOCATION:
+				order->SetStopLocation((OrderStopLocation)data);
+				break;
+
 			case MOF_UNLOAD:
 				order->SetUnloadType((OrderUnloadFlags)data);
 				if ((data & OUFB_NO_UNLOAD) != 0 && (order->GetLoadType() & OLFB_NO_LOAD) != 0) {
--- a/src/order_gui.cpp
+++ b/src/order_gui.cpp
@@ -202,6 +202,9 @@
 				}
 			} else {
 				SetDParam(4, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) ? STR_EMPTY : _station_load_types[unload][load]);
+				if (v->type == VEH_TRAIN) {
+					SetDParam(6, order->GetStopLocation() + STR_ORDER_STOP_LOCATION_NEAR_END);
+				}
 			}
 		} break;
 
@@ -361,6 +364,7 @@
 				order.MakeGoToStation(st_index);
 				if (_ctrl_pressed) order.SetLoadType(OLF_FULL_LOAD_ANY);
 				if (_settings_client.gui.new_nonstop && (v->type == VEH_TRAIN || v->type == VEH_ROAD)) order.SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
+				order.SetStopLocation(v->type == VEH_TRAIN ? (OrderStopLocation)(_settings_client.gui.stop_location) : OSL_PLATFORM_FAR_END);
 				return order;
 			}
 		}
@@ -868,9 +872,15 @@
 				this->DeleteChildWindows();
 				HideDropDownMenu(this);
 
-				if (sel == INVALID_ORDER || sel == this->selected_order) {
+				if (sel == INVALID_ORDER) {
 					/* Deselect clicked order */
 					this->selected_order = -1;
+				} else if (sel == this->selected_order) {
+					if (this->vehicle->type == VEH_TRAIN) {
+						DoCommandP(this->vehicle->tile, this->vehicle->index + (sel << 16),
+								MOF_STOP_LOCATION | ((GetVehicleOrder(this->vehicle, sel)->GetStopLocation() + 1) % OSL_END) << 4,
+								CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
+					}
 				} else {
 					/* Select clicked order */
 					this->selected_order = sel;
--- a/src/order_type.h
+++ b/src/order_type.h
@@ -72,6 +72,16 @@
 };
 
 /**
+ * Where to stop the trains.
+ */
+enum OrderStopLocation {
+	OSL_PLATFORM_NEAR_END = 0, ///< Stop at the near end of the platform
+	OSL_PLATFORM_MIDDLE   = 1, ///< Stop at the middle of the platform
+	OSL_PLATFORM_FAR_END  = 2, ///< Stop at the far end of the platform
+	OSL_END
+};
+
+/**
  * Reasons that could cause us to go to the depot.
  */
 enum OrderDepotTypeFlags {
@@ -122,7 +132,8 @@
  * Enumeration for the data to set in CmdModifyOrder.
  */
 enum ModifyOrderFlags {
-	MOF_NON_STOP,        ///< Passes a OrderNonStopFlags.
+	MOF_NON_STOP,        ///< Passes an OrderNonStopFlags.
+	MOF_STOP_LOCATION,   ///< Passes an OrderStopLocation.
 	MOF_UNLOAD,          ///< Passes an OrderUnloadType.
 	MOF_LOAD,            ///< Passes an OrderLoadType
 	MOF_DEPOT_ACTION,    ///< Selects the OrderDepotAction
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -1797,6 +1797,14 @@
 		}
 	}
 
+	/* Trains could now stop in a specific location. */
+	if (CheckSavegameVersion(117)) {
+		Order *o;
+		FOR_ALL_ORDERS(o) {
+			if (o->IsType(OT_GOTO_STATION)) o->SetStopLocation(OSL_PLATFORM_FAR_END);
+		}
+	}
+
 	AfterLoadLabelMaps();
 
 	GamelogPrintDebug(1);
--- a/src/saveload/order_sl.cpp
+++ b/src/saveload/order_sl.cpp
@@ -39,6 +39,8 @@
 			this->SetLoadType(_settings_client.gui.sg_full_load_any || CheckSavegameVersion(22) ? OLF_FULL_LOAD_ANY : OLFB_FULL_LOAD);
 		}
 
+		if (this->IsType(OT_GOTO_STATION)) this->SetStopLocation(OSL_PLATFORM_FAR_END);
+
 		/* Finally fix the unload flags */
 		if ((old_flags & 1) != 0) { // OFB_TRANSFER
 			this->SetUnloadType(OUFB_TRANSFER);
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -40,7 +40,7 @@
 
 #include "saveload_internal.h"
 
-extern const uint16 SAVEGAME_VERSION = 116;
+extern const uint16 SAVEGAME_VERSION = 117;
 
 SavegameType _savegame_type; ///< type of savegame we are loading
 
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -1280,6 +1280,7 @@
 	SettingEntry("vehicle.wagon_speed_limits"),
 	SettingEntry("vehicle.disable_elrails"),
 	SettingEntry("vehicle.freight_trains"),
+	SettingEntry("gui.stop_location"),
 };
 /** Trains sub-page */
 static SettingsPage _settings_vehicles_trains_page = {_settings_vehicles_trains, lengthof(_settings_vehicles_trains)};
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -43,6 +43,7 @@
 	bool   show_finances;                    ///< show finances at end of year
 	bool   sg_new_nonstop;                   ///< ttdpatch compatible nonstop handling read from pre v93 savegames
 	bool   new_nonstop;                      ///< ttdpatch compatible nonstop handling
+	uint8  stop_location;                    ///< what is the default stop location of trains?
 	bool   autoscroll;                       ///< scroll when moving mouse to the edge
 	byte   errmsg_duration;                  ///< duration of error message
 	bool   link_terraform_toolbar;           ///< display terraform toolbar when displaying rail, road, water and airport toolbars
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -2671,25 +2671,35 @@
 
 	if (v->type == VEH_TRAIN) {
 		if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
-		if (IsRailwayStation(tile) && IsFrontEngine(v) &&
-				!IsCompatibleTrainStationTile(tile + TileOffsByDiagDir(DirToDiagDir(v->direction)), tile)) {
-			DiagDirection dir = DirToDiagDir(v->direction);
-
-			x &= 0xF;
-			y &= 0xF;
-
-			if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
-			if (y == TILE_SIZE / 2) {
-				if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
-				int stop = TILE_SIZE - (v->u.rail.cached_veh_length + 1) / 2;
-				if (x == stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station
-				if (x < stop) {
-					uint16 spd;
-
-					v->vehstatus |= VS_TRAIN_SLOWING;
-					spd = max(0, (stop - x) * 20 - 15);
-					if (spd < v->cur_speed) v->cur_speed = spd;
-				}
+		if (!IsRailwayStation(tile) || !IsFrontEngine(v)) return VETSB_CONTINUE;
+
+		int station_ahead;
+		int station_length;
+		int stop = GetTrainStopLocation(station_id, tile, v, &station_ahead, &station_length);
+
+		/* Stop whenever that amount of station ahead + the distance from the
+		 * begin of the platform to the stop location is longer than the length
+		 * of the platform. Station ahead 'includes' the current tile where the
+		 * vehicle is on, so we need to substract that. */
+		if (station_length <= stop + station_ahead - TILE_SIZE) return VETSB_CONTINUE;
+
+		DiagDirection dir = DirToDiagDir(v->direction);
+
+		x &= 0xF;
+		y &= 0xF;
+
+		if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
+		if (y == TILE_SIZE / 2) {
+			if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
+			stop &= TILE_SIZE - 1;
+
+			if (x == stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station
+			if (x < stop) {
+				uint16 spd;
+
+				v->vehstatus |= VS_TRAIN_SLOWING;
+				spd = max(0, (stop - x) * 20 - 15);
+				if (spd < v->cur_speed) v->cur_speed = spd;
 			}
 		}
 	} else if (v->type == VEH_ROAD) {
--- a/src/table/settings.h
+++ b/src/table/settings.h
@@ -550,6 +550,7 @@
 	  SDTC_VAR(gui.autorenew_money,            SLE_UINT, S, CR,100000,        0,  2000000, 0, STR_CONFIG_SETTING_AUTORENEW_MONEY,             EngineRenewMoneyUpdate),
 	 SDTC_BOOL(gui.always_build_infrastructure,          S,  0, false,                        STR_CONFIG_SETTING_ALWAYS_BUILD_INFRASTRUCTURE, RedrawScreen),
 	 SDTC_BOOL(gui.new_nonstop,                          S,  0, false,                        STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT,          NULL),
+	  SDTC_VAR(gui.stop_location,             SLE_UINT8, S, MS,     2,        0,        2, 1, STR_CONFIG_SETTING_STOP_LOCATION,               NULL),
 	 SDTC_BOOL(gui.keep_all_autosave,                    S,  0, false,                        STR_NULL,                                       NULL),
 	 SDTC_BOOL(gui.autosave_on_exit,                     S,  0, false,                        STR_NULL,                                       NULL),
 	  SDTC_VAR(gui.max_num_autosaves,         SLE_UINT8, S,  0,    16,        0,      255, 0, STR_NULL,                                       NULL),
--- a/src/train.h
+++ b/src/train.h
@@ -300,6 +300,8 @@
 void FreeTrainTrackReservation(const Vehicle *v, TileIndex origin = INVALID_TILE, Trackdir orig_td = INVALID_TRACKDIR);
 bool TryPathReserve(Vehicle *v, bool mark_as_stuck = false, bool first_tile_okay = false);
 
+int GetTrainStopLocation(StationID station_id, TileIndex tile, const Vehicle *v, int *station_ahead, int *station_length);
+
 /**
  * This class 'wraps' Vehicle; you do not actually instantiate this class.
  * You create a Vehicle using AllocateVehicle, so it is added to the pool
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -354,6 +354,55 @@
 	AM_BRAKE
 };
 
+/**
+ * Get the stop location of (the center) of the front vehicle of a train at
+ * a platform of a station.
+ * @param station_id     the ID of the station where we're stopping
+ * @param tile           the tile where the vehicle currently is
+ * @param v              the vehicle to get the stop location of
+ * @param station_ahead  'return' the amount of 1/16th tiles in front of the train
+ * @param station_length 'return' the station length in 1/16th tiles
+ * @return the location, calculated from the begin of the station to stop at.
+ */
+int GetTrainStopLocation(StationID station_id, TileIndex tile, const Vehicle *v, int *station_ahead, int *station_length)
+{
+	const Station *st = GetStation(station_id);
+	*station_ahead  = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE;
+	*station_length = st->GetPlatformLength(tile) * TILE_SIZE;
+
+	/* Default to the middle of the station for stations stops that are not in
+	 * the order list like intermediate stations when non-stop is disabled */
+	OrderStopLocation osl = OSL_PLATFORM_MIDDLE;
+	if (v->u.rail.cached_total_length >= *station_length) {
+		/* The train is longer than the station, make it stop at the far end of the platform */
+		osl = OSL_PLATFORM_FAR_END;
+	} else if (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == station_id) {
+		osl = v->current_order.GetStopLocation();
+	}
+
+	/* The stop location of the FRONT! of the train */
+	int stop;
+	switch (osl) {
+		default: NOT_REACHED();
+
+		case OSL_PLATFORM_NEAR_END:
+			stop = v->u.rail.cached_total_length;
+			break;
+
+		case OSL_PLATFORM_MIDDLE:
+			stop = *station_length - (*station_length - v->u.rail.cached_total_length) / 2;
+			break;
+
+		case OSL_PLATFORM_FAR_END:
+			stop = *station_length;
+			break;
+	}
+
+	/* Substract half the front vehicle length of the train so we get the real
+	 * stop location of the train. */
+	return stop - (v->u.rail.cached_veh_length + 1) / 2;
+}
+
 /** new acceleration*/
 static int GetTrainAcceleration(Vehicle *v, bool mode)
 {
@@ -416,17 +465,23 @@
 	}
 
 	if (IsTileType(v->tile, MP_STATION) && IsFrontEngine(v)) {
-		if (v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile))) {
-			int station_length = GetStationByTile(v->tile)->GetPlatformLength(v->tile, DirToDiagDir(v->direction));
-
+		StationID sid = GetStationIndex(v->tile);
+		if (v->current_order.ShouldStopAtStation(v, sid)) {
+			int station_ahead;
+			int station_length;
+			int stop_at = GetTrainStopLocation(sid, v->tile, v, &station_ahead, &station_length);
+
+			/* The distance to go is whatever is still ahead of the train minus the
+			 * distance from the train's stop location to the end of the platform */
+			int distance_to_go = station_ahead / TILE_SIZE - (station_length - stop_at) / TILE_SIZE;
 			int st_max_speed = 120;
 
-			int delta_v = v->cur_speed / (station_length + 1);
+			int delta_v = v->cur_speed / (distance_to_go + 1);
 			if (v->max_speed > (v->cur_speed - delta_v)) {
 				st_max_speed = v->cur_speed - (delta_v / 10);
 			}
 
-			st_max_speed = max(st_max_speed, 25 * station_length);
+			st_max_speed = max(st_max_speed, 25 * distance_to_go);
 			max_speed = min(max_speed, st_max_speed);
 		}
 	}
@@ -3781,6 +3836,11 @@
 					/* Always try to extend the reservation when entering a tile. */
 					CheckNextTrainTile(v);
 				}
+
+				if (HasBit(r, VETS_ENTERED_STATION)) {
+					/* The new position is the location where we want to stop */
+					TrainEnterStation(v, r >> VETS_STATION_ID_OFFSET);
+				}
 			}
 		} else {
 			/* In a tunnel or on a bridge