diff --git a/src/lang/english.txt b/src/lang/english.txt --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -938,6 +938,10 @@ STR_CONFIG_SETTING_LONGDATE 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_TO 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 diff --git a/src/order_base.h b/src/order_base.h --- a/src/order_base.h +++ b/src/order_base.h @@ -166,8 +166,10 @@ public: 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 @@ public: 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. */ diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -454,6 +454,20 @@ CommandCost CmdInsertOrder(TileIndex til 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 @@ CommandCost CmdModifyOrder(TileIndex til 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 @@ CommandCost CmdModifyOrder(TileIndex til 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) { diff --git a/src/order_gui.cpp b/src/order_gui.cpp --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -202,6 +202,9 @@ void DrawOrderString(const Vehicle *v, c } } 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 @@ static Order GetOrderCmdFromTile(const V 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 @@ public: 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; diff --git a/src/order_type.h b/src/order_type.h --- a/src/order_type.h +++ b/src/order_type.h @@ -72,6 +72,16 @@ enum OrderNonStopFlags { }; /** + * 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 @@ enum OrderConditionComparator { * 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 diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -1797,6 +1797,14 @@ bool AfterLoadGame() } } + /* 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); diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -39,6 +39,8 @@ void Order::ConvertFromOldSavegame() 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); diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp --- 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 diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1280,6 +1280,7 @@ static SettingEntry _settings_vehicles_t 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)}; diff --git a/src/settings_type.h b/src/settings_type.h --- a/src/settings_type.h +++ b/src/settings_type.h @@ -43,6 +43,7 @@ struct GUISettings { 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 diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2671,25 +2671,35 @@ static VehicleEnterTileStatus VehicleEnt 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) { diff --git a/src/table/settings.h b/src/table/settings.h --- a/src/table/settings.h +++ b/src/table/settings.h @@ -550,6 +550,7 @@ const SettingDesc _settings[] = { 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), diff --git a/src/train.h b/src/train.h --- a/src/train.h +++ b/src/train.h @@ -300,6 +300,8 @@ void CheckTrainsLengths(); 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 diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -354,6 +354,55 @@ enum AccelType { 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 @@ static int GetTrainAcceleration(Vehicle } 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 @@ static void TrainController(Vehicle *v, /* 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