mirror of https://github.com/OpenTTD/OpenTTD
Feature: Show servicing vehicles in extended depots on viewport and on vehicle window.
parent
9869611ad8
commit
3715dcf2d7
|
@ -4441,6 +4441,7 @@ STR_VEHICLE_STATUS_STOPPED :{RED}Stopped
|
||||||
STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL :{RED}{VELOCITY} - Stopping
|
STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL :{RED}{VELOCITY} - Stopping
|
||||||
STR_VEHICLE_STATUS_TRAIN_NO_POWER :{RED}No power
|
STR_VEHICLE_STATUS_TRAIN_NO_POWER :{RED}No power
|
||||||
STR_VEHICLE_STATUS_TRAIN_STUCK :{ORANGE}Waiting for free path
|
STR_VEHICLE_STATUS_TRAIN_STUCK :{ORANGE}Waiting for free path
|
||||||
|
STR_VEHICLE_STATUS_SERVICING :{LTBLUE}Servicing vehicle
|
||||||
STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR :{ORANGE}Too far to next destination
|
STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR :{ORANGE}Too far to next destination
|
||||||
|
|
||||||
STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL :{LTBLUE}{1:VELOCITY} - Heading for {0:STATION}
|
STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL :{LTBLUE}{1:VELOCITY} - Heading for {0:STATION}
|
||||||
|
@ -4928,6 +4929,8 @@ STR_PERCENT_DOWN_SMALL :{TINY_FONT}{WHI
|
||||||
STR_PERCENT_DOWN :{WHITE}{NUM}%{DOWN_ARROW}
|
STR_PERCENT_DOWN :{WHITE}{NUM}%{DOWN_ARROW}
|
||||||
STR_PERCENT_UP_DOWN_SMALL :{TINY_FONT}{WHITE}{NUM}%{UP_ARROW}{DOWN_ARROW}
|
STR_PERCENT_UP_DOWN_SMALL :{TINY_FONT}{WHITE}{NUM}%{UP_ARROW}{DOWN_ARROW}
|
||||||
STR_PERCENT_UP_DOWN :{WHITE}{NUM}%{UP_ARROW}{DOWN_ARROW}
|
STR_PERCENT_UP_DOWN :{WHITE}{NUM}%{UP_ARROW}{DOWN_ARROW}
|
||||||
|
STR_SERVICING_INDICATOR_SMALL :{TINY_FONT}{LTBLUE}Servicing ({NUM}{NBSP}%)
|
||||||
|
STR_SERVICING_INDICATOR :{LTBLUE}Servicing ({NUM}{NBSP}%)
|
||||||
STR_PERCENT_NONE_SMALL :{TINY_FONT}{WHITE}{NUM}%
|
STR_PERCENT_NONE_SMALL :{TINY_FONT}{WHITE}{NUM}%
|
||||||
STR_PERCENT_NONE :{WHITE}{NUM}%
|
STR_PERCENT_NONE :{WHITE}{NUM}%
|
||||||
|
|
||||||
|
|
|
@ -485,11 +485,10 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
|
||||||
* to a depot (other vehicle types). */
|
* to a depot (other vehicle types). */
|
||||||
if (::Vehicle::Get(vehicle_id)->type == VEH_AIRCRAFT) {
|
if (::Vehicle::Get(vehicle_id)->type == VEH_AIRCRAFT) {
|
||||||
if (!::IsTileType(destination, MP_STATION)) return false;
|
if (!::IsTileType(destination, MP_STATION)) return false;
|
||||||
order.MakeGoToDepot(::GetStationIndex(destination), odtf, onsf, odaf);
|
|
||||||
} else {
|
} else {
|
||||||
if (::IsTileType(destination, MP_STATION)) return false;
|
if (::IsTileType(destination, MP_STATION)) return false;
|
||||||
order.MakeGoToDepot(::GetDepotIndex(destination), odtf, onsf, odaf);
|
|
||||||
}
|
}
|
||||||
|
order.MakeGoToDepot(::GetDepotIndex(destination), odtf, onsf, odaf);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ ScriptVehicleList_Depot::ScriptVehicleList_Depot(TileIndex tile)
|
||||||
case MP_STATION: // Aircraft
|
case MP_STATION: // Aircraft
|
||||||
if (!IsAirport(tile)) return;
|
if (!IsAirport(tile)) return;
|
||||||
type = VEH_AIRCRAFT;
|
type = VEH_AIRCRAFT;
|
||||||
dest = GetStationIndex(tile);
|
dest = GetDepotIndex(tile);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MP_RAILWAY:
|
case MP_RAILWAY:
|
||||||
|
|
|
@ -374,6 +374,25 @@ static bool CheckReverseShip(const Ship *v, Trackdir *trackdir = nullptr)
|
||||||
return YapfShipCheckReverse(v, trackdir);
|
return YapfShipCheckReverse(v, trackdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HandleShipEnterDepot(Ship *v)
|
||||||
|
{
|
||||||
|
assert(IsShipDepotTile(v->tile));
|
||||||
|
|
||||||
|
if (IsExtendedDepot(v->tile)) {
|
||||||
|
v->state |= TRACK_BIT_DEPOT;
|
||||||
|
v->cur_speed = 0;
|
||||||
|
v->UpdateCache();
|
||||||
|
v->UpdateViewport(true, true);
|
||||||
|
SetWindowClassesDirty(WC_SHIPS_LIST);
|
||||||
|
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
|
||||||
|
|
||||||
|
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||||
|
v->StartService();
|
||||||
|
} else {
|
||||||
|
VehicleEnterDepot(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool CheckShipLeaveDepot(Ship *v)
|
static bool CheckShipLeaveDepot(Ship *v)
|
||||||
{
|
{
|
||||||
if (!v->IsChainInDepot()) return false;
|
if (!v->IsChainInDepot()) return false;
|
||||||
|
|
118
src/vehicle.cpp
118
src/vehicle.cpp
|
@ -181,6 +181,117 @@ void VehicleServiceInDepot(Vehicle *v)
|
||||||
} while (v != nullptr && v->HasEngineType());
|
} while (v != nullptr && v->HasEngineType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of vehicles that should check for autoreplace this tick.
|
||||||
|
* Mapping of vehicle -> leave depot immediately after autoreplace.
|
||||||
|
*/
|
||||||
|
using AutoreplaceMap = std::map<VehicleID, bool>;
|
||||||
|
static AutoreplaceMap _vehicles_to_autoreplace;
|
||||||
|
|
||||||
|
void VehicleServiceInExtendedDepot(Vehicle *v)
|
||||||
|
{
|
||||||
|
/* Always work with the front of the vehicle */
|
||||||
|
assert(v == v->First());
|
||||||
|
assert(IsExtendedDepotTile(v->tile));
|
||||||
|
|
||||||
|
switch (v->type) {
|
||||||
|
case VEH_TRAIN: {
|
||||||
|
SetWindowClassesDirty(WC_TRAINS_LIST);
|
||||||
|
Train *t = Train::From(v);
|
||||||
|
t->ConsistChanged(CCF_ARRANGE);
|
||||||
|
t->UpdateViewport(true, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VEH_SHIP: {
|
||||||
|
SetWindowClassesDirty(WC_SHIPS_LIST);
|
||||||
|
Ship *ship = Ship::From(v);
|
||||||
|
ship->UpdateCache();
|
||||||
|
ship->UpdateViewport(true, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VEH_ROAD:
|
||||||
|
SetWindowClassesDirty(WC_ROADVEH_LIST);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VEH_AIRCRAFT:
|
||||||
|
SetWindowClassesDirty(WC_AIRCRAFT_LIST);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
DepotID depot_id = GetDepotIndex(v->tile);
|
||||||
|
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
|
||||||
|
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
|
||||||
|
|
||||||
|
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
|
||||||
|
|
||||||
|
VehicleServiceInDepot(v);
|
||||||
|
|
||||||
|
/* After a vehicle trigger, the graphics and properties of the vehicle could change. */
|
||||||
|
TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
|
||||||
|
v->MarkDirty();
|
||||||
|
|
||||||
|
if (v->current_order.IsType(OT_GOTO_DEPOT)) {
|
||||||
|
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
|
||||||
|
|
||||||
|
const Order *real_order = v->GetOrder(v->cur_real_order_index);
|
||||||
|
Order t = v->current_order;
|
||||||
|
v->current_order.MakeDummy();
|
||||||
|
|
||||||
|
/* Test whether we are heading for this depot. If not, do nothing.
|
||||||
|
* Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
|
||||||
|
if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
|
||||||
|
real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
|
||||||
|
t.GetDestination() != GetDepotIndex(v->tile)) {
|
||||||
|
/* We are heading for another depot, keep driving. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.IsRefit()) {
|
||||||
|
Backup<CompanyID> cur_company(_current_company, v->owner);
|
||||||
|
CommandCost cost = std::get<0>(Command<CMD_REFIT_VEHICLE>::Do(DC_EXEC, v->index, t.GetRefitCargo(), 0xFF, false, false, 0));
|
||||||
|
cur_company.Restore();
|
||||||
|
|
||||||
|
if (cost.Failed()) {
|
||||||
|
_vehicles_to_autoreplace[v->index] = false;
|
||||||
|
if (v->owner == _local_company) {
|
||||||
|
/* Notify the user that we stopped the vehicle */
|
||||||
|
SetDParam(0, v->index);
|
||||||
|
AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
|
||||||
|
}
|
||||||
|
} else if (cost.GetCost() != 0) {
|
||||||
|
v->profit_this_year -= cost.GetCost() << 8;
|
||||||
|
if (v->owner == _local_company) {
|
||||||
|
ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
|
||||||
|
/* Part of orders */
|
||||||
|
v->DeleteUnreachedImplicitOrders();
|
||||||
|
UpdateVehicleTimetable(v, true);
|
||||||
|
v->IncrementImplicitOrderIndex();
|
||||||
|
}
|
||||||
|
if (t.GetDepotActionType() & ODATFB_HALT) {
|
||||||
|
/* Vehicles are always stopped on entering depots. Do not restart this one. */
|
||||||
|
_vehicles_to_autoreplace[v->index] = false;
|
||||||
|
/* Invalidate last_loading_station. As the link from the station
|
||||||
|
* 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;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
|
* Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
|
||||||
*
|
*
|
||||||
|
@ -687,13 +798,6 @@ void ResetVehicleColourMap()
|
||||||
for (Vehicle *v : Vehicle::Iterate()) { v->colourmap = PAL_NONE; }
|
for (Vehicle *v : Vehicle::Iterate()) { v->colourmap = PAL_NONE; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* List of vehicles that should check for autoreplace this tick.
|
|
||||||
* Mapping of vehicle -> leave depot immediately after autoreplace.
|
|
||||||
*/
|
|
||||||
using AutoreplaceMap = std::map<VehicleID, bool>;
|
|
||||||
static AutoreplaceMap _vehicles_to_autoreplace;
|
|
||||||
|
|
||||||
void InitializeVehicles()
|
void InitializeVehicles()
|
||||||
{
|
{
|
||||||
_vehicles_to_autoreplace.clear();
|
_vehicles_to_autoreplace.clear();
|
||||||
|
|
|
@ -52,6 +52,7 @@ enum VehicleFlags {
|
||||||
VF_PATHFINDER_LOST, ///< Vehicle's pathfinder is lost.
|
VF_PATHFINDER_LOST, ///< Vehicle's pathfinder is lost.
|
||||||
VF_SERVINT_IS_CUSTOM, ///< Service interval is custom.
|
VF_SERVINT_IS_CUSTOM, ///< Service interval is custom.
|
||||||
VF_SERVINT_IS_PERCENT, ///< Service interval is percent.
|
VF_SERVINT_IS_PERCENT, ///< Service interval is percent.
|
||||||
|
VF_IS_SERVICING, ///< Vehicle is servicing.
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Bit numbers used to indicate which of the #NewGRFCache values are valid. */
|
/** Bit numbers used to indicate which of the #NewGRFCache values are valid. */
|
||||||
|
@ -782,6 +783,12 @@ public:
|
||||||
|
|
||||||
bool HandleBreakdown();
|
bool HandleBreakdown();
|
||||||
|
|
||||||
|
bool IsServicing() const { return HasBit(this->vehicle_flags, VF_IS_SERVICING); }
|
||||||
|
|
||||||
|
void StartService();
|
||||||
|
bool ContinueServicing();
|
||||||
|
void StopServicing();
|
||||||
|
|
||||||
bool NeedsAutorenewing(const Company *c, bool use_renew_setting = true) const;
|
bool NeedsAutorenewing(const Company *c, bool use_renew_setting = true) const;
|
||||||
|
|
||||||
bool NeedsServicing() const;
|
bool NeedsServicing() const;
|
||||||
|
|
|
@ -1131,3 +1131,85 @@ CommandCost CmdChangeServiceInt(DoCommandFlag flags, VehicleID veh_id, uint16_t
|
||||||
|
|
||||||
return CommandCost();
|
return CommandCost();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint16_t DEFAULT_SERVICE_TIME = 1 << 7;
|
||||||
|
const uint16_t TRAIN_SERVICE_TIME = 1 << 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A vehicle that entered an extended depot, starts servicing.
|
||||||
|
*/
|
||||||
|
void Vehicle::StartService()
|
||||||
|
{
|
||||||
|
assert(IsExtendedDepotTile(this->tile));
|
||||||
|
|
||||||
|
switch (this->type) {
|
||||||
|
case VEH_AIRCRAFT:
|
||||||
|
case VEH_ROAD:
|
||||||
|
case VEH_SHIP: {
|
||||||
|
this->wait_counter = DEFAULT_SERVICE_TIME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VEH_TRAIN: {
|
||||||
|
this->wait_counter = TRAIN_SERVICE_TIME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->cur_speed = 0;
|
||||||
|
this->subspeed = 0;
|
||||||
|
|
||||||
|
SetBit(this->vehicle_flags, VF_IS_SERVICING);
|
||||||
|
SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
|
||||||
|
assert(this->fill_percent_te_id == INVALID_TE_ID);
|
||||||
|
this->fill_percent_te_id = ShowFillingPercent(this->x_pos, this->y_pos, this->z_pos + 20, 0, STR_SERVICING_INDICATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if vehicle is servicing.
|
||||||
|
* If it is servicing, decrease time till finishing the servicing.
|
||||||
|
* It services the vehicle when servicing time ends.
|
||||||
|
* @return true if the vehicle is still servicing, false if it is not servicing.
|
||||||
|
*/
|
||||||
|
bool Vehicle::ContinueServicing()
|
||||||
|
{
|
||||||
|
if (!HasBit(this->vehicle_flags, VF_IS_SERVICING)) return false;
|
||||||
|
|
||||||
|
/* Update text effect every 16 ticks. */
|
||||||
|
if ((this->wait_counter & 15) == 0) {
|
||||||
|
uint8_t percent;
|
||||||
|
uint16_t max = this->type == VEH_TRAIN ? TRAIN_SERVICE_TIME : DEFAULT_SERVICE_TIME;
|
||||||
|
/* Return the percentage */
|
||||||
|
if ((max - this->wait_counter) * 2 < max) {
|
||||||
|
/* Less than 50%; round up, so that 0% means really empty. */
|
||||||
|
percent = (uint8_t)CeilDiv((max - this->wait_counter) * 100, max);
|
||||||
|
} else {
|
||||||
|
/* More than 50%; round down, so that 100% means really full. */
|
||||||
|
percent = (uint8_t)(((max - this->wait_counter) * 100) / max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->fill_percent_te_id == INVALID_TE_ID) {
|
||||||
|
this->fill_percent_te_id = ShowFillingPercent(this->x_pos, this->y_pos, this->z_pos + 20, percent, STR_SERVICING_INDICATOR);
|
||||||
|
} else {
|
||||||
|
UpdateFillingPercent(this->fill_percent_te_id, percent, STR_SERVICING_INDICATOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->wait_counter--) return true;
|
||||||
|
|
||||||
|
VehicleServiceInExtendedDepot(this);
|
||||||
|
this->StopServicing();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vehicle::StopServicing()
|
||||||
|
{
|
||||||
|
this->wait_counter = 0;
|
||||||
|
|
||||||
|
/* End servicing. */
|
||||||
|
ClrBit(this->vehicle_flags, VF_IS_SERVICING);
|
||||||
|
HideFillingPercent(&this->fill_percent_te_id);
|
||||||
|
InvalidateWindowData(WC_VEHICLE_VIEW, this->index);
|
||||||
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ bool IsValidImageIndex(uint8_t image_index);
|
||||||
typedef Vehicle *VehicleFromPosProc(Vehicle *v, void *data);
|
typedef Vehicle *VehicleFromPosProc(Vehicle *v, void *data);
|
||||||
|
|
||||||
void VehicleServiceInDepot(Vehicle *v);
|
void VehicleServiceInDepot(Vehicle *v);
|
||||||
|
void VehicleServiceInExtendedDepot(Vehicle *v);
|
||||||
uint CountVehiclesInChain(const Vehicle *v);
|
uint CountVehiclesInChain(const Vehicle *v);
|
||||||
void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc);
|
void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc);
|
||||||
void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc);
|
void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc);
|
||||||
|
|
|
@ -3111,6 +3111,8 @@ public:
|
||||||
} else { // no train
|
} else { // no train
|
||||||
str = STR_VEHICLE_STATUS_STOPPED;
|
str = STR_VEHICLE_STATUS_STOPPED;
|
||||||
}
|
}
|
||||||
|
} else if (HasBit(v->vehicle_flags, VF_IS_SERVICING)) {
|
||||||
|
str = STR_VEHICLE_STATUS_SERVICING;
|
||||||
} else if (v->IsInDepot() && v->IsWaitingForUnbunching()) {
|
} else if (v->IsInDepot() && v->IsWaitingForUnbunching()) {
|
||||||
str = STR_VEHICLE_STATUS_WAITING_UNBUNCHING;
|
str = STR_VEHICLE_STATUS_WAITING_UNBUNCHING;
|
||||||
} else if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_TRAIN_STUCK) && !v->current_order.IsType(OT_LOADING)) {
|
} else if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_TRAIN_STUCK) && !v->current_order.IsType(OT_LOADING)) {
|
||||||
|
|
Loading…
Reference in New Issue