diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 45a93c0c44..77afa780f4 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -1276,6 +1276,9 @@ struct BuildVehicleWindow : Window { break; case VEH_SHIP: + this->filter.railtypes = this->listview_mode ? INVALID_RAILTYPES : depot->r_types.rail_types; + break; + case VEH_AIRCRAFT: break; } @@ -1469,18 +1472,17 @@ struct BuildVehicleWindow : Window { EngineID sel_id = INVALID_ENGINE; this->eng_list.clear(); - for (const Engine *e : Engine::IterateType(VEH_SHIP)) { - if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; - EngineID eid = e->index; - if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue; + if (this->listview_mode || this->filter.railtypes != RAILTYPES_NONE) { + for (const Engine *e : Engine::IterateType(VEH_SHIP)) { + if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; + EngineID eid = e->index; + if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue; + this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0); - /* Filter by name or NewGRF extra text */ - if (!FilterByText(e)) continue; - - this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0); - - if (eid == this->sel_engine) sel_id = eid; + if (eid == this->sel_engine) sel_id = eid; + } } + this->SelectEngine(sel_id); } diff --git a/src/depot.cpp b/src/depot.cpp index bd9a3ecc1f..3451bdcf80 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -61,6 +61,23 @@ Depot::~Depot() CloseWindowById(GetWindowClassForVehicleType(this->veh_type), VehicleListIdentifier(VL_DEPOT_LIST, this->veh_type, this->owner, this->index).Pack()); + + InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type); +} + +/** + * Schedule deletion of this depot. + * + * This method is ought to be called after demolishing last depot part. + * The depot will be kept in the pool for a while so it can be + * placed again later without messing vehicle orders. + * + * @see Depot::IsInUse + */ +void Depot::Disuse() +{ + /* Mark that the depot is demolished and start the countdown. */ + this->delete_ctr = 8; } /** @@ -139,7 +156,8 @@ void Depot::AfterAddRemove(TileArea ta, bool adding) assert(!this->depot_tiles.empty()); this->xy = this->depot_tiles[0]; } else { - delete this; + assert(this->IsInUse()); + this->Disuse(); } InvalidateWindowData(WC_SELECT_DEPOT, veh_type); diff --git a/src/depot_base.h b/src/depot_base.h index f46634cd01..6f22f2428c 100644 --- a/src/depot_base.h +++ b/src/depot_base.h @@ -32,6 +32,7 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> { VehicleType veh_type; ///< Vehicle type of the depot. Owner owner; ///< Owner of the depot. + uint8_t delete_ctr; ///< Delete counter. If greater than 0 then it is decremented until it reaches 0; the depot is then deleted. Station *station; ///< For aircraft, station associated with this hangar. union { @@ -67,9 +68,24 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> { */ inline bool IsOfType(const Depot *d) const { - return GetTileType(d->xy) == GetTileType(this->xy); + return d->veh_type == this->veh_type; } + /** + * Check whether the depot currently is in use; in use means + * that it is not scheduled for deletion and that it still has + * a building on the map. Otherwise the building is demolished + * and the depot awaits to be deleted. + * @return true iff still in use + * @see Depot::Disuse + */ + inline bool IsInUse() const + { + return this->delete_ctr == 0; + } + + void Disuse(); + /* Check we can add some tiles to this depot. */ CommandCost BeforeAddTiles(TileArea ta); diff --git a/src/depot_cmd.cpp b/src/depot_cmd.cpp index 32cd01558b..1bbdfffe73 100644 --- a/src/depot_cmd.cpp +++ b/src/depot_cmd.cpp @@ -17,6 +17,7 @@ #include "vehiclelist.h" #include "window_func.h" #include "depot_cmd.h" +#include "timer/timer_game_tick.h" #include "table/strings.h" @@ -76,6 +77,20 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str return CommandCost(); } +void OnTick_Depot() +{ + if (_game_mode == GM_EDITOR) return; + + /* Clean up demolished depots. */ + for (Depot *d : Depot::Iterate()) { + if (d->IsInUse()) continue; + if ((TimerGameTick::counter + d->index) % Ticks::DEPOT_REMOVAL_TICKS != 0) continue; + if (--d->delete_ctr != 0) continue; + delete d; + } +} + + /** * Look for or check depot to join to, building a new one if necessary. * @param ta The area of the new depot. diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 511099bcd1..36b7846620 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -1133,6 +1133,7 @@ static void DepotSellAllConfirmationCallback(Window *win, bool confirmed) if (confirmed) { assert(Depot::IsValidID(win->window_number)); Depot *d = Depot::Get(win->window_number); + if (!d->IsInUse()) return; Command::Post(d->xy, d->veh_type); } } diff --git a/src/landscape.cpp b/src/landscape.cpp index 7964e3ba02..5da1a2995f 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -1654,6 +1654,7 @@ bool GenerateLandscape(uint8_t mode) void OnTick_Town(); void OnTick_Trees(); void OnTick_Station(); +void OnTick_Depot(); void OnTick_Industry(); void OnTick_Companies(); @@ -1667,6 +1668,7 @@ void CallLandscapeTick() OnTick_Town(); OnTick_Trees(); OnTick_Station(); + OnTick_Depot(); OnTick_Industry(); } diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 203d7784f2..63a5869acb 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -787,7 +787,7 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se if ((new_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0) { const Depot *dp = Depot::GetIfValid(new_order.GetDestination()); - if (dp == nullptr) return CMD_ERROR; + if (dp == nullptr || !dp->IsInUse()) return CMD_ERROR; ret = CheckOwnership(dp->owner); if (ret.Failed()) return ret; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 39aecf5a92..6aff783288 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2844,6 +2844,7 @@ bool AfterLoadGame() /* It can happen there is no depot here anymore (TTO/TTD savegames) */ depot->veh_type = VEH_INVALID; depot->owner = INVALID_OWNER; + depot->Disuse(); delete depot; continue; } diff --git a/src/saveload/depot_sl.cpp b/src/saveload/depot_sl.cpp index 1987605ea3..4bc5dde166 100644 --- a/src/saveload/depot_sl.cpp +++ b/src/saveload/depot_sl.cpp @@ -33,6 +33,7 @@ static const SaveLoad _depot_desc[] = { SLE_CONDVAR(Depot, ta.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION), SLE_CONDVAR(Depot, ta.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION), SLE_CONDREF(Depot, station, REF_STATION, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION), + SLE_CONDVAR(Depot, delete_ctr, SLE_UINT8, SLV_KEEP_REMOVED_DEPOTS, SL_MAX_VERSION), }; struct DEPTChunkHandler : ChunkHandler { diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index d8f10dd049..2976382750 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -395,6 +395,8 @@ enum SaveLoadVersion : uint16_t { SLV_DEPOT_SPREAD, ///< 318 PR#XXXXX Add a setting for max depot spread. SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS, ///< 319 PR#XXXXX Allow incompatible vehicle replacements. + SLV_KEEP_REMOVED_DEPOTS, ///< 320 PR#XXXXX Keep remove depots for a while. + SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/script/api/script_depotlist.cpp b/src/script/api/script_depotlist.cpp index 7997d399e7..0906a86da9 100644 --- a/src/script/api/script_depotlist.cpp +++ b/src/script/api/script_depotlist.cpp @@ -24,7 +24,7 @@ ScriptDepotList::ScriptDepotList(ScriptTile::TransportType transport_type) bool is_deity = ScriptCompanyMode::IsDeity(); CompanyID owner = ScriptObject::GetCompany(); for (const Depot *depot : Depot::Iterate()) { - if (depot->veh_type != (VehicleType)transport_type || + if (!depot->IsInUse() || depot->veh_type != (VehicleType)transport_type || (!is_deity && ::GetTileOwner(depot->xy) != owner)) continue; this->AddItem(depot->xy.base()); diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 498817772d..1695c4e153 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -187,7 +187,7 @@ static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance) const Depot *best_depot = nullptr; uint best_dist_sq = std::numeric_limits::max(); for (const Depot *depot : Depot::Iterate()) { - if (depot->veh_type != VEH_SHIP || depot->owner != v->owner) continue; + if (depot->veh_type != VEH_SHIP || depot->owner != v->owner || !depot->IsInUse()) continue; const TileIndex tile = depot->xy; const uint dist_sq = DistanceSquare(tile, v->tile); diff --git a/src/timer/timer_game_tick.h b/src/timer/timer_game_tick.h index 02ae2b16ff..7af0024bd4 100644 --- a/src/timer/timer_game_tick.h +++ b/src/timer/timer_game_tick.h @@ -78,6 +78,7 @@ public: static constexpr TimerGameTick::Ticks STATION_RATING_TICKS = 185; ///< Cycle duration for updating station rating. static constexpr TimerGameTick::Ticks STATION_ACCEPTANCE_TICKS = 250; ///< Cycle duration for updating station acceptance. static constexpr TimerGameTick::Ticks STATION_LINKGRAPH_TICKS = 504; ///< Cycle duration for cleaning dead links. + static constexpr TimerGameTick::Ticks DEPOT_REMOVAL_TICKS = 250; ///< Cycle duration for cleaning demolished depots. static constexpr TimerGameTick::Ticks CARGO_AGING_TICKS = 185; ///< Cycle duration for aging cargo. static constexpr TimerGameTick::Ticks INDUSTRY_PRODUCE_TICKS = 256; ///< Cycle duration for industry production. static constexpr TimerGameTick::Ticks TOWN_GROWTH_TICKS = 70; ///< Cycle duration for towns trying to grow (this originates from the size of the town array in TTD). diff --git a/src/viewport.cpp b/src/viewport.cpp index 6a9da5e44d..1892962398 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -3613,6 +3613,10 @@ void MarkCatchmentTilesDirty() MarkWholeScreenDirty(); } if (_viewport_highlight_depot != INVALID_DEPOT) { + Depot *dep = Depot::Get(_viewport_highlight_depot); + if (!dep->IsInUse()) { + _viewport_highlight_depot = INVALID_DEPOT; + } MarkWholeScreenDirty(); } }