Add freight train load/unload mode: through load

This commit is contained in:
Jonathan G Rennison
2018-03-26 20:12:50 +01:00
parent 52edafab5e
commit 78838ee2a2
21 changed files with 291 additions and 75 deletions

View File

@@ -295,7 +295,7 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
status = D_CANCELLED;
}
if ((*v)->current_order.IsType(OT_LOADING)) {
if ((*v)->current_order.IsAnyLoadingType()) {
/* Account for the vehicle having reached the current order and being in the loading phase. */
status = D_ARRIVED;
start_date -= order->GetTravelTime() + (((*v)->lateness_counter < 0) ? (*v)->lateness_counter : 0);
@@ -381,7 +381,7 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
}
/* If we are early, use the scheduled date as the expected date. We also take lateness to be zero. */
if (!should_reset_lateness && (*v)->lateness_counter < 0 && !(*v)->current_order.IsType(OT_LOADING)) {
if (!should_reset_lateness && (*v)->lateness_counter < 0 && !(*v)->current_order.IsAnyLoadingType()) {
od->expected_date -= (*v)->lateness_counter;
}

View File

@@ -1646,12 +1646,13 @@ static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft,
* @param front The vehicle to be updated.
* @param st The station the vehicle is loading at.
* @param ticks The time it would normally wait, based on cargo loaded and unloaded.
* @param platform_length_left Platform length left, negative values indicate train is overhanging platform
*/
static void UpdateLoadUnloadTicks(Vehicle *front, const Station *st, int ticks)
static void UpdateLoadUnloadTicks(Vehicle *front, const Station *st, int ticks, int platform_length_left)
{
if (front->type == VEH_TRAIN) {
/* Each platform tile is worth 2 rail vehicles. */
int overhang = front->GetGroundVehicleCache()->cached_total_length - st->GetPlatformLength(front->tile) * TILE_SIZE;
int overhang = -platform_length_left;
if (overhang > 0) {
ticks <<= 1;
ticks += (overhang * ticks) / 8;
@@ -1672,13 +1673,39 @@ static void LoadUnloadVehicle(Vehicle *front)
StationID last_visited = front->last_station_visited;
Station *st = Station::Get(last_visited);
TileIndex station_tile = front->tile;
if (front->type == VEH_TRAIN) station_tile = Train::From(front)->GetStationLoadingVehicle()->tile;
bool pull_through_mode = false;
bool load_unload_not_yet_in_station = false;
if (front->type == VEH_TRAIN && front->cur_real_order_index < front->GetNumOrders()) {
Order *order = front->GetOrder(front->cur_real_order_index);
if (order->IsType(OT_GOTO_STATION) && order->GetDestination() == last_visited &&
order->GetStopLocation() == OSL_PLATFORM_THROUGH) {
pull_through_mode = true;
for (Vehicle *v = front; v != NULL; v = v->Next()) {
/* Passengers may not be through-loaded */
if (v->cargo_cap > 0 && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
pull_through_mode = false;
break;
}
}
}
}
int platform_length_left = 0;
if (pull_through_mode) {
platform_length_left = st->GetPlatformLength(station_tile) * TILE_SIZE;
} else if (front->type == VEH_TRAIN) {
platform_length_left = st->GetPlatformLength(station_tile) * TILE_SIZE - front->GetGroundVehicleCache()->cached_total_length;
}
CargoStationIDStackSet next_station = front->GetNextStoppingStation();
bool use_autorefit = front->current_order.IsRefit() && front->current_order.GetRefitCargo() == CT_AUTO_REFIT;
CargoArray consist_capleft;
bool should_reserve_consist = false;
bool reserve_consist_cargo_type_loading = false;
if (_settings_game.order.improved_load && use_autorefit) {
if ((_settings_game.order.improved_load && use_autorefit) || pull_through_mode) {
if (front->cargo_payment == NULL) should_reserve_consist = true;
} else {
if ((front->current_order.GetLoadType() & OLFB_FULL_LOAD) || (front->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD)) {
@@ -1696,7 +1723,7 @@ static void LoadUnloadVehicle(Vehicle *front)
/* We have not waited enough time till the next round of loading/unloading */
if (front->load_unload_ticks != 0) return;
if (front->type == VEH_TRAIN && (!IsTileType(front->tile, MP_STATION) || GetStationIndex(front->tile) != st->index)) {
if (front->type == VEH_TRAIN && (!IsTileType(station_tile, MP_STATION) || GetStationIndex(station_tile) != st->index)) {
/* The train reversed in the station. Take the "easy" way
* out and let the train just leave as it always did. */
SetBit(front->vehicle_flags, VF_LOADING_FINISHED);
@@ -1722,6 +1749,25 @@ static void LoadUnloadVehicle(Vehicle *front)
uint artic_part = 0; // Articulated part we are currently trying to load. (not counting parts without capacity)
for (Vehicle *v = front; v != NULL; v = v->Next()) {
if (pull_through_mode && HasBit(Train::From(v)->flags, VRF_BEYOND_PLATFORM_END)) continue;
if (pull_through_mode && !v->IsArticulatedPart()) {
int length = Train::From(v)->gcache.cached_veh_length;
Vehicle *u = v;
while (u->HasArticulatedPart()) {
u = u->GetNextArticulatedPart();
length += Train::From(u)->gcache.cached_veh_length;
}
if (v != front && !HasBit(Train::From(v->Previous())->flags, VRF_BEYOND_PLATFORM_END) && length > platform_length_left) {
for (Vehicle *skip = v; skip != NULL; skip = skip->Next()) {
SetBit(Train::From(skip)->flags, VRF_NOT_YET_IN_PLATFORM);
if (skip->cargo.ReservedCount() || skip->cargo.UnloadCount()) {
load_unload_not_yet_in_station = true;
}
}
break; // articulated vehicle won't fit in platform, no loading
}
platform_length_left -= length;
}
if (v == front || !v->Previous()->HasArticulatedPart()) artic_part = 0;
if (v->cargo_cap == 0) continue;
artic_part++;
@@ -1895,7 +1941,7 @@ static void LoadUnloadVehicle(Vehicle *front)
/* Only set completely_emptied, if we just unloaded all remaining cargo */
completely_emptied &= anything_unloaded;
if (!anything_unloaded) delete payment;
if (!anything_unloaded && !load_unload_not_yet_in_station) delete payment;
ClrBit(front->vehicle_flags, VF_STOP_LOADING);
@@ -1923,9 +1969,9 @@ static void LoadUnloadVehicle(Vehicle *front)
SetBit(front->vehicle_flags, VF_STOP_LOADING);
}
UpdateLoadUnloadTicks(front, st, new_load_unload_ticks);
UpdateLoadUnloadTicks(front, st, new_load_unload_ticks, platform_length_left);
} else {
UpdateLoadUnloadTicks(front, st, 20); // We need the ticks for link refreshing.
UpdateLoadUnloadTicks(front, st, 20, platform_length_left); // We need the ticks for link refreshing.
bool finished_loading = true;
if (has_full_load_order) {
if (front->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {
@@ -1938,6 +1984,12 @@ static void LoadUnloadVehicle(Vehicle *front)
} else if (cargo_not_full != 0) {
finished_loading = false;
}
}
if (pull_through_mode && (front->current_order.IsRefit() || load_unload_not_yet_in_station)) {
finished_loading = false;
SetBit(Train::From(front)->flags, VRF_ADVANCE_IN_PLATFORM);
}
/* Refresh next hop stats if we're full loading to make the links
* known to the distribution algorithm and allow cargo to be sent
@@ -1945,7 +1997,6 @@ static void LoadUnloadVehicle(Vehicle *front)
* indefinitely if it hasn't visited the other links yet, or if the
* links die while it's loading. */
if (!finished_loading) LinkRefresher::Run(front, true, true);
}
SB(front->vehicle_flags, VF_LOADING_FINISHED, 1, finished_loading);
}
@@ -1956,7 +2007,8 @@ static void LoadUnloadVehicle(Vehicle *front)
* if _settings_client.gui.loading_indicators == 1, _local_company must be the owner or must be a spectator to show ind., so 1 > 0
* if _settings_client.gui.loading_indicators == 0, do not display indicators ... 0 is never greater than anything
*/
if (_game_mode != GM_MENU && (_settings_client.gui.loading_indicators > (uint)(front->owner != _local_company && _local_company != COMPANY_SPECTATOR))) {
if (_game_mode != GM_MENU && (_settings_client.gui.loading_indicators > (uint)(front->owner != _local_company && _local_company != COMPANY_SPECTATOR))
&& !front->current_order.IsType(OT_LOADING_ADVANCE)) {
StringID percent_up_down = STR_NULL;
int percent = CalcPercentVehicleFilled(front, &percent_up_down);
if (front->fill_percent_te_id == INVALID_TE_ID) {
@@ -1999,7 +2051,7 @@ void LoadUnloadStation(Station *st)
/* Check if anything will be loaded at all. Otherwise we don't need to reserve either. */
for (Vehicle *v : st->loading_vehicles) {
if ((v->vehstatus & (VS_STOPPED | VS_CRASHED))) continue;
if ((v->vehstatus & (VS_STOPPED | VS_CRASHED)) || v->current_order.IsType(OT_LOADING_ADVANCE)) continue;
assert(v->load_unload_ticks != 0);
if (--v->load_unload_ticks == 0) last_loading = v;
@@ -2015,7 +2067,7 @@ void LoadUnloadStation(Station *st)
if (last_loading == NULL) return;
for (Vehicle *v : st->loading_vehicles) {
if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v);
if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED)) && !v->current_order.IsType(OT_LOADING_ADVANCE)) LoadUnloadVehicle(v);
if (v == last_loading) break;
}

View File

@@ -418,6 +418,8 @@ protected:
}
}
if (this->current_order.IsType(OT_LOADING_ADVANCE)) tempmax = min(tempmax, 15);
if (this->cur_speed > max_speed) {
tempmax = max(this->cur_speed - (this->cur_speed / 10) - 1, max_speed);
}

View File

@@ -147,6 +147,7 @@ static bool OrderDestinationIsAllowed(const Order *order, const Vehicle *v, Owne
case OT_GOTO_STATION:
case OT_GOTO_WAYPOINT: dest_owner = BaseStation::Get(order->GetDestination())->owner; break;
case OT_GOTO_DEPOT: dest_owner = (v->type == VEH_AIRCRAFT) ? Station::Get(order->GetDestination())->owner : GetTileOwner(Depot::Get(order->GetDestination())->xy); break;
case OT_LOADING_ADVANCE:
case OT_LOADING: dest_owner = Station::Get(v->last_station_visited)->owner; break;
default: return true;
}
@@ -290,7 +291,7 @@ void HandleSharingCompanyDeletion(Owner owner)
}
/* current order */
if (!OrderDestinationIsAllowed(&v->current_order, v, owner)) {
if (v->current_order.IsType(OT_LOADING)) {
if (v->current_order.IsAnyLoadingType()) {
v->LeaveStation();
} else {
v->current_order.MakeDummy();

View File

@@ -4223,6 +4223,7 @@ STR_VEHICLE_VIEW_AIRCRAFT_STATE_START_STOP_TOOLTIP :{BLACK}Current
# Messages in the start stop button in the vehicle view
STR_VEHICLE_STATUS_LOADING_UNLOADING :{LTBLUE}Loading / Unloading
STR_VEHICLE_STATUS_LOADING_UNLOADING_ADVANCE :{STRING}, {VELOCITY}
STR_VEHICLE_STATUS_LEAVING :{LTBLUE}Leaving
STR_VEHICLE_STATUS_CRASHED :{RED}Crashed!
STR_VEHICLE_STATUS_BROKEN_DOWN :{RED}Broken down
@@ -4561,6 +4562,7 @@ STR_ORDER_AUTO_REFIT_ANY :available cargo
STR_ORDER_STOP_LOCATION_NEAR_END :[near end]
STR_ORDER_STOP_LOCATION_MIDDLE :[middle]
STR_ORDER_STOP_LOCATION_FAR_END :[far end]
STR_ORDER_STOP_LOCATION_THROUGH :[through load]
STR_ORDER_OUT_OF_RANGE :{RED} (Next destination is out of range)

View File

@@ -114,6 +114,12 @@ public:
*/
inline bool IsType(OrderType type) const { return this->GetType() == type; }
/**
* Check whether this order is either of OT_LOADING or OT_LOADING_ADVANCE.
* @return true if the order matches.
*/
inline bool IsAnyLoadingType() const { return this->GetType() == OT_LOADING || this->GetType() == OT_LOADING_ADVANCE; }
/**
* Get the type of order of this order.
* @return the order type.
@@ -131,6 +137,7 @@ public:
void MakeConditional(VehicleOrderID order);
void MakeImplicit(StationID destination);
void MakeWaiting();
void MakeLoadingAdvance(StationID destination);
/**
* Is this a 'goto' order with a real destination?

View File

@@ -193,6 +193,12 @@ void Order::MakeWaiting()
this->type = OT_WAITING;
}
void Order::MakeLoadingAdvance(StationID destination)
{
this->type = OT_LOADING_ADVANCE;
this->dest = destination;
}
/**
* Make this depot/station order also a refit order.
* @param cargo the cargo type to change to.
@@ -898,6 +904,7 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
switch (new_order.GetStopLocation()) {
case OSL_PLATFORM_NEAR_END:
case OSL_PLATFORM_MIDDLE:
case OSL_PLATFORM_THROUGH:
if (v->type != VEH_TRAIN) return CMD_ERROR;
FALLTHROUGH;
@@ -1232,6 +1239,11 @@ CommandCost CmdDeleteOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
*/
static void CancelLoadingDueToDeletedOrder(Vehicle *v)
{
if (v->current_order.IsType(OT_LOADING_ADVANCE)) {
SetBit(v->vehicle_flags, VF_LOADING_FINISHED);
return;
}
assert(v->current_order.IsType(OT_LOADING));
/* NON-stop flag is misused to see if a train is in a station that is
* on his order list or not */
@@ -1255,7 +1267,7 @@ void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord)
for (; u != NULL; u = u->NextShared()) {
assert(v->orders.list == u->orders.list);
if (sel_ord == u->cur_real_order_index && u->current_order.IsType(OT_LOADING)) {
if (sel_ord == u->cur_real_order_index && u->current_order.IsAnyLoadingType()) {
CancelLoadingDueToDeletedOrder(u);
}
@@ -1332,7 +1344,7 @@ CommandCost CmdSkipToOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (ret.Failed()) return ret;
if (flags & DC_EXEC) {
if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
if (v->current_order.IsAnyLoadingType()) v->LeaveStation();
if (v->current_order.IsType(OT_WAITING)) v->HandleWaiting(true);
v->cur_implicit_order_index = v->cur_real_order_index = sel_ord;
@@ -1741,7 +1753,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
* when this function is called.
*/
if (sel_ord == u->cur_real_order_index &&
(u->current_order.IsType(OT_GOTO_STATION) || u->current_order.IsType(OT_LOADING)) &&
(u->current_order.IsType(OT_GOTO_STATION) || u->current_order.IsAnyLoadingType()) &&
u->current_order.GetLoadType() != order->GetLoadType()) {
u->current_order.SetLoadType(order->GetLoadType());
}
@@ -2214,7 +2226,7 @@ void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist, bool reset_order_indic
if (reset_order_indices) {
v->cur_implicit_order_index = v->cur_real_order_index = 0;
v->cur_timetable_order_index = INVALID_VEH_ORDER_ID;
if (v->current_order.IsType(OT_LOADING)) {
if (v->current_order.IsAnyLoadingType()) {
CancelLoadingDueToDeletedOrder(v);
}
}
@@ -2512,6 +2524,9 @@ bool ProcessOrders(Vehicle *v)
case OT_LOADING:
return false;
case OT_LOADING_ADVANCE:
return false;
case OT_WAITING:
return false;
@@ -2605,6 +2620,7 @@ bool ProcessOrders(Vehicle *v)
*/
bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const
{
if (this->IsType(OT_LOADING_ADVANCE) && this->dest == station) return true;
bool is_dest_station = this->IsType(OT_GOTO_STATION) && this->dest == station;
return (!this->IsType(OT_GOTO_DEPOT) || (this->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0) &&

View File

@@ -1849,8 +1849,18 @@ public:
this->selected_order = -1;
} else if (sel == this->selected_order) {
if (this->vehicle->type == VEH_TRAIN && sel < this->vehicle->GetNumOrders()) {
int osl = ((this->vehicle->GetOrder(sel)->GetStopLocation() + 1) % OSL_END);
if (osl == OSL_PLATFORM_THROUGH) {
for (const Vehicle *u = this->vehicle; u != NULL; u = u->Next()) {
/* Passengers may not be through-loaded */
if (u->cargo_cap > 0 && IsCargoInClass(u->cargo_type, CC_PASSENGERS)) {
osl = OSL_PLATFORM_NEAR_END;
break;
}
}
}
DoCommandP(this->vehicle->tile, this->vehicle->index + (sel << 20),
MOF_STOP_LOCATION | ((this->vehicle->GetOrder(sel)->GetStopLocation() + 1) % OSL_END) << 4,
MOF_STOP_LOCATION | osl << 4,
CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
}
} else {

View File

@@ -46,6 +46,7 @@ enum OrderType {
OT_CONDITIONAL = 7,
OT_IMPLICIT = 8,
OT_WAITING = 9,
OT_LOADING_ADVANCE = 10,
OT_END
};
@@ -95,6 +96,7 @@ 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_PLATFORM_THROUGH = 3, ///< Load/unload through the platform
OSL_END
};

View File

@@ -3180,6 +3180,8 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int
/* this routine applies only to trains in depot tiles */
if (u->type != VEH_TRAIN || !IsRailDepotTile(tile)) return VETSB_CONTINUE;
if (u->current_order.IsType(OT_LOADING_ADVANCE)) u->LeaveStation();
Train *v = Train::From(u);
/* depot direction */

View File

@@ -81,6 +81,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_MULTIPLE_DOCKS, XSCF_NULL, 1, 1, "multiple_docks", NULL, NULL, "DOCK" },
{ XSLFI_TIMETABLE_EXTRA, XSCF_NULL, 1, 1, "timetable_extra", NULL, NULL, "ORDX" },
{ XSLFI_TRAIN_FLAGS_EXTRA, XSCF_NULL, 1, 1, "train_flags_extra", NULL, NULL, NULL },
{ XSLFI_TRAIN_THROUGH_LOAD, XSCF_NULL, 1, 1, "train_through_load", NULL, NULL, NULL },
{ XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker
};

View File

@@ -55,6 +55,7 @@ enum SlXvFeatureIndex {
XSLFI_MULTIPLE_DOCKS, ///< Multiple docks
XSLFI_TIMETABLE_EXTRA, ///< Vehicle timetable extra fields
XSLFI_TRAIN_FLAGS_EXTRA, ///< Train flags field extra size
XSLFI_TRAIN_THROUGH_LOAD, ///< Train through load/unload
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk

View File

@@ -328,7 +328,7 @@
if (v->breakdown_ctr != 0) return ScriptVehicle::VS_BROKEN;
if (v->IsStoppedInDepot()) return ScriptVehicle::VS_IN_DEPOT;
if (vehstatus & ::VS_STOPPED) return ScriptVehicle::VS_STOPPED;
if (v->current_order.IsType(OT_LOADING)) return ScriptVehicle::VS_AT_STATION;
if (v->current_order.IsAnyLoadingType()) return ScriptVehicle::VS_AT_STATION;
return ScriptVehicle::VS_RUNNING;
}

View File

@@ -3300,8 +3300,12 @@ static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, i
// reverse at waypoint
if (t->reverse_distance == 0) t->reverse_distance = t->gcache.cached_total_length;
}
if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
if (!IsRailStation(tile) || !v->IsFrontEngine()) return VETSB_CONTINUE;
if (HasBit(Train::From(v)->flags, VRF_BEYOND_PLATFORM_END)) return VETSB_CONTINUE;
Train *front = Train::From(v)->First();
if (!front->IsFrontEngine()) return VETSB_CONTINUE;
if (!(v == front || HasBit(Train::From(v)->Previous()->flags, VRF_BEYOND_PLATFORM_END))) return VETSB_CONTINUE;
if (!IsRailStation(tile)) return VETSB_CONTINUE;
if (!front->current_order.ShouldStopAtStation(front, station_id)) return VETSB_CONTINUE;
int station_ahead;
int station_length;
@@ -3326,9 +3330,9 @@ static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, i
if (x == stop) {
return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station
} else if (x < stop) {
v->vehstatus |= VS_TRAIN_SLOWING;
front->vehstatus |= VS_TRAIN_SLOWING;
uint16 spd = max(0, (stop - x) * 20 - 15);
if (spd < v->cur_speed) v->cur_speed = spd;
if (spd < front->cur_speed) front->cur_speed = spd;
}
}
} else if (v->type == VEH_ROAD) {

View File

@@ -292,8 +292,8 @@ static int CDECL VehicleTimetableSorter(Vehicle * const *ap, Vehicle * const *bp
int j = (int)b_order - (int)a_order;
/* Are we currently at an ordered station (un)loading? */
bool a_load = a->current_order.IsType(OT_LOADING) && a->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
bool b_load = b->current_order.IsType(OT_LOADING) && b->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
bool a_load = (a->current_order.IsType(OT_LOADING) && a->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) || a->current_order.IsType(OT_LOADING_ADVANCE);
bool b_load = (b->current_order.IsType(OT_LOADING) && b->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) || b->current_order.IsType(OT_LOADING_ADVANCE);
/* If the current order is not loading at the ordered station, decrease the order index by one since we have
* not yet arrived at the station (and thus the timetable entry; still in the travelling of the previous one).

View File

@@ -215,7 +215,7 @@ struct TimetableWindow : Window {
{
assert(HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED));
bool travelling = (!(v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_WAITING)) || v->current_order.GetNonStopType() == ONSF_STOP_EVERYWHERE);
bool travelling = (!(v->current_order.IsAnyLoadingType() || v->current_order.IsType(OT_WAITING)) || v->current_order.GetNonStopType() == ONSF_STOP_EVERYWHERE);
Ticks start_time = -v->current_order_time;
if (v->cur_timetable_order_index != INVALID_VEH_ORDER_ID && v->cur_timetable_order_index != v->cur_real_order_index) {
/* vehicle is taking a conditional order branch, adjust start time to compensate */

View File

@@ -41,6 +41,9 @@ enum VehicleRailFlags {
VRF_BREAKDOWN_STOPPED = 13,///< used to mark a train that is stopped because of a breakdown
VRF_NEED_REPAIR = 14,///< used to mark a train that has a reduced maximum speed because of a critical breakdown
VRF_TOO_HEAVY = 15,
VRF_BEYOND_PLATFORM_END = 16,
VRF_NOT_YET_IN_PLATFORM = 17,
VRF_ADVANCE_IN_PLATFORM = 18,
VRF_IS_BROKEN = (1 << VRF_BREAKDOWN_POWER) | (1 << VRF_BREAKDOWN_SPEED) | (1 << VRF_BREAKDOWN_STOPPED), ///< Bitmask of all flags that indicate a broken train (braking is not included)
};
@@ -76,7 +79,7 @@ bool TryPathReserve(Train *v, bool mark_as_stuck = false, bool first_tile_okay =
void DeleteVisibleTrain(Train *v);
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length);
int GetTrainStopLocation(StationID station_id, TileIndex tile, Train *v, int *station_ahead, int *station_length);
void CheckBreakdownFlags(Train *v);
void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type);
@@ -206,6 +209,18 @@ struct Train FINAL : public GroundVehicle<Train, VEH_TRAIN> {
return this->gcache.cached_veh_length / 2 + (this->Next() != NULL ? this->Next()->gcache.cached_veh_length + 1 : 0) / 2;
}
const Train *GetStationLoadingVehicle() const
{
const Train *v = this->First();
while (v && HasBit(v->flags, VRF_BEYOND_PLATFORM_END)) v = v->Next();
return v;
}
Train *GetStationLoadingVehicle()
{
return const_cast<Train *>(const_cast<const Train *>(this)->GetStationLoadingVehicle());
}
protected: // These functions should not be called outside acceleration code.
/**
* Gets the speed a broken down train (low speed breakdown) is limited to.

View File

@@ -341,8 +341,9 @@ void Train::ConsistChanged(ConsistChangeFlags allowed_changes)
* @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 Train *v, int *station_ahead, int *station_length)
int GetTrainStopLocation(StationID station_id, TileIndex tile, Train *v, int *station_ahead, int *station_length)
{
Train *front = v->First();
const Station *st = Station::Get(station_id);
*station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE;
*station_length = st->GetPlatformLength(tile) * TILE_SIZE;
@@ -350,11 +351,49 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, i
/* 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->gcache.cached_total_length >= *station_length) {
if (front->current_order.IsType(OT_GOTO_STATION) && front->current_order.GetDestination() == station_id) {
osl = front->current_order.GetStopLocation();
}
int overhang = front->gcache.cached_total_length - *station_length;
int adjust = 0;
if (osl == OSL_PLATFORM_THROUGH && overhang > 0) {
for (Train *u = front; u != NULL; u = u->Next()) {
/* Passengers may not be through-loaded */
if (u->cargo_cap > 0 && IsCargoInClass(u->cargo_type, CC_PASSENGERS)) {
osl = OSL_PLATFORM_FAR_END;
break;
}
}
}
if (osl == OSL_PLATFORM_THROUGH && overhang > 0) {
/* The train is longer than the station, and we can run through the station to load/unload */
for (Train *u = v; u != nullptr; u = u->Next()) {
if (overhang > 0 && !HasBit(u->flags, VRF_BEYOND_PLATFORM_END) && !u->IsArticulatedPart()) {
bool skip = true;
for (const Train *part = u; part != nullptr; part = part->HasArticulatedPart() ? part->GetNextArticulatedPart() : nullptr) {
if (part->cargo_cap != 0) {
skip = false;
break;
}
}
if (skip) {
for (Train *part = u; part != nullptr; part = part->HasArticulatedPart() ? part->GetNextArticulatedPart() : nullptr) {
SetBit(part->flags, VRF_BEYOND_PLATFORM_END);
}
}
}
if (HasBit(u->flags, VRF_BEYOND_PLATFORM_END)) {
overhang -= u->gcache.cached_veh_length;
adjust += u->gcache.cached_veh_length;
} else {
break;
}
}
for (Train *u = front; u != v; u = u->Next()) overhang -= u->gcache.cached_veh_length; // only advance until rear of train is in platform
if (overhang < 0) adjust += overhang;
} else if (overhang >= 0) {
/* 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 */
@@ -363,21 +402,22 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, i
default: NOT_REACHED();
case OSL_PLATFORM_NEAR_END:
stop = v->gcache.cached_total_length;
stop = front->gcache.cached_total_length;
break;
case OSL_PLATFORM_MIDDLE:
stop = *station_length - (*station_length - v->gcache.cached_total_length) / 2;
stop = *station_length - (*station_length -front->gcache.cached_total_length) / 2;
break;
case OSL_PLATFORM_FAR_END:
case OSL_PLATFORM_THROUGH:
stop = *station_length;
break;
}
/* Subtract half the front vehicle length of the train so we get the real
* stop location of the train. */
return stop - (v->gcache.cached_veh_length + 1) / 2;
return stop - ((v->gcache.cached_veh_length + 1) / 2) + adjust;
}
@@ -460,12 +500,15 @@ int Train::GetCurrentMaxSpeed() const
this->gcache.cached_max_track_speed :
this->tcache.cached_max_curve_speed;
if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && IsRailStationTile(this->tile)) {
StationID sid = GetStationIndex(this->tile);
if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC) {
Train *v_platform = const_cast<Train *>(this->GetStationLoadingVehicle());
TileIndex platform_tile = v_platform->tile;
if (IsRailStationTile(platform_tile)) {
StationID sid = GetStationIndex(platform_tile);
if (this->current_order.ShouldStopAtStation(this, sid)) {
int station_ahead;
int station_length;
int stop_at = GetTrainStopLocation(sid, this->tile, this, &station_ahead, &station_length);
int stop_at = GetTrainStopLocation(sid, platform_tile, v_platform, &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 */
@@ -484,6 +527,7 @@ int Train::GetCurrentMaxSpeed() const
}
}
}
}
for (const Train *u = this; u != NULL; u = u->Next()) {
if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && u->track == TRACK_BIT_DEPOT) {
@@ -2008,6 +2052,8 @@ void ReverseTrainDirection(Train *v)
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
}
if (v->current_order.IsType(OT_LOADING_ADVANCE)) v->LeaveStation();
v->reverse_distance = 0;
/* Clear path reservation in front if train is not stuck. */
@@ -2145,12 +2191,14 @@ CommandCost CmdReverseTrainDirection(TileIndex tile, DoCommandFlag flags, uint32
if (flags & DC_EXEC) {
/* Properly leave the station if we are loading and won't be loading anymore */
if (v->current_order.IsType(OT_LOADING)) {
if (v->current_order.IsAnyLoadingType()) {
const Vehicle *last = v;
while (last->Next() != NULL) last = last->Next();
/* not a station || different station --> leave the station */
if (!IsTileType(last->tile, MP_STATION) || GetStationIndex(last->tile) != GetStationIndex(v->tile)) {
if (!IsTileType(last->tile, MP_STATION) || !IsTileType(v->tile, MP_STATION) ||
GetStationIndex(last->tile) != GetStationIndex(v->tile) ||
HasBit(v->flags, VRF_BEYOND_PLATFORM_END)) {
v->LeaveStation();
}
}
@@ -2931,7 +2979,7 @@ static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir,
* order list itself is empty. */
if (v->current_order.IsType(OT_LEAVESTATION)) {
orders.SwitchToNextOrder(false);
} else if (v->current_order.IsType(OT_LOADING) || (!v->current_order.IsType(OT_GOTO_DEPOT) && (
} else if (v->current_order.IsAnyLoadingType() || (!v->current_order.IsType(OT_GOTO_DEPOT) && (
v->current_order.IsType(OT_GOTO_STATION) ?
IsRailStationTile(v->tile) && v->current_order.GetDestination() == GetStationIndex(v->tile) :
v->tile == v->dest_tile))) {
@@ -3233,8 +3281,9 @@ static void TrainEnterStation(Train *v, StationID station)
v->BeginLoading();
TriggerStationRandomisation(st, v->tile, SRT_TRAIN_ARRIVES);
TriggerStationAnimation(st, v->tile, SAT_TRAIN_ARRIVES);
TileIndex station_tile = v->GetStationLoadingVehicle()->tile;
TriggerStationRandomisation(st, station_tile, SRT_TRAIN_ARRIVES);
TriggerStationAnimation(st, station_tile, SAT_TRAIN_ARRIVES);
}
/* Check if the vehicle is compatible with the specified tile */
@@ -3691,7 +3740,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
}
if (HasBit(r, VETS_ENTERED_STATION)) {
/* The new position is the end of the platform */
TrainEnterStation(v, r >> VETS_STATION_ID_OFFSET);
TrainEnterStation(v->First(), r >> VETS_STATION_ID_OFFSET);
}
}
} else {
@@ -3916,7 +3965,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
if (HasBit(r, VETS_ENTERED_STATION)) {
/* The new position is the location where we want to stop */
TrainEnterStation(v, r >> VETS_STATION_ID_OFFSET);
TrainEnterStation(v->First(), r >> VETS_STATION_ID_OFFSET);
}
}
} else {

View File

@@ -294,6 +294,7 @@ bool Vehicle::NeedsAutomaticServicing() const
{
if (this->HasDepotOrder()) return false;
if (this->current_order.IsType(OT_LOADING)) return false;
if (this->current_order.IsType(OT_LOADING_ADVANCE)) return false;
if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
return NeedsServicing();
}
@@ -2530,8 +2531,13 @@ static void VehicleIncreaseStats(const Vehicle *front)
*/
void Vehicle::BeginLoading()
{
if (this->type == VEH_TRAIN) {
assert(IsTileType(Train::From(this)->GetStationLoadingVehicle()->tile, MP_STATION));
} else {
assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
}
bool no_load_prepare = false;
if (this->current_order.IsType(OT_GOTO_STATION) &&
this->current_order.GetDestination() == this->last_station_visited) {
this->DeleteUnreachedImplicitOrders();
@@ -2546,7 +2552,9 @@ void Vehicle::BeginLoading()
* whether the train is lost or not; not marking a train lost
* that arrives at random stations is bad. */
this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
} else if (this->current_order.IsType(OT_LOADING_ADVANCE)) {
this->current_order.MakeLoading(true);
no_load_prepare = true;
} else {
/* We weren't scheduled to stop here. Insert an implicit order
* to show that we are stopping here.
@@ -2634,9 +2642,11 @@ void Vehicle::BeginLoading()
this->current_order.MakeLoading(false);
}
if (!no_load_prepare) {
VehicleIncreaseStats(this);
PrepareUnload(this);
}
SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
@@ -2687,11 +2697,18 @@ uint32 Vehicle::GetLastLoadingStationValidCargoMask() const
*/
void Vehicle::LeaveStation()
{
assert(this->current_order.IsType(OT_LOADING));
assert(this->current_order.IsAnyLoadingType());
delete this->cargo_payment;
assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
if (this->type == VEH_TRAIN) {
for (Train *v = Train::From(this); v != nullptr; v = v->Next()) {
ClrBit(v->flags, VRF_BEYOND_PLATFORM_END);
ClrBit(v->flags, VRF_NOT_YET_IN_PLATFORM);
}
}
/* Only update the timetable if the vehicle was supposed to stop here. */
if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
@@ -2756,9 +2773,10 @@ void Vehicle::LeaveStation()
if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
/* Trigger station animation (trains only) */
if (IsTileType(this->tile, MP_STATION)) {
TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
TileIndex station_tile = Train::From(this)->GetStationLoadingVehicle()->tile;
if (IsTileType(station_tile, MP_STATION)) {
TriggerStationRandomisation(st, station_tile, SRT_TRAIN_DEPARTS);
TriggerStationAnimation(st, station_tile, SAT_TRAIN_DEPARTS);
}
SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
@@ -2791,6 +2809,30 @@ void Vehicle::LeaveStation()
this->MarkDirty();
}
/**
* Perform all actions when switching to advancing within a station for loading/unloading
* @pre this->current_order.IsType(OT_LOADING)
* @pre this->type == VEH_TRAIN
*/
void Vehicle::AdvanceLoadingInStation()
{
assert(this->current_order.IsType(OT_LOADING));
assert(this->type == VEH_TRAIN);
ClrBit(Train::From(this)->flags, VRF_ADVANCE_IN_PLATFORM);
for (Train *v = Train::From(this); v != nullptr; v = v->Next()) {
if (HasBit(v->flags, VRF_NOT_YET_IN_PLATFORM)) {
ClrBit(v->flags, VRF_NOT_YET_IN_PLATFORM);
} else {
SetBit(v->flags, VRF_BEYOND_PLATFORM_END);
}
}
HideFillingPercent(&this->fill_percent_te_id);
this->current_order.MakeLoadingAdvance(this->last_station_visited);
this->MarkDirty();
}
void Vehicle::RecalculateOrderOccupancyAverage()
{
@@ -2839,7 +2881,10 @@ void Vehicle::HandleLoading(bool mode)
if (!mode && this->type != VEH_TRAIN) PayStationSharingFee(this, Station::Get(this->last_station_visited));
/* Not the first call for this tick, or still loading */
if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) {
if (!mode && this->type == VEH_TRAIN && HasBit(Train::From(this)->flags, VRF_ADVANCE_IN_PLATFORM)) this->AdvanceLoadingInStation();
return;
}
this->PlayLeaveStationSound();
@@ -2966,7 +3011,7 @@ CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command, Tile
}
if (flags & DC_EXEC) {
if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
if (this->current_order.IsAnyLoadingType()) this->LeaveStation();
if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
uint16 &gv_flags = this->GetGroundVehicleFlags();

View File

@@ -354,6 +354,7 @@ public:
void BeginLoading();
void CancelReservation(StationID next, Station *st);
void LeaveStation();
void AdvanceLoadingInStation();
GroundVehicleCache *GetGroundVehicleCache();
const GroundVehicleCache *GetGroundVehicleCache() const;

View File

@@ -3213,6 +3213,12 @@ public:
str = STR_VEHICLE_STATUS_LOADING_UNLOADING;
break;
case OT_LOADING_ADVANCE:
str = STR_VEHICLE_STATUS_LOADING_UNLOADING_ADVANCE;
SetDParam(0, STR_VEHICLE_STATUS_LOADING_UNLOADING);
SetDParam(1, v->GetDisplaySpeed());
break;
case OT_GOTO_WAYPOINT: {
assert(v->type == VEH_TRAIN || v->type == VEH_SHIP);
SetDParam(0, v->current_order.GetDestination());