From 620d14c502cf86c2dc80a8327c1e2119f139f7ac Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Mon, 19 Feb 2024 21:25:38 +0100 Subject: [PATCH 1/2] Codechange: Add a depot for each airport that has a hangar. --- src/saveload/afterload.cpp | 59 ++++++++++++++++--------- src/saveload/compat/station_sl_compat.h | 1 + src/saveload/saveload.cpp | 6 +++ src/saveload/saveload.h | 2 + src/saveload/station_sl.cpp | 1 + src/station.cpp | 24 ++++++++++ src/station_base.h | 5 +++ src/station_cmd.cpp | 6 +++ 8 files changed, 82 insertions(+), 22 deletions(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 06102930b7..be048f33c8 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2425,28 +2425,6 @@ bool AfterLoadGame() for (Depot *d : Depot::Iterate()) d->build_date = TimerGameCalendar::date; } - /* In old versions it was possible to remove an airport while a plane was - * taking off or landing. This gives all kind of problems when building - * another airport in the same station so we don't allow that anymore. - * For old savegames with such aircraft we just throw them in the air and - * treat the aircraft like they were flying already. */ - if (IsSavegameVersionBefore(SLV_146)) { - for (Aircraft *v : Aircraft::Iterate()) { - if (!v->IsNormalAircraft()) continue; - Station *st = GetTargetAirportIfValid(v); - if (st == nullptr && v->state != FLYING) { - v->state = FLYING; - UpdateAircraftCache(v); - AircraftNextAirportPos_and_Order(v); - /* get aircraft back on running altitude */ - if ((v->vehstatus & VS_CRASHED) == 0) { - GetAircraftFlightLevelBounds(v, &v->z_pos, nullptr); - SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlightLevel(v)); - } - } - } - } - /* Move the animation frame to the same location (m7) for all objects. */ if (IsSavegameVersionBefore(SLV_147)) { for (auto t : Map::Iterate()) { @@ -2792,6 +2770,43 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_ADD_DEPOTS_TO_HANGARS)) { + for (Station *st : Station::Iterate()) { + if ((st->facilities & FACIL_AIRPORT) && st->airport.HasHangar()) { + /* Add a built-in hangar for some airport types. */ + assert(Depot::CanAllocateItem()); + st->airport.AddHangar(); + } else { + /* If airport has no hangar, remove old go to hangar orders + * that could remain from removing an airport with a hangar + * and rebuilding it with an airport with no hangar. */ + RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, st->index); + } + } + } + + /* In old versions it was possible to remove an airport while a plane was + * taking off or landing. This gives all kind of problems when building + * another airport in the same station so we don't allow that anymore. + * For old savegames with such aircraft we just throw them in the air and + * treat the aircraft like they were flying already. */ + if (IsSavegameVersionBefore(SLV_146)) { + for (Aircraft *v : Aircraft::Iterate()) { + if (!v->IsNormalAircraft()) continue; + Station *st = GetTargetAirportIfValid(v); + if (st == nullptr && v->state != FLYING) { + v->state = FLYING; + UpdateAircraftCache(v); + AircraftNextAirportPos_and_Order(v); + /* get aircraft back on running altitude */ + if ((v->vehstatus & VS_CRASHED) == 0) { + GetAircraftFlightLevelBounds(v, &v->z_pos, nullptr); + SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlightLevel(v)); + } + } + } + } + /* This triggers only when old snow_lines were copied into the snow_line_height. */ if (IsSavegameVersionBefore(SLV_164) && _settings_game.game_creation.snow_line_height >= MIN_SNOWLINE_HEIGHT * TILE_HEIGHT) { _settings_game.game_creation.snow_line_height /= TILE_HEIGHT; diff --git a/src/saveload/compat/station_sl_compat.h b/src/saveload/compat/station_sl_compat.h index 1c24a8d5d9..e7b1dbab68 100644 --- a/src/saveload/compat/station_sl_compat.h +++ b/src/saveload/compat/station_sl_compat.h @@ -108,6 +108,7 @@ const SaveLoadCompat _station_normal_sl_compat[] = { SLC_VAR("airport.layout"), SLC_VAR("airport.flags"), SLC_VAR("airport.rotation"), + SLC_VAR("airport.hangar"), SLC_VAR("storage"), SLC_VAR("airport.psa"), SLC_VAR("indtype"), diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 24eb4aa54d..e1962e98c7 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -23,6 +23,7 @@ #include "../stdafx.h" #include "../debug.h" #include "../station_base.h" +#include "../depot_base.h" #include "../thread.h" #include "../town.h" #include "../network/network.h" @@ -1121,6 +1122,7 @@ static size_t ReferenceToInt(const void *obj, SLRefType rt) case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1; case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1; case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1; + case REF_DEPOT: return ((const Depot*)obj)->index + 1; default: NOT_REACHED(); } } @@ -1202,6 +1204,10 @@ static void *IntToReference(size_t index, SLRefType rt) if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index); SlErrorCorrupt("Referencing invalid LinkGraphJob"); + case REF_DEPOT: + if (Depot::IsValidID(index)) return Depot::Get(index); + SlErrorCorrupt("Referencing invalid Depot"); + default: NOT_REACHED(); } } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 9149d42711..46b269d216 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -383,6 +383,7 @@ enum SaveLoadVersion : uint16_t { SLV_GROUP_NUMBERS, ///< 336 PR#12297 Add per-company group numbers. SLV_INCREASE_STATION_TYPE_FIELD_SIZE, ///< 337 PR#12572 Increase size of StationType field in map array SLV_ROAD_WAYPOINTS, ///< 338 PR#12572 Road waypoints + SLV_ADD_DEPOTS_TO_HANGARS, ///< 339 PR#10691 Add depots to airports that have a hangar. SL_MAX_VERSION, ///< Highest possible saveload version }; @@ -598,6 +599,7 @@ enum SLRefType { REF_STORAGE = 9, ///< Load/save a reference to a persistent storage. REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph. REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job. + REF_DEPOT = 12, ///< Load/save a reference to a depot. }; /** diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 2f427d8d52..6b84cdb56f 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -610,6 +610,7 @@ public: SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION), SLE_VAR(Station, airport.flags, SLE_UINT64), SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION), + SLE_CONDREF(Station, airport.hangar, REF_DEPOT, SLV_ADD_DEPOTS_TO_HANGARS, SL_MAX_VERSION), SLEG_CONDARR("storage", _old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161), SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), diff --git a/src/station.cpp b/src/station.cpp index e0dab59067..ee1833963a 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -26,6 +26,7 @@ #include "core/random_func.hpp" #include "linkgraph/linkgraph.h" #include "linkgraph/linkgraphschedule.h" +#include "depot_base.h" #include "table/strings.h" @@ -727,6 +728,29 @@ Money AirportMaintenanceCost(Owner owner) return total_cost >> 3; } +/** + * Create a hangar on the airport. + */ +void Airport::AddHangar() +{ + assert(this->hangar == nullptr); + assert(Depot::CanAllocateItem()); + assert(this->GetNumHangars() > 0); + Station *st = Station::GetByTile(this->GetHangarTile(0)); + this->hangar = new Depot(this->GetHangarTile(0)); + this->hangar->build_date = st->build_date; + this->hangar->town = st->town; +} + +/** + * Delete the hangar on the airport. + */ +void Airport::RemoveHangar() +{ + delete this->hangar; + this->hangar = nullptr; +} + bool StationCompare::operator() (const Station *lhs, const Station *rhs) const { return lhs->index < rhs->index; diff --git a/src/station_base.h b/src/station_base.h index 6dda3ca843..f5db44f23b 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -18,6 +18,7 @@ #include "linkgraph/linkgraph_type.h" #include "newgrf_storage.h" #include "bitmap_type.h" +#include "depot_type.h" static const uint8_t INITIAL_STATION_RATING = 175; static const uint8_t MAX_STATION_RATING = 255; @@ -294,6 +295,7 @@ struct Airport : public TileArea { uint8_t type; ///< Type of this airport, @see AirportTypes uint8_t layout; ///< Airport layout number. Direction rotation; ///< How this airport is rotated. + Depot *hangar; ///< The corresponding hangar of this airport, if any. PersistentStorage *psa; ///< Persistent storage for NewGRF airports. @@ -404,6 +406,9 @@ struct Airport : public TileArea { return num; } + void AddHangar(); + void RemoveHangar(); + private: /** * Retrieve hangar information of a hangar at a given tile. diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 0dd4edb779..f3cefb224e 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -67,6 +67,7 @@ #include "timer/timer_game_tick.h" #include "cheat_type.h" #include "road_func.h" +#include "depot_base.h" #include "widgets/station_widget.h" @@ -2539,6 +2540,8 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, uint8_t airport /* Check if a valid, buildable airport was chosen for construction */ const AirportSpec *as = AirportSpec::Get(airport_type); + + if (!as->depots.empty() && !Depot::CanAllocateItem()) return CMD_ERROR; if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR; if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR; @@ -2632,6 +2635,8 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, uint8_t airport AirportTileAnimationTrigger(st, iter, AAT_BUILT); } + if (!as->depots.empty()) st->airport.AddHangar(); + UpdateAirplanesOnNewStation(st); Company::Get(st->owner)->infrastructure.airport++; @@ -2679,6 +2684,7 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) OrderBackup::Reset(tile_cur, false); CloseWindowById(WC_VEHICLE_DEPOT, tile_cur); } + st->airport.RemoveHangar(); /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. * And as for construction, always remove it, even if the setting is not set, in order to avoid the From 678b6ad46715a512e9f9dd75d2092564fbe88fe8 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 25 Aug 2023 11:39:01 +0200 Subject: [PATCH 2/2] Codechange: Go to hangar orders store the DepotID instead of the StationID. --- src/aircraft_cmd.cpp | 33 +++++------- src/depot_map.h | 11 ++-- src/order_backup.cpp | 8 +-- src/order_backup.h | 2 +- src/order_cmd.cpp | 90 ++++++++++++++++----------------- src/order_func.h | 3 +- src/order_gui.cpp | 6 +-- src/saveload/afterload.cpp | 29 +++++++++++ src/saveload/saveload.h | 2 + src/script/api/script_order.cpp | 7 +-- src/station.cpp | 15 ++++++ src/vehicle.cpp | 2 +- src/vehicle_gui.cpp | 3 +- 13 files changed, 123 insertions(+), 88 deletions(-) diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index 44d76650a1..7ba8dde6db 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -41,6 +41,7 @@ #include "framerate_type.h" #include "aircraft_cmd.h" #include "vehicle_cmd.h" +#include "depot_base.h" #include "table/strings.h" @@ -136,7 +137,7 @@ static StationID FindNearestHangar(const Aircraft *v) if (v->current_order.IsType(OT_GOTO_STATION) || (v->current_order.IsType(OT_GOTO_DEPOT) && (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0)) { last_dest = Station::GetIfValid(v->last_station_visited); - next_dest = Station::GetIfValid(v->current_order.GetDestination()); + next_dest = Station::GetIfValid(GetTargetDestination(v->current_order, true)); } else { last_dest = GetTargetAirportIfValid(v); next_dest = Station::GetIfValid(v->GetNextStoppingStation().value); @@ -407,9 +408,10 @@ ClosestDepot Aircraft::FindClosestDepot() if (station == INVALID_STATION) return ClosestDepot(); st = Station::Get(station); + assert(st->airport.hangar != nullptr); } - return ClosestDepot(st->xy, st->index); + return ClosestDepot(st->xy, st->airport.hangar->index); } static void CheckIfAircraftNeedsService(Aircraft *v) @@ -424,13 +426,13 @@ static void CheckIfAircraftNeedsService(Aircraft *v) * we don't want to consider going to a depot too. */ if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return; - const Station *st = Station::Get(v->current_order.GetDestination()); + const Station *st = Station::Get(GetTargetDestination(v->current_order, true)); assert(st != nullptr); /* only goto depot if the target airport has a depot */ if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) { - v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE); + v->current_order.MakeGoToDepot(st->airport.hangar->index, ODTFB_SERVICE); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); } else if (v->current_order.IsType(OT_GOTO_DEPOT)) { v->current_order.MakeDummy(); @@ -892,7 +894,7 @@ static bool AircraftController(Aircraft *v) /* Jump into our "holding pattern" state machine if possible */ if (v->pos >= afc->nofelements) { v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N); - } else if (v->targetairport != v->current_order.GetDestination()) { + } else if (v->targetairport != GetTargetDestination(v->current_order, true)) { /* If not possible, just get out of here fast */ v->state = FLYING; UpdateAircraftCache(v); @@ -1449,7 +1451,7 @@ static void AircraftLandAirplane(Aircraft *v) void AircraftNextAirportPos_and_Order(Aircraft *v) { if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) { - v->targetairport = v->current_order.GetDestination(); + v->targetairport = GetTargetDestination(v->current_order, true); } const Station *st = GetTargetAirportIfValid(v); @@ -1539,7 +1541,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap return; /* We are leaving a hangar, but have to go to the exact same one; re-enter */ - if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) { + if (v->current_order.IsType(OT_GOTO_DEPOT) && GetTargetDestination(v->current_order, true) == v->targetairport) { VehicleEnterDepot(v); return; } @@ -1548,7 +1550,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return; /* We are already at the target airport, we need to find a terminal */ - if (v->current_order.GetDestination() == v->targetairport) { + if (GetTargetDestination(v->current_order, true) == v->targetairport) { /* FindFreeTerminal: * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */ if (v->subtype == AIR_HELICOPTER) { @@ -1599,7 +1601,7 @@ static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass * case OT_GOTO_STATION: // ready to fly to another airport break; case OT_GOTO_DEPOT: // visit hangar for servicing, sale, etc. - go_to_hangar = v->current_order.GetDestination() == v->targetairport; + go_to_hangar = GetTargetDestination(v->current_order, true) == v->targetairport; break; case OT_CONDITIONAL: /* In case of a conditional order we just have to wait a tick @@ -2103,7 +2105,7 @@ static bool AircraftEventHandler(Aircraft *v, int loop) /* Check the distance to the next destination. This code works because the target * airport is only updated after take off and not on the ground. */ Station *cur_st = Station::GetIfValid(v->targetairport); - Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : nullptr; + Station *next_st = Station::GetIfValid(GetTargetDestination(v->current_order, true)); if (cur_st != nullptr && cur_st->airport.tile != INVALID_TILE && next_st != nullptr && next_st->airport.tile != INVALID_TILE) { uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile); @@ -2169,18 +2171,7 @@ void UpdateAirplanesOnNewStation(const Station *st) if (!v->IsNormalAircraft() || v->targetairport != st->index) continue; assert(v->state == FLYING); - Order *o = &v->current_order; - /* The aircraft is heading to a hangar, but the new station doesn't have one, - * or the aircraft can't land on the new station. Cancel current order. */ - if (o->IsType(OT_GOTO_DEPOT) && !(o->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) && o->GetDestination() == st->index && - (!st->airport.HasHangar() || !CanVehicleUseStation(v, st))) { - o->MakeDummy(); - SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); - } v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation); UpdateAircraftCache(v); } - - /* Heliports don't have a hangar. Invalidate all go to hangar orders from all aircraft. */ - if (!st->airport.HasHangar()) RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, st->index, true); } diff --git a/src/depot_map.h b/src/depot_map.h index 87bd915431..78a0f0273c 100644 --- a/src/depot_map.h +++ b/src/depot_map.h @@ -43,16 +43,21 @@ inline bool IsDepotTile(Tile tile) return IsRailDepotTile(tile) || IsRoadDepotTile(tile) || IsShipDepotTile(tile) || IsHangarTile(tile); } +extern DepotID GetHangarIndex(TileIndex t); + /** * Get the index of which depot is attached to the tile. * @param t the tile - * @pre IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t) + * @pre IsDepotTile(t) * @return DepotID */ inline DepotID GetDepotIndex(Tile t) { - /* Hangars don't have a Depot class, thus store no DepotID. */ - assert(IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t)); + assert(IsDepotTile(t)); + + /* Hangars don't store depot id on m2. */ + if (IsTileType(t, MP_STATION)) return GetHangarIndex(t); + return t.m2(); } diff --git a/src/order_backup.cpp b/src/order_backup.cpp index f696d8435d..9843bbf36f 100644 --- a/src/order_backup.cpp +++ b/src/order_backup.cpp @@ -246,18 +246,14 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID us * Removes an order from all vehicles. Triggers when, say, a station is removed. * @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]). * @param destination The destination. Can be a StationID, DepotID or WaypointID. - * @param hangar Only used for airports in the destination. - * When false, remove airport and hangar orders. - * When true, remove either airport or hangar order. */ -/* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination, bool hangar) +/* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination) { for (OrderBackup *ob : OrderBackup::Iterate()) { for (Order *order = ob->orders; order != nullptr; order = order->next) { OrderType ot = order->GetType(); if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue; - if (ot == OT_GOTO_DEPOT && hangar && !IsHangarTile(ob->tile)) continue; // Not an aircraft? Can't have a hangar order. - if (ot == OT_IMPLICIT || (IsHangarTile(ob->tile) && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION; + if (ot == OT_IMPLICIT) ot = OT_GOTO_STATION; if (ot == type && order->GetDestination() == destination) { /* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */ delete ob; diff --git a/src/order_backup.h b/src/order_backup.h index 8616c564ee..23832d59af 100644 --- a/src/order_backup.h +++ b/src/order_backup.h @@ -59,7 +59,7 @@ public: static void ClearGroup(GroupID group); static void ClearVehicle(const Vehicle *v); - static void RemoveOrder(OrderType type, DestinationID destination, bool hangar); + static void RemoveOrder(OrderType type, DestinationID destination); }; #endif /* ORDER_BACKUP_H */ diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 1d6743106e..070b0d84b1 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -650,7 +650,7 @@ TileIndex Order::GetLocation(const Vehicle *v, bool airport) const case OT_GOTO_DEPOT: if (this->GetDestination() == INVALID_DEPOT) return INVALID_TILE; - return (v->type == VEH_AIRCRAFT) ? Station::Get(this->GetDestination())->xy : Depot::Get(this->GetDestination())->xy; + return Depot::Get(this->GetDestination())->xy; default: return INVALID_TILE; @@ -684,6 +684,28 @@ uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int return v->type == VEH_AIRCRAFT ? DistanceSquare(prev_tile, cur_tile) : DistanceManhattan(prev_tile, cur_tile); } +/** + * Get the station or depot index associated to an order of a vehicle. + * For aircraft, it will return the index of the associated station, even for go to hangar orders. + * @param o Order to check. + * @param is_aircraft Whether the order is of an aircraft vehicle. + * @return index associated to a station or depot, or INVALID_STATION. + */ +DestinationID GetTargetDestination(const Order &o, bool is_aircraft) +{ + DestinationID destination_id = o.GetDestination(); + switch (o.GetType()) { + case OT_GOTO_STATION: + return destination_id; + case OT_GOTO_DEPOT: + assert(Depot::IsValidID(destination_id)); + return is_aircraft ? GetStationIndex(Depot::Get(destination_id)->xy) : destination_id; + default: + return INVALID_STATION; + } +} + + /** * Add an order to the orderlist of a vehicle. * @param flags operation to perform @@ -763,40 +785,31 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se case OT_GOTO_DEPOT: { if ((new_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0) { - if (v->type == VEH_AIRCRAFT) { - const Station *st = Station::GetIfValid(new_order.GetDestination()); + const Depot *dp = Depot::GetIfValid(new_order.GetDestination()); - if (st == nullptr) return CMD_ERROR; + if (dp == nullptr) return CMD_ERROR; - ret = CheckOwnership(st->owner); - if (ret.Failed()) return ret; + ret = CheckOwnership(GetTileOwner(dp->xy)); + if (ret.Failed()) return ret; - if (!CanVehicleUseStation(v, st) || !st->airport.HasHangar()) { - return CMD_ERROR; - } - } else { - const Depot *dp = Depot::GetIfValid(new_order.GetDestination()); + switch (v->type) { + case VEH_TRAIN: + if (!IsRailDepotTile(dp->xy)) return CMD_ERROR; + break; - if (dp == nullptr) return CMD_ERROR; + case VEH_ROAD: + if (!IsRoadDepotTile(dp->xy)) return CMD_ERROR; + break; - ret = CheckOwnership(GetTileOwner(dp->xy)); - if (ret.Failed()) return ret; + case VEH_SHIP: + if (!IsShipDepotTile(dp->xy)) return CMD_ERROR; + break; - switch (v->type) { - case VEH_TRAIN: - if (!IsRailDepotTile(dp->xy)) return CMD_ERROR; - break; - - case VEH_ROAD: - if (!IsRoadDepotTile(dp->xy)) return CMD_ERROR; - break; - - case VEH_SHIP: - if (!IsShipDepotTile(dp->xy)) return CMD_ERROR; - break; + case VEH_AIRCRAFT: + if (!CanVehicleUseStation(v, Station::GetByTile(dp->xy)) || !IsHangarTile(dp->xy)) return CMD_ERROR; + break; default: return CMD_ERROR; - } } } @@ -1780,24 +1793,11 @@ void CheckOrders(const Vehicle *v) * Removes an order from all vehicles. Triggers when, say, a station is removed. * @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]). * @param destination The destination. Can be a StationID, DepotID or WaypointID. - * @param hangar Only used for airports in the destination. - * When false, remove airport and hangar orders. - * When true, remove either airport or hangar order. */ -void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar) +void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination) { - /* Aircraft have StationIDs for depot orders and never use DepotIDs - * This fact is handled specially below - */ - /* Go through all vehicles */ for (Vehicle *v : Vehicle::Iterate()) { - if ((v->type == VEH_AIRCRAFT && v->current_order.IsType(OT_GOTO_DEPOT) && !hangar ? OT_GOTO_STATION : v->current_order.GetType()) == type && - (!hangar || v->type == VEH_AIRCRAFT) && v->current_order.GetDestination() == destination) { - v->current_order.MakeDummy(); - SetWindowDirty(WC_VEHICLE_VIEW, v->index); - } - /* Clear the order from the order-list */ int id = -1; for (Order *order : v->Orders()) { @@ -1806,8 +1806,7 @@ restart: OrderType ot = order->GetType(); if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue; - if (ot == OT_GOTO_DEPOT && hangar && v->type != VEH_AIRCRAFT) continue; // Not an aircraft? Can't have a hangar order. - if (ot == OT_IMPLICIT || (v->type == VEH_AIRCRAFT && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION; + if (ot == OT_IMPLICIT) ot = OT_GOTO_STATION; if (ot == type && order->GetDestination() == destination) { /* We want to clear implicit orders, but we don't want to make them * dummy orders. They should just vanish. Also check the actual order @@ -1841,7 +1840,7 @@ restart: } } - OrderBackup::RemoveOrder(type, destination, hangar); + OrderBackup::RemoveOrder(type, destination); } /** @@ -2054,7 +2053,8 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool v->SetDestTile(Depot::Get(order->GetDestination())->xy); } else { Aircraft *a = Aircraft::From(v); - DestinationID destination = a->current_order.GetDestination(); + DestinationID destination_depot = a->current_order.GetDestination(); + StationID destination = GetStationIndex(Depot::Get(destination_depot)->xy); if (a->targetairport != destination) { /* The aircraft is now heading for a different hangar than the next in the orders */ a->SetDestTile(a->GetOrderStationLocation(destination)); diff --git a/src/order_func.h b/src/order_func.h index 12f7d4684a..797066e1eb 100644 --- a/src/order_func.h +++ b/src/order_func.h @@ -15,7 +15,7 @@ #include "company_type.h" /* Functions */ -void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar = false); +void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination); void InvalidateVehicleOrder(const Vehicle *v, int data); void CheckOrders(const Vehicle*); void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false, bool reset_order_indices = true); @@ -23,6 +23,7 @@ bool ProcessOrders(Vehicle *v); bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0, bool pbs_look_ahead = false); VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v); uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int conditional_depth = 0); +DestinationID GetTargetDestination(const Order &o, bool is_aircraft); void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable, int left, int middle, int right); diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 77b68d9479..3bcfda6c49 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -34,6 +34,7 @@ #include "error.h" #include "order_cmd.h" #include "company_cmd.h" +#include "depot_base.h" #include "widgets/order_widget.h" @@ -309,7 +310,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int /* Going to a specific depot. */ SetDParam(0, STR_ORDER_GO_TO_DEPOT_FORMAT); SetDParam(2, v->type); - SetDParam(3, order->GetDestination()); + SetDParam(3, GetTargetDestination(*order, v->type == VEH_AIRCRAFT)); } if (order->GetDepotOrderType() & ODTFB_SERVICE) { @@ -384,8 +385,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile) /* check depot first */ if (IsDepotTypeTile(tile, (TransportType)(uint)v->type) && IsTileOwner(tile, _local_company)) { - order.MakeGoToDepot(v->type == VEH_AIRCRAFT ? GetStationIndex(tile) : GetDepotIndex(tile), - ODTFB_PART_OF_ORDERS, + order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS, (_settings_client.gui.new_nonstop && v->IsGroundVehicle()) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE); if (_ctrl_pressed) { diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index be048f33c8..7f35427e0a 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2785,6 +2785,35 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_DEPOTID_IN_HANGAR_ORDERS)) { + /* Update go to hangar orders so they store the DepotID instead of StationID. */ + for (Aircraft *a : Aircraft::Iterate()) { + if (!a->IsNormalAircraft()) continue; + + /* Update current order. */ + if (a->current_order.IsType(OT_GOTO_DEPOT)) { + Depot *dep = Station::Get(a->current_order.GetDestination())->airport.hangar; + if (dep == nullptr) { + /* Aircraft heading to a removed hangar. */ + a->current_order.MakeDummy(); + } else { + a->current_order.SetDestination(dep->index); + } + } + + /* Update each aircraft order list once. */ + if (a->orders == nullptr) continue; + if (a->orders->GetFirstSharedVehicle() != a) continue; + + for (Order *order : a->Orders()) { + if (!order->IsType(OT_GOTO_DEPOT)) continue; + StationID station_id = order->GetDestination(); + Station *st = Station::Get(station_id); + order->SetDestination(st->airport.hangar->index); + } + } + } + /* In old versions it was possible to remove an airport while a plane was * taking off or landing. This gives all kind of problems when building * another airport in the same station so we don't allow that anymore. diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 46b269d216..801be47716 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -385,6 +385,8 @@ enum SaveLoadVersion : uint16_t { SLV_ROAD_WAYPOINTS, ///< 338 PR#12572 Road waypoints SLV_ADD_DEPOTS_TO_HANGARS, ///< 339 PR#10691 Add depots to airports that have a hangar. + SLV_DEPOTID_IN_HANGAR_ORDERS, ///< 340 PR#10691 Go to hangar orders store the DepotID instead of StationID. + SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/script/api/script_order.cpp b/src/script/api/script_order.cpp index b68f7fd67a..5dcd39ac4e 100644 --- a/src/script/api/script_order.cpp +++ b/src/script/api/script_order.cpp @@ -245,18 +245,13 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr const Order *order = ::ResolveOrder(vehicle_id, order_position); if (order == nullptr || order->GetType() == OT_CONDITIONAL) return INVALID_TILE; - const Vehicle *v = ::Vehicle::Get(vehicle_id); switch (order->GetType()) { case OT_GOTO_DEPOT: { /* We don't know where the nearest depot is... (yet) */ if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) return INVALID_TILE; - if (v->type != VEH_AIRCRAFT) return ::Depot::Get(order->GetDestination())->xy; - /* Aircraft's hangars are referenced by StationID, not DepotID */ - const Station *st = ::Station::Get(order->GetDestination()); - if (!st->airport.HasHangar()) return INVALID_TILE; - return st->airport.GetHangarTile(0); + return ::Depot::Get(order->GetDestination())->xy; } case OT_GOTO_STATION: { diff --git a/src/station.cpp b/src/station.cpp index ee1833963a..0ee8f2a605 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -747,10 +747,25 @@ void Airport::AddHangar() */ void Airport::RemoveHangar() { + RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->hangar->index); + + for (Aircraft *a : Aircraft::Iterate()) { + if (!a->IsNormalAircraft()) continue; + if (!a->current_order.IsType(OT_GOTO_DEPOT)) continue; + if (a->current_order.GetDestination() != this->hangar->index) continue; + a->current_order.MakeDummy(); + } + delete this->hangar; this->hangar = nullptr; } +DepotID GetHangarIndex(TileIndex t) { + assert(IsAirportTile(t)); + assert(Station::GetByTile(t)->airport.hangar != nullptr); + return Station::GetByTile(t)->airport.hangar->index; +} + bool StationCompare::operator() (const Station *lhs, const Station *rhs) const { return lhs->index < rhs->index; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 9db7da2fa1..8a7a471e8a 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1619,7 +1619,7 @@ void VehicleEnterDepot(Vehicle *v) * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */ if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) && real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) && - (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) { + v->current_order.GetDestination() != GetDepotIndex(v->tile)) { /* We are heading for another depot, keep driving. */ return; } diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 2a43b351a3..aaaf10f993 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -45,6 +45,7 @@ #include "train_cmd.h" #include "hotkeys.h" #include "group_cmd.h" +#include "depot_base.h" #include "safeguards.h" @@ -3141,7 +3142,7 @@ public: case OT_GOTO_DEPOT: { SetDParam(0, v->type); - SetDParam(1, v->current_order.GetDestination()); + SetDParam(1, GetTargetDestination(v->current_order, v->type == VEH_AIRCRAFT)); SetDParam(2, PackVelocity(v->GetDisplaySpeed(), v->type)); if (v->current_order.GetDestination() == INVALID_DEPOT) { /* This case *only* happens when multiple nearest depot orders