Feature: Order flag to unbunch vehicles at depot (#11945)

This commit is contained in:
Tyler Trahan
2024-02-03 08:04:24 -05:00
committed by GitHub
parent a0c480c4e4
commit f6dd5053a3
20 changed files with 293 additions and 28 deletions

View File

@@ -810,6 +810,10 @@ void Vehicle::HandlePathfindingResult(bool path_found)
SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type));
/* Unbunching data is no longer valid. */
this->ResetDepotUnbunching();
/* Notify user about the event. */
AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
@@ -1635,12 +1639,23 @@ void VehicleEnterDepot(Vehicle *v)
* before the stop to the station after the stop can't be predicted
* we shouldn't construct it when the vehicle visits the next stop. */
v->last_loading_station = INVALID_STATION;
/* Clear unbunching data. */
v->ResetDepotUnbunching();
/* Announce that the vehicle is waiting to players and AIs. */
if (v->owner == _local_company) {
SetDParam(0, v->index);
AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
}
AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
}
/* If we've entered our unbunching depot, record the round trip duration. */
if (v->current_order.GetDepotActionType() & ODATFB_UNBUNCH && v->depot_unbunching_last_departure > 0) {
v->round_trip_time = (TimerGameTick::counter - v->depot_unbunching_last_departure);
}
v->current_order.MakeDummy();
}
}
@@ -2402,6 +2417,85 @@ void Vehicle::HandleLoading(bool mode)
this->IncrementImplicitOrderIndex();
}
/**
* Check if the current vehicle has an unbunching order.
* @return true Iff this vehicle has an unbunching order.
*/
bool Vehicle::HasUnbunchingOrder() const
{
for (Order *o : this->Orders()) {
if (o->IsType(OT_GOTO_DEPOT) && o->GetDepotActionType() & ODATFB_UNBUNCH) return true;
}
return false;
}
/**
* Leave an unbunching depot and calculate the next departure time for shared order vehicles.
*/
void Vehicle::LeaveUnbunchingDepot()
{
/* Set the start point for this round trip time. */
this->depot_unbunching_last_departure = TimerGameTick::counter;
/* Tell the timetable we are now "on time." */
this->lateness_counter = 0;
SetWindowDirty(WC_VEHICLE_TIMETABLE, this->index);
/* Find the average travel time of vehicles that we share orders with. */
uint num_vehicles = 0;
TimerGameTick::Ticks total_travel_time = 0;
Vehicle *u = this->FirstShared();
for (; u != nullptr; u = u->NextShared()) {
/* Ignore vehicles that are manually stopped or crashed. */
if (u->vehstatus & (VS_STOPPED | VS_CRASHED)) continue;
num_vehicles++;
total_travel_time += u->round_trip_time;
}
/* Make sure we cannot divide by 0. */
num_vehicles = std::max(num_vehicles, 1u);
/* Calculate the separation by finding the average travel time, then calculating equal separation (minimum 1 tick) between vehicles. */
TimerGameTick::Ticks separation = std::max((total_travel_time / num_vehicles / num_vehicles), 1u);
TimerGameTick::TickCounter next_departure = TimerGameTick::counter + separation;
/* Set the departure time of all vehicles that we share orders with. */
u = this->FirstShared();
for (; u != nullptr; u = u->NextShared()) {
/* Ignore vehicles that are manually stopped or crashed. */
if (u->vehstatus & (VS_STOPPED | VS_CRASHED)) continue;
u->depot_unbunching_next_departure = next_departure;
}
}
/**
* Check whether a vehicle inside a depot is waiting for unbunching.
* @return True if the vehicle must continue waiting, or false if it may try to leave the depot.
*/
bool Vehicle::IsWaitingForUnbunching() const
{
assert(this->IsInDepot());
/* Don't bother if there are no vehicles sharing orders. */
if (!this->IsOrderListShared()) return false;
/* Don't do anything if there aren't enough orders. */
if (this->GetNumOrders() <= 1) return false;
/*
* Make sure this is the correct depot for unbunching.
* If we are headed for the first order, we must wrap around back to the last order.
*/
bool is_first_order = (this->GetOrder(this->cur_real_order_index) == this->GetFirstOrder());
Order *previous_order = (is_first_order) ? this->GetLastOrder() : this->GetOrder(this->cur_real_order_index - 1);
if (previous_order == nullptr || !previous_order->IsType(OT_GOTO_DEPOT) || !(previous_order->GetDepotActionType() & ODATFB_UNBUNCH)) return false;
return (this->depot_unbunching_next_departure > TimerGameTick::counter);
};
/**
* Send this vehicle to the depot using the given command(s).
* @param flags the command flags (like execute and such).
@@ -2416,6 +2510,9 @@ CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
if (this->IsStoppedInDepot()) return CMD_ERROR;
/* No matter why we're headed to the depot, unbunching data is no longer valid. */
if (flags & DC_EXEC) this->ResetDepotUnbunching();
if (this->current_order.IsType(OT_GOTO_DEPOT)) {
bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
if (((command & DepotCommand::Service) != DepotCommand::None) == halt_in_depot) {