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_NO_POWER :{RED}No power
|
||||
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_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_UP_DOWN_SMALL :{TINY_FONT}{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 :{WHITE}{NUM}%
|
||||
|
||||
|
|
|
@ -485,11 +485,10 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
|
|||
* to a depot (other vehicle types). */
|
||||
if (::Vehicle::Get(vehicle_id)->type == VEH_AIRCRAFT) {
|
||||
if (!::IsTileType(destination, MP_STATION)) return false;
|
||||
order.MakeGoToDepot(::GetStationIndex(destination), odtf, onsf, odaf);
|
||||
} else {
|
||||
if (::IsTileType(destination, MP_STATION)) return false;
|
||||
order.MakeGoToDepot(::GetDepotIndex(destination), odtf, onsf, odaf);
|
||||
}
|
||||
order.MakeGoToDepot(::GetDepotIndex(destination), odtf, onsf, odaf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ ScriptVehicleList_Depot::ScriptVehicleList_Depot(TileIndex tile)
|
|||
case MP_STATION: // Aircraft
|
||||
if (!IsAirport(tile)) return;
|
||||
type = VEH_AIRCRAFT;
|
||||
dest = GetStationIndex(tile);
|
||||
dest = GetDepotIndex(tile);
|
||||
break;
|
||||
|
||||
case MP_RAILWAY:
|
||||
|
|
|
@ -374,6 +374,25 @@ static bool CheckReverseShip(const Ship *v, Trackdir *trackdir = nullptr)
|
|||
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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -687,13 +798,6 @@ void ResetVehicleColourMap()
|
|||
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()
|
||||
{
|
||||
_vehicles_to_autoreplace.clear();
|
||||
|
|
|
@ -52,6 +52,7 @@ enum VehicleFlags {
|
|||
VF_PATHFINDER_LOST, ///< Vehicle's pathfinder is lost.
|
||||
VF_SERVINT_IS_CUSTOM, ///< Service interval is custom.
|
||||
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. */
|
||||
|
@ -782,6 +783,12 @@ public:
|
|||
|
||||
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 NeedsServicing() const;
|
||||
|
|
|
@ -1131,3 +1131,85 @@ CommandCost CmdChangeServiceInt(DoCommandFlag flags, VehicleID veh_id, uint16_t
|
|||
|
||||
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);
|
||||
|
||||
void VehicleServiceInDepot(Vehicle *v);
|
||||
void VehicleServiceInExtendedDepot(Vehicle *v);
|
||||
uint CountVehiclesInChain(const Vehicle *v);
|
||||
void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc);
|
||||
void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc);
|
||||
|
|
|
@ -3111,6 +3111,8 @@ public:
|
|||
} else { // no train
|
||||
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()) {
|
||||
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)) {
|
||||
|
|
Loading…
Reference in New Issue