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; 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. */ /* Account for the vehicle having reached the current order and being in the loading phase. */
status = D_ARRIVED; status = D_ARRIVED;
start_date -= order->GetTravelTime() + (((*v)->lateness_counter < 0) ? (*v)->lateness_counter : 0); 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 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; 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 front The vehicle to be updated.
* @param st The station the vehicle is loading at. * @param st The station the vehicle is loading at.
* @param ticks The time it would normally wait, based on cargo loaded and unloaded. * @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) { if (front->type == VEH_TRAIN) {
/* Each platform tile is worth 2 rail vehicles. */ /* 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) { if (overhang > 0) {
ticks <<= 1; ticks <<= 1;
ticks += (overhang * ticks) / 8; ticks += (overhang * ticks) / 8;
@@ -1672,13 +1673,39 @@ static void LoadUnloadVehicle(Vehicle *front)
StationID last_visited = front->last_station_visited; StationID last_visited = front->last_station_visited;
Station *st = Station::Get(last_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(); CargoStationIDStackSet next_station = front->GetNextStoppingStation();
bool use_autorefit = front->current_order.IsRefit() && front->current_order.GetRefitCargo() == CT_AUTO_REFIT; bool use_autorefit = front->current_order.IsRefit() && front->current_order.GetRefitCargo() == CT_AUTO_REFIT;
CargoArray consist_capleft; CargoArray consist_capleft;
bool should_reserve_consist = false; bool should_reserve_consist = false;
bool reserve_consist_cargo_type_loading = 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; if (front->cargo_payment == NULL) should_reserve_consist = true;
} else { } else {
if ((front->current_order.GetLoadType() & OLFB_FULL_LOAD) || (front->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD)) { 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 */ /* We have not waited enough time till the next round of loading/unloading */
if (front->load_unload_ticks != 0) return; 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 /* The train reversed in the station. Take the "easy" way
* out and let the train just leave as it always did. */ * out and let the train just leave as it always did. */
SetBit(front->vehicle_flags, VF_LOADING_FINISHED); 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) 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()) { 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 == front || !v->Previous()->HasArticulatedPart()) artic_part = 0;
if (v->cargo_cap == 0) continue; if (v->cargo_cap == 0) continue;
artic_part++; artic_part++;
@@ -1895,7 +1941,7 @@ static void LoadUnloadVehicle(Vehicle *front)
/* Only set completely_emptied, if we just unloaded all remaining cargo */ /* Only set completely_emptied, if we just unloaded all remaining cargo */
completely_emptied &= anything_unloaded; 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); ClrBit(front->vehicle_flags, VF_STOP_LOADING);
@@ -1923,9 +1969,9 @@ static void LoadUnloadVehicle(Vehicle *front)
SetBit(front->vehicle_flags, VF_STOP_LOADING); 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 { } 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; bool finished_loading = true;
if (has_full_load_order) { if (has_full_load_order) {
if (front->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) { if (front->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {
@@ -1938,15 +1984,20 @@ static void LoadUnloadVehicle(Vehicle *front)
} else if (cargo_not_full != 0) { } else if (cargo_not_full != 0) {
finished_loading = false; finished_loading = false;
} }
/* Refresh next hop stats if we're full loading to make the links
* known to the distribution algorithm and allow cargo to be sent
* along them. Otherwise the vehicle could wait for cargo
* 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);
} }
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
* along them. Otherwise the vehicle could wait for cargo
* 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); 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 == 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 _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; StringID percent_up_down = STR_NULL;
int percent = CalcPercentVehicleFilled(front, &percent_up_down); int percent = CalcPercentVehicleFilled(front, &percent_up_down);
if (front->fill_percent_te_id == INVALID_TE_ID) { 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. */ /* Check if anything will be loaded at all. Otherwise we don't need to reserve either. */
for (Vehicle *v : st->loading_vehicles) { 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); assert(v->load_unload_ticks != 0);
if (--v->load_unload_ticks == 0) last_loading = v; if (--v->load_unload_ticks == 0) last_loading = v;
@@ -2015,7 +2067,7 @@ void LoadUnloadStation(Station *st)
if (last_loading == NULL) return; if (last_loading == NULL) return;
for (Vehicle *v : st->loading_vehicles) { 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; 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) { if (this->cur_speed > max_speed) {
tempmax = max(this->cur_speed - (this->cur_speed / 10) - 1, 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_STATION:
case OT_GOTO_WAYPOINT: dest_owner = BaseStation::Get(order->GetDestination())->owner; break; 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_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; case OT_LOADING: dest_owner = Station::Get(v->last_station_visited)->owner; break;
default: return true; default: return true;
} }
@@ -290,7 +291,7 @@ void HandleSharingCompanyDeletion(Owner owner)
} }
/* current order */ /* current order */
if (!OrderDestinationIsAllowed(&v->current_order, v, owner)) { if (!OrderDestinationIsAllowed(&v->current_order, v, owner)) {
if (v->current_order.IsType(OT_LOADING)) { if (v->current_order.IsAnyLoadingType()) {
v->LeaveStation(); v->LeaveStation();
} else { } else {
v->current_order.MakeDummy(); 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 # Messages in the start stop button in the vehicle view
STR_VEHICLE_STATUS_LOADING_UNLOADING :{LTBLUE}Loading / Unloading 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_LEAVING :{LTBLUE}Leaving
STR_VEHICLE_STATUS_CRASHED :{RED}Crashed! STR_VEHICLE_STATUS_CRASHED :{RED}Crashed!
STR_VEHICLE_STATUS_BROKEN_DOWN :{RED}Broken down 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_NEAR_END :[near end]
STR_ORDER_STOP_LOCATION_MIDDLE :[middle] STR_ORDER_STOP_LOCATION_MIDDLE :[middle]
STR_ORDER_STOP_LOCATION_FAR_END :[far end] 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) 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; } 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. * Get the type of order of this order.
* @return the order type. * @return the order type.
@@ -131,6 +137,7 @@ public:
void MakeConditional(VehicleOrderID order); void MakeConditional(VehicleOrderID order);
void MakeImplicit(StationID destination); void MakeImplicit(StationID destination);
void MakeWaiting(); void MakeWaiting();
void MakeLoadingAdvance(StationID destination);
/** /**
* Is this a 'goto' order with a real destination? * Is this a 'goto' order with a real destination?

View File

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

View File

@@ -1849,8 +1849,18 @@ public:
this->selected_order = -1; this->selected_order = -1;
} else if (sel == this->selected_order) { } else if (sel == this->selected_order) {
if (this->vehicle->type == VEH_TRAIN && sel < this->vehicle->GetNumOrders()) { 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), 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)); CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
} }
} else { } else {

View File

@@ -46,6 +46,7 @@ enum OrderType {
OT_CONDITIONAL = 7, OT_CONDITIONAL = 7,
OT_IMPLICIT = 8, OT_IMPLICIT = 8,
OT_WAITING = 9, OT_WAITING = 9,
OT_LOADING_ADVANCE = 10,
OT_END OT_END
}; };
@@ -95,6 +96,7 @@ enum OrderStopLocation {
OSL_PLATFORM_NEAR_END = 0, ///< Stop at the near end of the platform 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_MIDDLE = 1, ///< Stop at the middle of the platform
OSL_PLATFORM_FAR_END = 2, ///< Stop at the far end 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 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 */ /* this routine applies only to trains in depot tiles */
if (u->type != VEH_TRAIN || !IsRailDepotTile(tile)) return VETSB_CONTINUE; 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); Train *v = Train::From(u);
/* depot direction */ /* 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_MULTIPLE_DOCKS, XSCF_NULL, 1, 1, "multiple_docks", NULL, NULL, "DOCK" },
{ XSLFI_TIMETABLE_EXTRA, XSCF_NULL, 1, 1, "timetable_extra", NULL, NULL, "ORDX" }, { 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_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 { 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_MULTIPLE_DOCKS, ///< Multiple docks
XSLFI_TIMETABLE_EXTRA, ///< Vehicle timetable extra fields XSLFI_TIMETABLE_EXTRA, ///< Vehicle timetable extra fields
XSLFI_TRAIN_FLAGS_EXTRA, ///< Train flags field extra size 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_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 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->breakdown_ctr != 0) return ScriptVehicle::VS_BROKEN;
if (v->IsStoppedInDepot()) return ScriptVehicle::VS_IN_DEPOT; if (v->IsStoppedInDepot()) return ScriptVehicle::VS_IN_DEPOT;
if (vehstatus & ::VS_STOPPED) return ScriptVehicle::VS_STOPPED; 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; return ScriptVehicle::VS_RUNNING;
} }

View File

@@ -3300,8 +3300,12 @@ static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, i
// reverse at waypoint // reverse at waypoint
if (t->reverse_distance == 0) t->reverse_distance = t->gcache.cached_total_length; 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 (HasBit(Train::From(v)->flags, VRF_BEYOND_PLATFORM_END)) return VETSB_CONTINUE;
if (!IsRailStation(tile) || !v->IsFrontEngine()) 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_ahead;
int station_length; int station_length;
@@ -3326,9 +3330,9 @@ static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, i
if (x == stop) { if (x == stop) {
return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station
} else if (x < stop) { } else if (x < stop) {
v->vehstatus |= VS_TRAIN_SLOWING; front->vehstatus |= VS_TRAIN_SLOWING;
uint16 spd = max(0, (stop - x) * 20 - 15); 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) { } 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; int j = (int)b_order - (int)a_order;
/* Are we currently at an ordered station (un)loading? */ /* 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 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; 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 /* 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). * 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)); 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; 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) { 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 */ /* 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_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_NEED_REPAIR = 14,///< used to mark a train that has a reduced maximum speed because of a critical breakdown
VRF_TOO_HEAVY = 15, 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) 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); 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 CheckBreakdownFlags(Train *v);
void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type); 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; 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. protected: // These functions should not be called outside acceleration code.
/** /**
* Gets the speed a broken down train (low speed breakdown) is limited to. * 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 * @param station_length 'return' the station length in 1/16th tiles
* @return the location, calculated from the begin of the station to stop at. * @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); const Station *st = Station::Get(station_id);
*station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE; *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE;
*station_length = st->GetPlatformLength(tile) * 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 /* 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 */ * the order list like intermediate stations when non-stop is disabled */
OrderStopLocation osl = OSL_PLATFORM_MIDDLE; 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 */ /* The train is longer than the station, make it stop at the far end of the platform */
osl = OSL_PLATFORM_FAR_END; 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 */ /* 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(); default: NOT_REACHED();
case OSL_PLATFORM_NEAR_END: case OSL_PLATFORM_NEAR_END:
stop = v->gcache.cached_total_length; stop = front->gcache.cached_total_length;
break; break;
case OSL_PLATFORM_MIDDLE: 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; break;
case OSL_PLATFORM_FAR_END: case OSL_PLATFORM_FAR_END:
case OSL_PLATFORM_THROUGH:
stop = *station_length; stop = *station_length;
break; break;
} }
/* Subtract half the front vehicle length of the train so we get the real /* Subtract half the front vehicle length of the train so we get the real
* stop location of the train. */ * 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,27 +500,31 @@ int Train::GetCurrentMaxSpeed() const
this->gcache.cached_max_track_speed : this->gcache.cached_max_track_speed :
this->tcache.cached_max_curve_speed; this->tcache.cached_max_curve_speed;
if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && IsRailStationTile(this->tile)) { if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC) {
StationID sid = GetStationIndex(this->tile); Train *v_platform = const_cast<Train *>(this->GetStationLoadingVehicle());
if (this->current_order.ShouldStopAtStation(this, sid)) { TileIndex platform_tile = v_platform->tile;
int station_ahead; if (IsRailStationTile(platform_tile)) {
int station_length; StationID sid = GetStationIndex(platform_tile);
int stop_at = GetTrainStopLocation(sid, this->tile, this, &station_ahead, &station_length); if (this->current_order.ShouldStopAtStation(this, sid)) {
int station_ahead;
int 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 /* 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 */ * 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 distance_to_go = station_ahead / TILE_SIZE - (station_length - stop_at) / TILE_SIZE;
if (distance_to_go > 0) { if (distance_to_go > 0) {
int st_max_speed = 120; int st_max_speed = 120;
int delta_v = this->cur_speed / (distance_to_go + 1); int delta_v = this->cur_speed / (distance_to_go + 1);
if (max_speed > (this->cur_speed - delta_v)) { if (max_speed > (this->cur_speed - delta_v)) {
st_max_speed = this->cur_speed - (delta_v / 10); st_max_speed = this->cur_speed - (delta_v / 10);
}
st_max_speed = max(st_max_speed, 25 * distance_to_go);
max_speed = min(max_speed, st_max_speed);
} }
st_max_speed = max(st_max_speed, 25 * distance_to_go);
max_speed = min(max_speed, st_max_speed);
} }
} }
} }
@@ -2008,6 +2052,8 @@ void ReverseTrainDirection(Train *v)
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
} }
if (v->current_order.IsType(OT_LOADING_ADVANCE)) v->LeaveStation();
v->reverse_distance = 0; v->reverse_distance = 0;
/* Clear path reservation in front if train is not stuck. */ /* 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) { if (flags & DC_EXEC) {
/* Properly leave the station if we are loading and won't be loading anymore */ /* 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; const Vehicle *last = v;
while (last->Next() != NULL) last = last->Next(); while (last->Next() != NULL) last = last->Next();
/* not a station || different station --> leave the station */ /* 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(); v->LeaveStation();
} }
} }
@@ -2931,7 +2979,7 @@ static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir,
* order list itself is empty. */ * order list itself is empty. */
if (v->current_order.IsType(OT_LEAVESTATION)) { if (v->current_order.IsType(OT_LEAVESTATION)) {
orders.SwitchToNextOrder(false); 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) ? v->current_order.IsType(OT_GOTO_STATION) ?
IsRailStationTile(v->tile) && v->current_order.GetDestination() == GetStationIndex(v->tile) : IsRailStationTile(v->tile) && v->current_order.GetDestination() == GetStationIndex(v->tile) :
v->tile == v->dest_tile))) { v->tile == v->dest_tile))) {
@@ -3233,8 +3281,9 @@ static void TrainEnterStation(Train *v, StationID station)
v->BeginLoading(); v->BeginLoading();
TriggerStationRandomisation(st, v->tile, SRT_TRAIN_ARRIVES); TileIndex station_tile = v->GetStationLoadingVehicle()->tile;
TriggerStationAnimation(st, v->tile, SAT_TRAIN_ARRIVES); TriggerStationRandomisation(st, station_tile, SRT_TRAIN_ARRIVES);
TriggerStationAnimation(st, station_tile, SAT_TRAIN_ARRIVES);
} }
/* Check if the vehicle is compatible with the specified tile */ /* 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)) { if (HasBit(r, VETS_ENTERED_STATION)) {
/* The new position is the end of the platform */ /* 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 { } else {
@@ -3916,7 +3965,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
if (HasBit(r, VETS_ENTERED_STATION)) { if (HasBit(r, VETS_ENTERED_STATION)) {
/* The new position is the location where we want to stop */ /* 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 { } else {

View File

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

View File

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

View File

@@ -3213,6 +3213,12 @@ public:
str = STR_VEHICLE_STATUS_LOADING_UNLOADING; str = STR_VEHICLE_STATUS_LOADING_UNLOADING;
break; 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: { case OT_GOTO_WAYPOINT: {
assert(v->type == VEH_TRAIN || v->type == VEH_SHIP); assert(v->type == VEH_TRAIN || v->type == VEH_SHIP);
SetDParam(0, v->current_order.GetDestination()); SetDParam(0, v->current_order.GetDestination());