From 4d706f945abbe79297daedac546f75ac022eb089 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Mon, 19 Feb 2024 21:25:38 +0100 Subject: [PATCH 001/143] 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 06102930b7e75..be048f33c869e 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 1c24a8d5d9220..e7b1dbab681fb 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 24eb4aa54dc4e..e1962e98c7ec6 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 9149d42711420..46b269d2166c7 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 2f427d8d52989..6b84cdb56f2e9 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -610,6 +610,7 @@ class SlStationNormal : public DefaultSaveLoadHandler> 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 6dda3ca8435cb..f5db44f23b677 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 0dd4edb779406..f3cefb224e510 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 d2fd753f97fe626d0716ed7805dad9485f068dc4 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 25 Aug 2023 11:39:01 +0200 Subject: [PATCH 002/143] 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 44d76650a171b..7ba8dde6db39e 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 87bd91543145c..78a0f0273c742 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 f696d8435dea5..9843bbf36f138 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 8616c564eed24..23832d59afe74 100644 --- a/src/order_backup.h +++ b/src/order_backup.h @@ -59,7 +59,7 @@ struct OrderBackup : OrderBackupPool::PoolItem<&_order_backup_pool>, BaseConsist 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 1d6743106efe2..070b0d84b1d84 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; - - if (!CanVehicleUseStation(v, st) || !st->airport.HasHangar()) { - return CMD_ERROR; - } - } else { - const Depot *dp = Depot::GetIfValid(new_order.GetDestination()); - - if (dp == nullptr) return CMD_ERROR; + ret = CheckOwnership(GetTileOwner(dp->xy)); + if (ret.Failed()) return ret; - ret = CheckOwnership(GetTileOwner(dp->xy)); - if (ret.Failed()) return ret; + switch (v->type) { + case VEH_TRAIN: + if (!IsRailDepotTile(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_ROAD: - if (!IsRoadDepotTile(dp->xy)) return CMD_ERROR; - break; + case VEH_SHIP: + if (!IsShipDepotTile(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 @@ void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool 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 @@ void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool } } - 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 12f7d4684a9b9..797066e1eb57a 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 77b68d9479b2f..3bcfda6c4904e 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 be048f33c869e..7f35427e0a1eb 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 46b269d2166c7..801be47716fe2 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 b68f7fd67a7f2..5dcd39ac4eb60 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 ee1833963a291..0ee8f2a60593a 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 9db7da2fa1c8f..8a7a471e8affd 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 2a43b351a39ae..aaaf10f9935d2 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 @@ struct VehicleViewWindow : Window { 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 From 1c7b8db520c758eeea9522ae43475898e5e3fe83 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 25 Aug 2023 14:49:03 +0200 Subject: [PATCH 003/143] Codechange: Set the DepotID as the window number of WC_VEHICLE_DEPOT windows instead of a TileIndex. --- src/aircraft_cmd.cpp | 4 +- src/depot.cpp | 2 +- src/depot_cmd.cpp | 2 +- src/depot_func.h | 2 +- src/depot_gui.cpp | 97 +++++++++++++++++++++----------------------- src/group_cmd.cpp | 3 +- src/rail_cmd.cpp | 9 ++-- src/road_cmd.cpp | 9 ++-- src/roadveh_cmd.cpp | 2 +- src/ship_cmd.cpp | 5 ++- src/station_cmd.cpp | 5 +-- src/train_cmd.cpp | 19 +++++---- src/vehicle.cpp | 13 +++--- src/vehicle_cmd.cpp | 7 ++-- src/water_cmd.cpp | 2 +- src/window_type.h | 2 +- 16 files changed, 94 insertions(+), 89 deletions(-) diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index 7ba8dde6db39e..be615812a4218 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -1490,7 +1490,9 @@ void AircraftLeaveHangar(Aircraft *v, Direction exit_dir) VehicleServiceInDepot(v); v->LeaveUnbunchingDepot(); SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos); - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + + /* When called from UpdateOldAircraft(), tile may not be a hangar. */ + if (IsHangarTile(v->tile)) InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); SetWindowClassesDirty(WC_AIRCRAFT_LIST); } diff --git a/src/depot.cpp b/src/depot.cpp index 13317e8a35dad..38330a873993a 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -41,7 +41,7 @@ Depot::~Depot() RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->index); /* Delete the depot-window */ - CloseWindowById(WC_VEHICLE_DEPOT, this->xy); + CloseWindowById(WC_VEHICLE_DEPOT, this->index); /* Delete the depot list */ VehicleType vt = GetDepotVehicleType(this->xy); diff --git a/src/depot_cmd.cpp b/src/depot_cmd.cpp index 294de69e32ea7..1e1700f6cbf32 100644 --- a/src/depot_cmd.cpp +++ b/src/depot_cmd.cpp @@ -68,7 +68,7 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str /* Update the orders and depot */ SetWindowClassesDirty(WC_VEHICLE_ORDERS); - SetWindowDirty(WC_VEHICLE_DEPOT, d->xy); + SetWindowDirty(WC_VEHICLE_DEPOT, d->index); /* Update the depot list */ VehicleType vt = GetDepotVehicleType(d->xy); diff --git a/src/depot_func.h b/src/depot_func.h index 208e02110c9f3..7b3b6a9eeb0a2 100644 --- a/src/depot_func.h +++ b/src/depot_func.h @@ -13,7 +13,7 @@ #include "vehicle_type.h" #include "slope_func.h" -void ShowDepotWindow(TileIndex tile, VehicleType type); +void ShowDepotWindow(DepotID depot_id); void InitDepotWindowBlockSizes(); void DeleteDepotHighlightOfVehicle(const Vehicle *v); diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index e62915fe2c5bb..750beb96ebe7c 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -266,15 +266,17 @@ struct DepotWindow : Window { Scrollbar *hscroll; ///< Only for trains. Scrollbar *vscroll; - DepotWindow(WindowDesc &desc, TileIndex tile, VehicleType type) : Window(desc) + DepotWindow(WindowDesc &desc, DepotID depot_id) : Window(desc) { - assert(IsCompanyBuildableVehicleType(type)); // ensure that we make the call with a valid type + assert(Depot::IsValidID(depot_id)); + Depot *depot = Depot::Get(depot_id); + assert(IsCompanyBuildableVehicleType(GetDepotVehicleType(depot->xy))); this->sel = INVALID_VEHICLE; this->vehicle_over = INVALID_VEHICLE; this->generate_list = true; this->hovered_widget = -1; - this->type = type; + this->type = GetDepotVehicleType(depot->xy); this->num_columns = 1; // for non-trains this gets set in FinishInitNested() this->unitnumber_digits = 2; @@ -282,22 +284,22 @@ struct DepotWindow : Window { this->hscroll = (this->type == VEH_TRAIN ? this->GetScrollbar(WID_D_H_SCROLL) : nullptr); this->vscroll = this->GetScrollbar(WID_D_V_SCROLL); /* Don't show 'rename button' of aircraft hangar */ - this->GetWidget(WID_D_SHOW_RENAME)->SetDisplayedPlane(type == VEH_AIRCRAFT ? SZSP_NONE : 0); + this->GetWidget(WID_D_SHOW_RENAME)->SetDisplayedPlane(this->type == VEH_AIRCRAFT ? SZSP_NONE : 0); /* Only train depots have a horizontal scrollbar and a 'sell chain' button */ - if (type == VEH_TRAIN) this->GetWidget(WID_D_MATRIX)->widget_data = 1 << MAT_COL_START; - this->GetWidget(WID_D_SHOW_H_SCROLL)->SetDisplayedPlane(type == VEH_TRAIN ? 0 : SZSP_HORIZONTAL); - this->GetWidget(WID_D_SHOW_SELL_CHAIN)->SetDisplayedPlane(type == VEH_TRAIN ? 0 : SZSP_NONE); - this->SetupWidgetData(type); - this->FinishInitNested(tile); + if (this->type == VEH_TRAIN) this->GetWidget(WID_D_MATRIX)->widget_data = 1 << MAT_COL_START; + this->GetWidget(WID_D_SHOW_H_SCROLL)->SetDisplayedPlane(this->type == VEH_TRAIN ? 0 : SZSP_HORIZONTAL); + this->GetWidget(WID_D_SHOW_SELL_CHAIN)->SetDisplayedPlane(this->type == VEH_TRAIN ? 0 : SZSP_NONE); + this->SetupWidgetData(this->type); + this->FinishInitNested(depot_id); - this->owner = GetTileOwner(tile); + this->owner = GetTileOwner(depot->xy); OrderBackup::Reset(); } void Close([[maybe_unused]] int data = 0) override { CloseWindowById(WC_BUILD_VEHICLE, this->window_number); - CloseWindowById(GetWindowClassForVehicleType(this->type), VehicleListIdentifier(VL_DEPOT_LIST, this->type, this->owner, this->GetDepotIndex()).Pack(), false); + CloseWindowById(GetWindowClassForVehicleType(this->type), VehicleListIdentifier(VL_DEPOT_LIST, this->type, this->owner, this->window_number).Pack(), false); OrderBackup::Reset(this->window_number); this->Window::Close(); } @@ -426,7 +428,7 @@ struct DepotWindow : Window { if (widget != WID_D_CAPTION) return; SetDParam(0, this->type); - SetDParam(1, this->GetDepotIndex()); + SetDParam(1, (this->type == VEH_AIRCRAFT) ? GetStationIndex(Depot::Get(this->window_number)->xy) : this->window_number); } struct GetDepotVehiclePtData { @@ -711,7 +713,7 @@ struct DepotWindow : Window { if (this->generate_list) { /* Generate the vehicle list * It's ok to use the wagon pointers for non-trains as they will be ignored */ - BuildDepotVehicleList(this->type, this->window_number, &this->vehicle_list, &this->wagon_list); + BuildDepotVehicleList(this->type, Depot::Get(this->window_number)->xy, &this->vehicle_list, &this->wagon_list); this->generate_list = false; DepotSortList(&this->vehicle_list); @@ -742,8 +744,7 @@ struct DepotWindow : Window { } /* Setup disabled buttons. */ - TileIndex tile = this->window_number; - this->SetWidgetsDisabledState(!IsTileOwner(tile, _local_company), + this->SetWidgetsDisabledState(this->owner != _local_company, WID_D_STOP_ALL, WID_D_START_ALL, WID_D_SELL, @@ -759,6 +760,8 @@ struct DepotWindow : Window { void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { + TileIndex tile = Depot::Get(this->window_number)->xy; + switch (widget) { case WID_D_MATRIX: // List this->DepotClick(pt.x, pt.y); @@ -766,7 +769,7 @@ struct DepotWindow : Window { case WID_D_BUILD: // Build vehicle ResetObjectToPlace(); - ShowBuildVehicleWindow(this->window_number, this->type); + ShowBuildVehicleWindow(tile, this->type); break; case WID_D_CLONE: // Clone button @@ -789,20 +792,20 @@ struct DepotWindow : Window { if (_ctrl_pressed) { ShowExtraViewportWindow(this->window_number); } else { - ScrollMainWindowToTile(this->window_number); + ScrollMainWindowToTile(tile); } break; case WID_D_RENAME: // Rename button SetDParam(0, this->type); - SetDParam(1, Depot::GetByTile((TileIndex)this->window_number)->index); + SetDParam(1, this->window_number); ShowQueryString(STR_DEPOT_NAME, STR_DEPOT_RENAME_DEPOT_CAPTION, MAX_LENGTH_DEPOT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS); break; case WID_D_STOP_ALL: case WID_D_START_ALL: { VehicleListIdentifier vli(VL_DEPOT_LIST, this->type, this->owner); - Command::Post(this->window_number, widget == WID_D_START_ALL, false, vli); + Command::Post(tile, widget == WID_D_START_ALL, false, vli); break; } @@ -810,7 +813,7 @@ struct DepotWindow : Window { /* Only open the confirmation window if there are anything to sell */ if (!this->vehicle_list.empty() || !this->wagon_list.empty()) { SetDParam(0, this->type); - SetDParam(1, this->GetDepotIndex()); + SetDParam(1, this->window_number); ShowQuery( STR_DEPOT_CAPTION, STR_DEPOT_SELL_CONFIRMATION_TEXT, @@ -821,11 +824,11 @@ struct DepotWindow : Window { break; case WID_D_VEHICLE_LIST: - ShowVehicleListWindow(GetTileOwner(this->window_number), this->type, (TileIndex)this->window_number); + ShowVehicleListWindow(this->owner, this->type, tile); break; case WID_D_AUTOREPLACE: - Command::Post(this->window_number, this->type); + Command::Post(tile, this->type); break; } @@ -836,7 +839,7 @@ struct DepotWindow : Window { if (!str.has_value()) return; /* Do depot renaming */ - Command::Post(STR_ERROR_CAN_T_RENAME_DEPOT, this->GetDepotIndex(), *str); + Command::Post(STR_ERROR_CAN_T_RENAME_DEPOT, this->window_number, *str); } bool OnRightClick([[maybe_unused]] Point pt, WidgetID widget) override @@ -902,10 +905,10 @@ struct DepotWindow : Window { { if (_ctrl_pressed) { /* Share-clone, do not open new viewport, and keep tool active */ - Command::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, this->window_number, v->index, true); + Command::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, Depot::Get(this->window_number)->xy, v->index, true); } else { /* Copy-clone, open viewport for new vehicle, and deselect the tool (assume player wants to change things on new vehicle) */ - if (Command::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, CcCloneVehicle, this->window_number, v->index, false)) { + if (Command::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, CcCloneVehicle, Depot::Get(this->window_number)->xy, v->index, false)) { ResetObjectToPlace(); } } @@ -1111,43 +1114,33 @@ struct DepotWindow : Window { return ES_NOT_HANDLED; } - - /** - * Gets the DepotID of the current window. - * In the case of airports, this is the station ID. - * @return Depot or station ID of this window. - */ - inline uint16_t GetDepotIndex() const - { - return (this->type == VEH_AIRCRAFT) ? ::GetStationIndex(this->window_number) : ::GetDepotIndex(this->window_number); - } }; static void DepotSellAllConfirmationCallback(Window *win, bool confirmed) { if (confirmed) { - DepotWindow *w = (DepotWindow*)win; - TileIndex tile = w->window_number; - VehicleType vehtype = w->type; - Command::Post(tile, vehtype); + assert(Depot::IsValidID(win->window_number)); + Depot *d = Depot::Get(win->window_number); + Command::Post(d->xy, GetDepotVehicleType(d->xy)); } } /** - * Opens a depot window - * @param tile The tile where the depot/hangar is located - * @param type The type of vehicles in the depot + * Opens a depot window. + * @param depot_id Index of the depot. */ -void ShowDepotWindow(TileIndex tile, VehicleType type) +void ShowDepotWindow(DepotID depot_id) { - if (BringWindowToFrontById(WC_VEHICLE_DEPOT, tile) != nullptr) return; + assert(Depot::IsValidID(depot_id)); + if (BringWindowToFrontById(WC_VEHICLE_DEPOT, depot_id) != nullptr) return; - switch (type) { + Depot *d = Depot::Get(depot_id); + switch (GetDepotVehicleType(d->xy)) { default: NOT_REACHED(); - case VEH_TRAIN: new DepotWindow(_train_depot_desc, tile, type); break; - case VEH_ROAD: new DepotWindow(_road_depot_desc, tile, type); break; - case VEH_SHIP: new DepotWindow(_ship_depot_desc, tile, type); break; - case VEH_AIRCRAFT: new DepotWindow(_aircraft_depot_desc, tile, type); break; + case VEH_TRAIN: new DepotWindow(_train_depot_desc, depot_id); break; + case VEH_ROAD: new DepotWindow(_road_depot_desc, depot_id); break; + case VEH_SHIP: new DepotWindow(_ship_depot_desc, depot_id); break; + case VEH_AIRCRAFT: new DepotWindow(_aircraft_depot_desc, depot_id); break; } } @@ -1164,7 +1157,11 @@ void DeleteDepotHighlightOfVehicle(const Vehicle *v) */ if (_special_mouse_mode != WSM_DRAGDROP) return; - w = dynamic_cast(FindWindowById(WC_VEHICLE_DEPOT, v->tile)); + /* For shadows and rotors, do nothing. */ + if (v->type == VEH_AIRCRAFT && !Aircraft::From(v)->IsNormalAircraft()) return; + + assert(IsDepotTile(v->tile)); + w = dynamic_cast(FindWindowById(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile))); if (w != nullptr) { if (w->sel == v->index) ResetObjectToPlace(); } diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index 96aa2d1c9fe79..3930c81469d93 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -19,6 +19,7 @@ #include "core/pool_func.hpp" #include "order_backup.h" #include "group_cmd.h" +#include "depot_map.h" #include "table/strings.h" @@ -579,7 +580,7 @@ std::tuple CmdAddVehicleGroup(DoCommandFlag flags, GroupID } } - SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); + if (IsDepotTypeTile(v->tile, (TransportType)v->type)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); SetWindowDirty(WC_VEHICLE_VIEW, v->index); SetWindowDirty(WC_VEHICLE_DETAILS, v->index); InvalidateWindowData(WC_VEHICLE_VIEW, v->index); diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 0429661eda40c..a44d5d5521056 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1654,8 +1654,9 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile)); /* Update build vehicle window related to this depot */ - InvalidateWindowData(WC_VEHICLE_DEPOT, tile); - InvalidateWindowData(WC_BUILD_VEHICLE, tile); + DepotID depot_id = GetDepotIndex(tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); + InvalidateWindowData(WC_BUILD_VEHICLE, depot_id); } found_convertible_track = true; cost.AddCost(RailConvertCost(type, totype)); @@ -2774,7 +2775,7 @@ static bool ClickTile_Track(TileIndex tile) { if (!IsRailDepot(tile)) return false; - ShowDepotWindow(tile, VEH_TRAIN); + ShowDepotWindow(GetDepotIndex(tile)); return true; } @@ -2973,7 +2974,7 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int if (v->Next() == nullptr) VehicleEnterDepot(v->First()); v->tile = tile; - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); return VETSB_ENTERED_WORMHOLE; } diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index e4489c86218fa..c673fc991a2a1 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -2081,7 +2081,7 @@ static bool ClickTile_Road(TileIndex tile) { if (!IsRoadDepot(tile)) return false; - ShowDepotWindow(tile, VEH_ROAD); + ShowDepotWindow(GetDepotIndex(tile)); return true; } @@ -2269,7 +2269,7 @@ static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int, if (rv->Next() == nullptr) VehicleEnterDepot(rv->First()); rv->tile = tile; - InvalidateWindowData(WC_VEHICLE_DEPOT, rv->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile)); return VETSB_ENTERED_WORMHOLE; } break; @@ -2543,8 +2543,9 @@ CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_s if (IsRoadDepotTile(tile)) { /* Update build vehicle window related to this depot */ - InvalidateWindowData(WC_VEHICLE_DEPOT, tile); - InvalidateWindowData(WC_BUILD_VEHICLE, tile); + DepotID depot_id = GetDepotIndex(tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); + InvalidateWindowData(WC_BUILD_VEHICLE, depot_id); } } } else { diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 957dda01caa29..beb641b9dfdb3 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -1043,7 +1043,7 @@ bool RoadVehLeaveDepot(RoadVehicle *v, bool first) v->UpdatePosition(); v->UpdateInclination(true, true); - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); return true; } diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index dcd2f6825fe32..8577169c3269a 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -424,12 +424,13 @@ static bool CheckShipLeaveDepot(Ship *v) v->cur_speed = 0; v->UpdateViewport(true, true); - SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); + DepotID depot_id = GetDepotIndex(v->tile); + SetWindowDirty(WC_VEHICLE_DEPOT, depot_id); VehicleServiceInDepot(v); v->LeaveUnbunchingDepot(); v->PlayLeaveStationSound(); - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); SetWindowClassesDirty(WC_SHIPS_LIST); return false; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index f3cefb224e510..9bbaf5f4ac66a 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2682,7 +2682,6 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) for (uint i = 0; i < st->airport.GetNumHangars(); ++i) { TileIndex tile_cur = st->airport.GetHangarTile(i); OrderBackup::Reset(tile_cur, false); - CloseWindowById(WC_VEHICLE_DEPOT, tile_cur); } st->airport.RemoveHangar(); @@ -3698,8 +3697,8 @@ static bool ClickTile_Station(TileIndex tile) if (bst->facilities & FACIL_WAYPOINT) { ShowWaypointWindow(Waypoint::From(bst)); } else if (IsHangar(tile)) { - const Station *st = Station::From(bst); - ShowDepotWindow(st->airport.GetHangarTile(st->airport.GetHangarNum(tile)), VEH_AIRCRAFT); + assert(Station::From(bst)->airport.HasHangar()); + ShowDepotWindow(Station::From(bst)->airport.hangar->index); } else { ShowStationViewWindow(bst->index); } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index a9fb14ee54923..9b77dddd04d91 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -615,6 +615,7 @@ void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret) { const RailVehicleInfo *rvi = &e->u.rail; + assert(IsRailDepotTile(tile)); /* Check that the wagon can drive on the track in question */ if (!IsCompatibleRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR; @@ -645,7 +646,7 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const v->SetWagon(); v->SetFreeWagon(); - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(tile)); v->cargo_type = e->GetDefaultCargoType(); assert(IsValidCargoID(v->cargo_type)); @@ -1363,7 +1364,7 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID if (dst_head != nullptr) dst_head->First()->MarkDirty(); /* We are undoubtedly changing something in the depot and train list. */ - InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(src->tile)); InvalidateWindowClassesData(WC_TRAINS_LIST, 0); } else { /* We don't want to execute what we're just tried. */ @@ -1447,7 +1448,7 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b NormaliseTrainHead(new_head); /* We are undoubtedly changing something in the depot and train list. */ - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); InvalidateWindowClassesData(WC_TRAINS_LIST, 0); /* Actually delete the sold 'goods' */ @@ -1966,7 +1967,7 @@ void ReverseTrainDirection(Train *v) { if (IsRailDepotTile(v->tile)) { if (IsWholeTrainInsideDepot(v)) return; - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); } /* Clear path reservation in front if train is not stuck. */ @@ -1989,7 +1990,7 @@ void ReverseTrainDirection(Train *v) AdvanceWagonsAfterSwap(v); if (IsRailDepotTile(v->tile)) { - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); } ToggleBit(v->flags, VRF_TOGGLE_REVERSE); @@ -2079,7 +2080,7 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool ToggleBit(v->flags, VRF_REVERSE_DIRECTION); front->ConsistChanged(CCF_ARRANGE); - SetWindowDirty(WC_VEHICLE_DEPOT, front->tile); + if (IsRailDepotTile(front->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile)); SetWindowDirty(WC_VEHICLE_DETAILS, front->index); SetWindowDirty(WC_VEHICLE_VIEW, front->index); SetWindowClassesDirty(WC_TRAINS_LIST); @@ -2273,7 +2274,7 @@ static bool CheckTrainStayInDepot(Train *v) /* if the train got no power, then keep it in the depot */ if (v->gcache.cached_power == 0) { v->vehstatus |= VS_STOPPED; - SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); + SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); return true; } @@ -2334,7 +2335,7 @@ static bool CheckTrainStayInDepot(Train *v) v->UpdatePosition(); UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner); v->UpdateAcceleration(); - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); return false; } @@ -3628,7 +3629,7 @@ static void DeleteLastWagon(Train *v) /* Update the depot window if the first vehicle is in depot - * if v == first, then it is updated in PreDestructor() */ if (first->track == TRACK_BIT_DEPOT) { - SetWindowDirty(WC_VEHICLE_DEPOT, first->tile); + SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(first->tile)); } v->last_station_visited = first->last_station_visited; // for PreDestructor } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 8a7a471e8affd..d10f8dcb0f332 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -297,7 +297,7 @@ uint Vehicle::Crash(bool) InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0); SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP); SetWindowDirty(WC_VEHICLE_DETAILS, this->index); - SetWindowDirty(WC_VEHICLE_DEPOT, this->tile); + if (IsDepotTile(this->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile)); delete this->cargo_payment; assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment @@ -868,8 +868,8 @@ void Vehicle::PreDestructor() if (v->disaster_vehicle != INVALID_VEHICLE) ReleaseDisasterVehicle(v->disaster_vehicle); } - if (this->Previous() == nullptr) { - InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile); + if (this->Previous() == nullptr && IsDepotTile(this->tile)) { + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile)); } if (this->IsPrimaryVehicle()) { @@ -1554,6 +1554,7 @@ void VehicleEnterDepot(Vehicle *v) /* Always work with the front of the vehicle */ assert(v == v->First()); + DepotID depot_id = GetDepotIndex(v->tile); switch (v->type) { case VEH_TRAIN: { Train *t = Train::From(v); @@ -1580,7 +1581,7 @@ void VehicleEnterDepot(Vehicle *v) ship->state = TRACK_BIT_DEPOT; ship->UpdateCache(); ship->UpdateViewport(true, true); - SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); + SetWindowDirty(WC_VEHICLE_DEPOT, depot_id); break; } @@ -1595,9 +1596,9 @@ void VehicleEnterDepot(Vehicle *v) if (v->type != VEH_TRAIN) { /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters. * We only increase the number of vehicles when the first one enters, so we will not need to search for more vehicles in the depot */ - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); } - SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); + SetWindowDirty(WC_VEHICLE_DEPOT, depot_id); v->vehstatus |= VS_HIDDEN; v->cur_speed = 0; diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index 6d22d56b0abf6..ebe5ecc1f5c4b 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -37,6 +37,7 @@ #include "roadveh_cmd.h" #include "train_cmd.h" #include "ship_cmd.h" +#include "depot_base.h" #include #include @@ -178,7 +179,7 @@ std::tuple CmdBuildVehicle(D NormalizeTrainVehInDepot(Train::From(v)); } - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); InvalidateWindowClassesData(GetWindowClassForVehicleType(type), 0); SetWindowDirty(WC_COMPANY, _current_company); if (IsLocalCompany()) { @@ -553,7 +554,7 @@ std::tuple CmdRefitVehicle(DoCommandFla InvalidateWindowData(WC_VEHICLE_DETAILS, front->index); InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); } - SetWindowDirty(WC_VEHICLE_DEPOT, front->tile); + if (IsDepotTile(front->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile)); } else { /* Always invalidate the cache; querycost might have filled it. */ v->InvalidateNewGRFCacheOfChain(); @@ -639,7 +640,7 @@ CommandCost CmdStartStopVehicle(DoCommandFlag flags, VehicleID veh_id, bool eval v->MarkDirty(); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); - SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); + if (IsDepotTile(v->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); SetWindowClassesDirty(GetWindowClassForVehicleType(v->type)); InvalidateWindowData(WC_VEHICLE_VIEW, v->index); } diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index c4b01c05edff7..37c7f08fd380c 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -1334,7 +1334,7 @@ static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, static bool ClickTile_Water(TileIndex tile) { if (GetWaterTileType(tile) == WATER_TILE_DEPOT) { - ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP); + ShowDepotWindow(GetDepotIndex(tile)); return true; } return false; diff --git a/src/window_type.h b/src/window_type.h index 0896d5ff6fc1e..6161aeee78bc8 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -346,7 +346,7 @@ enum WindowClass { /** * Depot view; %Window numbers: - * - #TileIndex = #DepotWidgets + * - #DepotID = #DepotWidgets */ WC_VEHICLE_DEPOT, From 60024e2d2187bf62cbf9817894dc0254ba67dfe9 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 17 Jul 2024 00:01:55 +0200 Subject: [PATCH 004/143] Codechange: Set DepotID related window numbers to WC_BUILD_VEHICLE windows. --- src/build_vehicle_gui.cpp | 67 +++++++++++++++++++++++++-------------- src/depot_base.h | 10 +++++- src/depot_gui.cpp | 4 +-- src/group_gui.cpp | 2 +- src/vehicle_gui.cpp | 13 ++------ src/vehicle_gui.h | 5 +-- src/window_type.h | 4 +-- 7 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 1a985eb64a251..d0e1ba31c94a3 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -38,6 +38,7 @@ #include "querystring_gui.h" #include "stringfilter_type.h" #include "hotkeys.h" +#include "depot_base.h" #include "widgets/build_vehicle_widget.h" @@ -1163,6 +1164,25 @@ enum BuildVehicleHotkeys { BVHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string }; +/** + * Return a unique window number for the BuildVehicleWindow. + * The BuildVehicleWindow can be opened for a valid depot or + * for a specific vehicle type ("Available Trains", "Available Ships"...). + * The corresponding unique window number is chosen as: + * - For existing depots, the depot id. + * - For vehicle types, it is MAX_DEPOTS + vehicle_type + * @param depot_id the depot id + * @param type the vehicle type + * @return the depot id for valid depots or MAX_DEPOTS + vehicle_type otherwise. + */ +DepotID GetBuildVehicleWindowNumber(DepotID depot_id, VehicleType type) +{ + assert(depot_id == INVALID_DEPOT || Depot::IsValidID(depot_id)); + assert(IsCompanyBuildableVehicleType(type)); + if (depot_id != INVALID_DEPOT) return depot_id; + return MAX_DEPOTS + type; +} + /** GUI for building vehicles. */ struct BuildVehicleWindow : Window { VehicleType vehicle_type; ///< Type of vehicles shown in the window. @@ -1201,11 +1221,11 @@ struct BuildVehicleWindow : Window { } } - BuildVehicleWindow(WindowDesc &desc, TileIndex tile, VehicleType type) : Window(desc), vehicle_editbox(MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS) + BuildVehicleWindow(WindowDesc &desc, DepotID depot_id, VehicleType type) : Window(desc), vehicle_editbox(MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS) { this->vehicle_type = type; - this->listview_mode = tile == INVALID_TILE; - this->window_number = this->listview_mode ? (int)type : tile.base(); + this->listview_mode = depot_id == INVALID_DEPOT; + this->window_number = GetBuildVehicleWindowNumber(depot_id, type); this->sel_engine = INVALID_ENGINE; @@ -1240,16 +1260,13 @@ struct BuildVehicleWindow : Window { this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9); - if (tile == INVALID_TILE) { - this->FinishInitNested(type); - } else { - this->FinishInitNested(tile); - } + this->FinishInitNested(this->window_number); this->querystrings[WID_BV_FILTER] = &this->vehicle_editbox; this->vehicle_editbox.cancel_button = QueryString::ACTION_CLEAR; - this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company; + Depot *depot = Depot::GetIfValid(depot_id); + this->owner = (depot != nullptr) ? GetTileOwner(depot->xy) : _local_company; this->eng_list.ForceRebuild(); this->GenerateBuildList(); // generate the list, since we need it in the next line @@ -1264,13 +1281,16 @@ struct BuildVehicleWindow : Window { /** Set the filter type according to the depot type */ void UpdateFilterByTile() { + TileIndex tile = INVALID_TILE; + if (!this->listview_mode) tile = Depot::Get(this->window_number)->xy; + switch (this->vehicle_type) { default: NOT_REACHED(); case VEH_TRAIN: if (this->listview_mode) { this->filter.railtype = INVALID_RAILTYPE; } else { - this->filter.railtype = GetRailType(this->window_number); + this->filter.railtype = GetRailType(tile); } break; @@ -1278,9 +1298,9 @@ struct BuildVehicleWindow : Window { if (this->listview_mode) { this->filter.roadtype = INVALID_ROADTYPE; } else { - this->filter.roadtype = GetRoadTypeRoad(this->window_number); + this->filter.roadtype = GetRoadTypeRoad(tile); if (this->filter.roadtype == INVALID_ROADTYPE) { - this->filter.roadtype = GetRoadTypeTram(this->window_number); + this->filter.roadtype = GetRoadTypeTram(tile); } } break; @@ -1326,7 +1346,7 @@ struct BuildVehicleWindow : Window { if (!this->listview_mode) { /* Query for cost and refitted capacity */ - auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command::Do(DC_QUERY_COST, this->window_number, this->sel_engine, true, cargo, INVALID_CLIENT_ID); + auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command::Do(DC_QUERY_COST, Depot::Get(this->window_number)->xy, this->sel_engine, true, cargo, INVALID_CLIENT_ID); if (ret.Succeeded()) { this->te.cost = ret.GetCost() - e->GetCost(); this->te.capacity = refit_capacity; @@ -1613,10 +1633,15 @@ struct BuildVehicleWindow : Window { CargoID cargo = this->cargo_filter_criteria; if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO; + + assert(Depot::IsValidID(this->window_number)); + Depot *depot = Depot::Get(this->window_number); + assert(depot->xy != INVALID_TILE); + if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) { - Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); + Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, depot->xy, sel_eng, true, cargo, INVALID_CLIENT_ID); } else { - Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); + Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, depot->xy, sel_eng, true, cargo, INVALID_CLIENT_ID); } /* Update last used variant in hierarchy and refresh if necessary. */ @@ -1930,17 +1955,11 @@ static WindowDesc _build_vehicle_desc( &BuildVehicleWindow::hotkeys ); -void ShowBuildVehicleWindow(TileIndex tile, VehicleType type) +void ShowBuildVehicleWindow(DepotID depot_id, VehicleType type) { - /* We want to be able to open both Available Train as Available Ships, - * so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number. - * As it always is a low value, it won't collide with any real tile - * number. */ - uint num = (tile == INVALID_TILE) ? (int)type : tile.base(); - assert(IsCompanyBuildableVehicleType(type)); - CloseWindowById(WC_BUILD_VEHICLE, num); + CloseWindowById(WC_BUILD_VEHICLE, GetBuildVehicleWindowNumber(depot_id, type)); - new BuildVehicleWindow(_build_vehicle_desc, tile, type); + new BuildVehicleWindow(_build_vehicle_desc, depot_id, type); } diff --git a/src/depot_base.h b/src/depot_base.h index 1d8330fc74035..a5be19f3a4910 100644 --- a/src/depot_base.h +++ b/src/depot_base.h @@ -14,7 +14,15 @@ #include "core/pool_type.hpp" #include "timer/timer_game_calendar.h" -typedef Pool DepotPool; +static const DepotID MAX_DEPOTS = 64000; +/** + * For build_vehicle_window, each vehicle type needs its own unique value. + * So we need some special indexes: MAX_DEPOTS + VEH_TYPE_XXX. + * @see GetBuildVehicleWindowNumber + */ +static_assert(MAX_DEPOTS + VEH_COMPANY_END - 1 <= INVALID_DEPOT); + +typedef Pool DepotPool; extern DepotPool _depot_pool; struct Depot : DepotPool::PoolItem<&_depot_pool> { diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 750beb96ebe7c..4f400de33bb5b 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -769,7 +769,7 @@ struct DepotWindow : Window { case WID_D_BUILD: // Build vehicle ResetObjectToPlace(); - ShowBuildVehicleWindow(tile, this->type); + ShowBuildVehicleWindow(this->window_number, this->type); break; case WID_D_CLONE: // Clone button @@ -824,7 +824,7 @@ struct DepotWindow : Window { break; case WID_D_VEHICLE_LIST: - ShowVehicleListWindow(this->owner, this->type, tile); + ShowVehicleListWindowDepot(this->owner, this->type, this->window_number); break; case WID_D_AUTOREPLACE: diff --git a/src/group_gui.cpp b/src/group_gui.cpp index bb2a8d840d410..ff3244c3a8972 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -842,7 +842,7 @@ class VehicleGroupWindow : public BaseVehicleListWindow { break; case WID_GL_AVAILABLE_VEHICLES: - ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype); + ShowBuildVehicleWindow(INVALID_DEPOT, this->vli.vtype); break; case WID_GL_MANAGE_VEHICLES_DROPDOWN: { diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index aaaf10f9935d2..bb277bab34de0 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -2123,7 +2123,7 @@ struct VehicleListWindow : public BaseVehicleListWindow { } case WID_VL_AVAILABLE_VEHICLES: - ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype); + ShowBuildVehicleWindow(INVALID_DEPOT, this->vli.vtype); break; case WID_VL_MANAGE_VEHICLES_DROPDOWN: { @@ -2268,16 +2268,9 @@ void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationI ShowVehicleListWindowLocal(company, VL_STATION_LIST, vehicle_type, station); } -void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile) +void ShowVehicleListWindowDepot(CompanyID company, VehicleType vehicle_type, DepotID depot_id) { - uint16_t depot_airport_index; - - if (vehicle_type == VEH_AIRCRAFT) { - depot_airport_index = GetStationIndex(depot_tile); - } else { - depot_airport_index = GetDepotIndex(depot_tile); - } - ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, depot_airport_index); + ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, depot_id); } diff --git a/src/vehicle_gui.h b/src/vehicle_gui.h index ebd0d3fde85af..cf5d8a381dc92 100644 --- a/src/vehicle_gui.h +++ b/src/vehicle_gui.h @@ -18,6 +18,7 @@ #include "station_type.h" #include "engine_type.h" #include "company_type.h" +#include "depot_type.h" void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit = false); @@ -56,7 +57,7 @@ void DrawRoadVehImage(const Vehicle *v, const Rect &r, VehicleID selection, Engi void DrawShipImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type); void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type); -void ShowBuildVehicleWindow(TileIndex tile, VehicleType type); +void ShowBuildVehicleWindow(DepotID depot_id, VehicleType type); uint ShowRefitOptionsList(int left, int right, int y, EngineID engine); StringID GetCargoSubtypeText(const Vehicle *v); @@ -64,7 +65,7 @@ StringID GetCargoSubtypeText(const Vehicle *v); void ShowVehicleListWindow(const Vehicle *v); void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type); void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station); -void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile); +void ShowVehicleListWindowDepot(CompanyID company, VehicleType vehicle_type, DepotID depot_id); /** * Get the height of a single vehicle in the GUIs. diff --git a/src/window_type.h b/src/window_type.h index 6161aeee78bc8..4437d26b3f408 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -383,8 +383,8 @@ enum WindowClass { /** * Build vehicle; %Window numbers: - * - #VehicleType = #BuildVehicleWidgets - * - #TileIndex = #BuildVehicleWidgets + * - #DepotID = #BuildVehicleWidgets, for existing depots + * - #MAX_DEPOTS + VehicleType = #BuildVehicleWidgets, for "Available Trains"... */ WC_BUILD_VEHICLE, From 742c78c4042933afc3b08c0b17b0686d98607662 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 25 Aug 2023 15:02:16 +0200 Subject: [PATCH 005/143] Codechange: OrderBackups are indexed through DepotID instead of TileIndex. --- src/depot.cpp | 2 +- src/order_backup.cpp | 37 +++++++++++++++++++++---------------- src/order_backup.h | 9 +++++---- src/order_cmd.h | 2 +- src/saveload/order_sl.cpp | 8 +++++++- src/saveload/saveload.h | 1 + src/station_cmd.cpp | 4 ---- 7 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/depot.cpp b/src/depot.cpp index 38330a873993a..49297f91fa776 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -35,7 +35,7 @@ Depot::~Depot() } /* Clear the order backup. */ - OrderBackup::Reset(this->xy, false); + OrderBackup::Reset(this->index, false); /* Clear the depot from all order-lists */ RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->index); diff --git a/src/order_backup.cpp b/src/order_backup.cpp index 9843bbf36f138..053c407c8c7aa 100644 --- a/src/order_backup.cpp +++ b/src/order_backup.cpp @@ -19,6 +19,8 @@ #include "order_cmd.h" #include "group_cmd.h" #include "vehicle_func.h" +#include "depot_map.h" +#include "depot_base.h" #include "safeguards.h" @@ -45,8 +47,9 @@ OrderBackup::~OrderBackup() */ OrderBackup::OrderBackup(const Vehicle *v, uint32_t user) { + assert(IsDepotTile(v->tile)); this->user = user; - this->tile = v->tile; + this->depot_id = GetDepotIndex(v->tile); this->group = v->group_id; this->CopyConsistPropertiesFrom(v); @@ -123,8 +126,10 @@ void OrderBackup::DoRestore(Vehicle *v) */ /* static */ void OrderBackup::Restore(Vehicle *v, uint32_t user) { + assert(IsDepotTile(v->tile)); + DepotID depot_id_veh = GetDepotIndex(v->tile); for (OrderBackup *ob : OrderBackup::Iterate()) { - if (v->tile != ob->tile || ob->user != user) continue; + if (depot_id_veh != ob->depot_id || ob->user != user) continue; ob->DoRestore(v); delete ob; @@ -133,28 +138,28 @@ void OrderBackup::DoRestore(Vehicle *v) /** * Reset an OrderBackup given a tile and user. - * @param tile The tile associated with the OrderBackup. + * @param depot_id The depot associated with the OrderBackup. * @param user The user associated with the OrderBackup. * @note Must not be used from the GUI! */ -/* static */ void OrderBackup::ResetOfUser(TileIndex tile, uint32_t user) +/* static */ void OrderBackup::ResetOfUser(DepotID depot_id, uint32_t user) { for (OrderBackup *ob : OrderBackup::Iterate()) { - if (ob->user == user && (ob->tile == tile || tile == INVALID_TILE)) delete ob; + if (ob->user == user && (ob->depot_id == depot_id || depot_id == INVALID_DEPOT)) delete ob; } } /** * Clear an OrderBackup * @param flags For command. - * @param tile Tile related to the to-be-cleared OrderBackup. + * @param depot_id Tile related to the to-be-cleared OrderBackup. * @param user_id User that had the OrderBackup. * @return The cost of this operation or an error. */ -CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id) +CommandCost CmdClearOrderBackup(DoCommandFlag flags, DepotID depot_id, ClientID user_id) { - /* No need to check anything. If the tile or user don't exist we just ignore it. */ - if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, user_id); + assert(Depot::IsValidID(depot_id)); + if (flags & DC_EXEC) OrderBackup::ResetOfUser(depot_id, user_id); return CommandCost(); } @@ -173,18 +178,18 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID us /* If it's not a backup of us, ignore it. */ if (ob->user != user) continue; - Command::Post(0, static_cast(user)); + Command::Post(ob->depot_id, static_cast(user)); return; } } /** * Reset the OrderBackups from GUI/game logic. - * @param t The tile of the order backup. + * @param depot_id The index of the depot associated to the order backups. * @param from_gui Whether the call came from the GUI, i.e. whether * it must be synced over the network. */ -/* static */ void OrderBackup::Reset(TileIndex t, bool from_gui) +/* static */ void OrderBackup::Reset(DepotID depot_id, bool from_gui) { /* The user has CLIENT_ID_SERVER as default when network play is not active, * but compiled it. A network client has its own variable for the unique @@ -195,16 +200,16 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID us for (OrderBackup *ob : OrderBackup::Iterate()) { /* If this is a GUI action, and it's not a backup of us, ignore it. */ if (from_gui && ob->user != user) continue; - /* If it's not for our chosen tile either, ignore it. */ - if (t != INVALID_TILE && t != ob->tile) continue; + /* If it's not for our chosen depot either, ignore it. */ + if (depot_id != INVALID_DEPOT && depot_id != ob->depot_id) continue; if (from_gui) { /* We need to circumvent the "prevention" from this command being executed * while the game is paused, so use the internal method. Nor do we want * this command to get its cost estimated when shift is pressed. */ - Command::Unsafe(STR_NULL, nullptr, true, false, ob->tile, CommandTraits::Args{ ob->tile, static_cast(user) }); + Command::Unsafe(STR_NULL, nullptr, true, false, ob->depot_id, CommandTraits::Args{ ob->depot_id, static_cast(user) }); } else { - /* The command came from the game logic, i.e. the clearing of a tile. + /* The command came from the game logic, i.e. the clearing of a depot. * In that case we have no need to actually sync this, just do it. */ delete ob; } diff --git a/src/order_backup.h b/src/order_backup.h index 23832d59afe74..823e74b2f397f 100644 --- a/src/order_backup.h +++ b/src/order_backup.h @@ -16,6 +16,7 @@ #include "vehicle_type.h" #include "base_consist.h" #include "saveload/saveload.h" +#include "depot_type.h" /** Unique identifier for an order backup. */ typedef uint8_t OrderBackupID; @@ -34,8 +35,8 @@ struct OrderBackup : OrderBackupPool::PoolItem<&_order_backup_pool>, BaseConsist private: friend SaveLoadTable GetOrderBackupDescription(); ///< Saving and loading of order backups. friend struct BKORChunkHandler; ///< Creating empty orders upon savegame loading. - uint32_t user; ///< The user that requested the backup. - TileIndex tile; ///< Tile of the depot where the order was changed. + uint32_t user; ///< The user that requested the backup. + DepotID depot_id; ///< Depot where the order was changed. GroupID group; ///< The group the vehicle was part of. const Vehicle *clone; ///< Vehicle this vehicle was a clone of. @@ -53,9 +54,9 @@ struct OrderBackup : OrderBackupPool::PoolItem<&_order_backup_pool>, BaseConsist static void Backup(const Vehicle *v, uint32_t user); static void Restore(Vehicle *v, uint32_t user); - static void ResetOfUser(TileIndex tile, uint32_t user); + static void ResetOfUser(DepotID depot_id, uint32_t user); static void ResetUser(uint32_t user); - static void Reset(TileIndex tile = INVALID_TILE, bool from_gui = true); + static void Reset(DepotID depot = INVALID_DEPOT, bool from_gui = true); static void ClearGroup(GroupID group); static void ClearVehicle(const Vehicle *v); diff --git a/src/order_cmd.h b/src/order_cmd.h index b0ab888d9421a..d59ca58adf857 100644 --- a/src/order_cmd.h +++ b/src/order_cmd.h @@ -21,7 +21,7 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se CommandCost CmdOrderRefit(DoCommandFlag flags, VehicleID veh, VehicleOrderID order_number, CargoID cargo); CommandCost CmdCloneOrder(DoCommandFlag flags, CloneOptions action, VehicleID veh_dst, VehicleID veh_src); CommandCost CmdMoveOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID moving_order, VehicleOrderID target_order); -CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id); +CommandCost CmdClearOrderBackup(DoCommandFlag flags, DepotID depot_id, ClientID user_id); DEF_CMD_TRAIT(CMD_MODIFY_ORDER, CmdModifyOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) DEF_CMD_TRAIT(CMD_SKIP_TO_ORDER, CmdSkipToOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index 0ce1bd206e452..fe7e6d22d37f0 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -16,6 +16,7 @@ #include "../order_backup.h" #include "../settings_type.h" #include "../network/network.h" +#include "../depot_map.h" #include "../safeguards.h" @@ -243,11 +244,14 @@ struct ORDLChunkHandler : ChunkHandler { } }; +static TileIndex _tile; + SaveLoadTable GetOrderBackupDescription() { static const SaveLoad _order_backup_desc[] = { SLE_VAR(OrderBackup, user, SLE_UINT32), - SLE_VAR(OrderBackup, tile, SLE_UINT32), + SLEG_CONDVAR("tile", _tile, SLE_UINT32, SL_MIN_VERSION, SLV_DEPOTID_BACKUP_ORDERS), + SLE_CONDVAR(OrderBackup, depot_id, SLE_UINT16, SLV_DEPOTID_BACKUP_ORDERS, SL_MAX_VERSION), SLE_VAR(OrderBackup, group, SLE_UINT16), SLE_CONDVAR(OrderBackup, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SL_MIN_VERSION, SLV_192), SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, SLV_192, SL_MAX_VERSION), @@ -297,6 +301,8 @@ struct BKORChunkHandler : ChunkHandler { OrderBackup *ob = new (index) OrderBackup(); SlObject(ob, slt); } + + if (IsSavegameVersionBefore(SLV_DEPOTID_BACKUP_ORDERS)) _order_backup_pool.CleanPool(); } void FixPointers() const override diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 801be47716fe2..8f549c0ad4c5c 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -386,6 +386,7 @@ enum SaveLoadVersion : uint16_t { 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. + SLV_DEPOTID_BACKUP_ORDERS, ///< 341 PR#XXXXX Backup orders are indexed through DepotIDs. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 9bbaf5f4ac66a..a0e8f8c80514f 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2679,10 +2679,6 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) } if (flags & DC_EXEC) { - for (uint i = 0; i < st->airport.GetNumHangars(); ++i) { - TileIndex tile_cur = st->airport.GetHangarTile(i); - OrderBackup::Reset(tile_cur, false); - } st->airport.RemoveHangar(); /* The noise level is the noise from the airport and reduce it to account for the distance to the town center. From 0a385dc01015059df9425241bda6f193130a01de Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 17 Jul 2024 00:19:41 +0200 Subject: [PATCH 006/143] Prepare: Add some members to depot struct. --- src/build_vehicle_gui.cpp | 48 +++++++------ src/depot.cpp | 100 ++++++++++++++++++++++++++-- src/depot_base.h | 34 +++++++++- src/depot_cmd.cpp | 5 +- src/depot_gui.cpp | 13 ++-- src/economy.cpp | 7 ++ src/order_cmd.cpp | 23 +------ src/rail.h | 13 ++++ src/rail_cmd.cpp | 2 +- src/road.h | 13 ++++ src/road_cmd.cpp | 2 +- src/saveload/afterload.cpp | 34 ++++++++++ src/saveload/depot_sl.cpp | 6 ++ src/saveload/oldloader_sl.cpp | 2 + src/saveload/saveload.h | 1 + src/script/api/script_depotlist.cpp | 31 ++------- src/station.cpp | 10 ++- src/water_cmd.cpp | 2 +- 18 files changed, 259 insertions(+), 87 deletions(-) diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index d0e1ba31c94a3..3d8ed88f52ce6 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -1187,8 +1187,8 @@ DepotID GetBuildVehicleWindowNumber(DepotID depot_id, VehicleType type) struct BuildVehicleWindow : Window { VehicleType vehicle_type; ///< Type of vehicles shown in the window. union { - RailType railtype; ///< Rail type to show, or #INVALID_RAILTYPE. - RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE. + RailTypes railtypes; ///< Rail types to show, or #INVALID_RAILTYPES. + RoadTypes roadtypes; ///< Road types to show, or #INVALID_ROADTYPES. } filter; ///< Filter to apply. bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction uint8_t sort_criteria; ///< Current sort criterium. @@ -1266,7 +1266,7 @@ struct BuildVehicleWindow : Window { this->vehicle_editbox.cancel_button = QueryString::ACTION_CLEAR; Depot *depot = Depot::GetIfValid(depot_id); - this->owner = (depot != nullptr) ? GetTileOwner(depot->xy) : _local_company; + this->owner = depot != nullptr ? depot->owner : _local_company; this->eng_list.ForceRebuild(); this->GenerateBuildList(); // generate the list, since we need it in the next line @@ -1281,28 +1281,16 @@ struct BuildVehicleWindow : Window { /** Set the filter type according to the depot type */ void UpdateFilterByTile() { - TileIndex tile = INVALID_TILE; - if (!this->listview_mode) tile = Depot::Get(this->window_number)->xy; + Depot *depot = this->listview_mode ? nullptr : Depot::Get(this->window_number); switch (this->vehicle_type) { default: NOT_REACHED(); case VEH_TRAIN: - if (this->listview_mode) { - this->filter.railtype = INVALID_RAILTYPE; - } else { - this->filter.railtype = GetRailType(tile); - } + this->filter.railtypes = this->listview_mode ? INVALID_RAILTYPES : depot->r_types.rail_types; break; case VEH_ROAD: - if (this->listview_mode) { - this->filter.roadtype = INVALID_ROADTYPE; - } else { - this->filter.roadtype = GetRoadTypeRoad(tile); - if (this->filter.roadtype == INVALID_ROADTYPE) { - this->filter.roadtype = GetRoadTypeTram(tile); - } - } + this->filter.roadtypes = this->listview_mode ? INVALID_ROADTYPES : depot->r_types.road_types; break; case VEH_SHIP: @@ -1421,7 +1409,7 @@ struct BuildVehicleWindow : Window { EngineID eid = e->index; const RailVehicleInfo *rvi = &e->u.rail; - if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue; + if (!this->listview_mode && !HasPowerOnRails(rvi->railtype, this->filter.railtypes)) continue; if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue; /* Filter now! So num_engines and num_wagons is valid */ @@ -1481,7 +1469,7 @@ struct BuildVehicleWindow : Window { if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; EngineID eid = e->index; if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue; - if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue; + if (!this->listview_mode && !HasPowerOnRoads(e->u.road.roadtype, this->filter.roadtypes)) continue; /* Filter by name or NewGRF extra text */ if (!FilterByText(e)) continue; @@ -1521,7 +1509,7 @@ struct BuildVehicleWindow : Window { this->eng_list.clear(); - const Station *st = this->listview_mode ? nullptr : Station::GetByTile(this->window_number); + const Station *st = this->listview_mode ? nullptr : Depot::Get(this->window_number)->station; /* Make list of all available planes. * Also check to see if the previously selected plane is still available, @@ -1761,11 +1749,21 @@ struct BuildVehicleWindow : Window { switch (widget) { case WID_BV_CAPTION: if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) { - const RailTypeInfo *rti = GetRailTypeInfo(this->filter.railtype); - SetDParam(0, rti->strings.build_caption); + uint num_railtypes = CountBits(this->filter.railtypes); + if (num_railtypes != 1) { + SetDParam(0, STR_BUY_VEHICLE_TRAIN_ALL_CAPTION); + } else { + const RailTypeInfo *rti = GetRailTypeInfo((RailType)FindFirstBit(this->filter.railtypes)); + SetDParam(0, rti->strings.build_caption); + } } else if (this->vehicle_type == VEH_ROAD && !this->listview_mode) { - const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype); - SetDParam(0, rti->strings.build_caption); + uint num_roadtypes = CountBits(this->filter.roadtypes); + if (num_roadtypes != 1) { + SetDParam(0, STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION); + } else { + const RoadTypeInfo *rti = GetRoadTypeInfo((RoadType)FindFirstBit(this->filter.roadtypes)); + SetDParam(0, rti->strings.build_caption); + } } else { SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type); } diff --git a/src/depot.cpp b/src/depot.cpp index 49297f91fa776..bc5f62c8bda20 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -15,9 +15,12 @@ #include "core/pool_func.hpp" #include "vehicle_gui.h" #include "vehiclelist.h" +#include "command_func.h" #include "safeguards.h" +#include "table/strings.h" + /** All our depots tucked away in a pool. */ DepotPool _depot_pool("Depot"); INSTANTIATE_POOL_METHODS(Depot) @@ -29,8 +32,9 @@ Depot::~Depot() { if (CleaningPool()) return; - if (!IsDepotTile(this->xy) || GetDepotIndex(this->xy) != this->index) { - /* It can happen there is no depot here anymore (TTO/TTD savegames) */ + if (this->owner == INVALID_OWNER) { + /* Deleting depot remnants of TTD savegames while saveload conversion. */ + assert(this->veh_type == VEH_INVALID); return; } @@ -44,6 +48,94 @@ Depot::~Depot() CloseWindowById(WC_VEHICLE_DEPOT, this->index); /* Delete the depot list */ - VehicleType vt = GetDepotVehicleType(this->xy); - CloseWindowById(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_DEPOT_LIST, vt, GetTileOwner(this->xy), this->index).Pack()); + CloseWindowById(GetWindowClassForVehicleType(this->veh_type), + VehicleListIdentifier(VL_DEPOT_LIST, + this->veh_type, this->owner, this->index).Pack()); +} + +/** + * Check we can add some tiles to this depot. + * @param ta The affected tile area. + * @return Whether it is possible to add the tiles or an error message. + */ +CommandCost Depot::BeforeAddTiles(TileArea ta) +{ + assert(ta.tile != INVALID_TILE); + + if (this->ta.tile != INVALID_TILE) { + /* Important when the old rect is completely inside the new rect, resp. the old one was empty. */ + ta.Add(this->ta.tile); + ta.Add(TileAddXY(this->ta.tile, this->ta.w - 1, this->ta.h - 1)); + } + + return CommandCost(); +} + +/** + * Add some tiles to this depot and rescan area for depot_tiles. + * @param ta Affected tile area + * @param adding Whether adding or removing depot tiles. + */ +void Depot::AfterAddRemove(TileArea ta, bool adding) +{ + assert(ta.tile != INVALID_TILE); + + if (adding) { + if (this->ta.tile != INVALID_TILE) { + ta.Add(this->ta.tile); + ta.Add(TileAddXY(this->ta.tile, this->ta.w - 1, this->ta.h - 1)); + } + } else { + ta = this->ta; + } + + this->ta.Clear(); + + for (TileIndex tile : ta) { + if (!IsDepotTile(tile)) continue; + if (GetDepotIndex(tile) != this->index) continue; + this->ta.Add(tile); + } + + if (this->ta.tile != INVALID_TILE) { + this->RescanDepotTiles(); + assert(!this->depot_tiles.empty()); + this->xy = this->depot_tiles[0]; + } else { + delete this; + } +} + +/** + * Rescan depot_tiles. Done after AfterAddRemove and SaveLoad. + * Updates the tiles of the depot and its railtypes/roadtypes... + */ +void Depot::RescanDepotTiles() +{ + this->depot_tiles.clear(); + RailTypes old_rail_types = this->r_types.rail_types; + this->r_types.rail_types = RAILTYPES_NONE; + + for (TileIndex tile : this->ta) { + if (!IsDepotTile(tile)) continue; + if (GetDepotIndex(tile) != this->index) continue; + this->depot_tiles.push_back(tile); + switch (veh_type) { + case VEH_ROAD: + this->r_types.road_types |= GetPresentRoadTypes(tile); + break; + case VEH_TRAIN: + this->r_types.rail_types |= (RailTypes)(1 << GetRailType(tile)); + break; + case VEH_SHIP: + /* Mark this ship depot has at least one part, so ships can be built. */ + this->r_types.rail_types |= INVALID_RAILTYPES; + break; + default: break; + } + } + + if (old_rail_types != this->r_types.rail_types) { + InvalidateWindowData(WC_BUILD_VEHICLE, this->index, 0, true); + } } diff --git a/src/depot_base.h b/src/depot_base.h index a5be19f3a4910..9c8a732afd2b8 100644 --- a/src/depot_base.h +++ b/src/depot_base.h @@ -13,6 +13,8 @@ #include "depot_map.h" #include "core/pool_type.hpp" #include "timer/timer_game_calendar.h" +#include "rail_type.h" +#include "road_type.h" static const DepotID MAX_DEPOTS = 64000; /** @@ -25,6 +27,8 @@ static_assert(MAX_DEPOTS + VEH_COMPANY_END - 1 <= INVALID_DEPOT); typedef Pool DepotPool; extern DepotPool _depot_pool; +class CommandCost; + struct Depot : DepotPool::PoolItem<&_depot_pool> { /* DepotID index member of DepotPool is 2 bytes. */ uint16_t town_cn; ///< The N-1th depot for this town (consecutive number) @@ -33,11 +37,30 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> { std::string name; TimerGameCalendar::Date build_date; ///< Date of construction - Depot(TileIndex xy = INVALID_TILE) : xy(xy) {} + VehicleType veh_type; ///< Vehicle type of the depot. + Owner owner; ///< Owner of the depot. + Station *station; ///< For aircraft, station associated with this hangar. + + union { + RoadTypes road_types; + RailTypes rail_types; + } r_types; + + TileArea ta; + std::vector depot_tiles; + + Depot(TileIndex xy = INVALID_TILE, VehicleType type = VEH_INVALID, Owner owner = INVALID_OWNER, Station *station = nullptr) : + xy(xy), + veh_type(type), + owner(owner), + station(station), + ta(xy, 1, 1) {} + ~Depot(); static inline Depot *GetByTile(TileIndex tile) { + assert(Depot::IsValidID(GetDepotIndex(tile))); return Depot::Get(GetDepotIndex(tile)); } @@ -51,6 +74,15 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> { { return GetTileType(d->xy) == GetTileType(this->xy); } + + /* Check we can add some tiles to this depot. */ + CommandCost BeforeAddTiles(TileArea ta); + + /* Add some tiles to this depot and rescan area for depot_tiles. */ + void AfterAddRemove(TileArea ta, bool adding); + + /* Rescan depot_tiles. Done after AfterAddRemove and SaveLoad. */ + void RescanDepotTiles(); }; #endif /* DEPOT_BASE_H */ diff --git a/src/depot_cmd.cpp b/src/depot_cmd.cpp index 1e1700f6cbf32..5b29591616455 100644 --- a/src/depot_cmd.cpp +++ b/src/depot_cmd.cpp @@ -48,7 +48,7 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str Depot *d = Depot::GetIfValid(depot_id); if (d == nullptr) return CMD_ERROR; - CommandCost ret = CheckTileOwnership(d->xy); + CommandCost ret = CheckOwnership(d->owner); if (ret.Failed()) return ret; bool reset = text.empty(); @@ -71,8 +71,7 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str SetWindowDirty(WC_VEHICLE_DEPOT, d->index); /* Update the depot list */ - VehicleType vt = GetDepotVehicleType(d->xy); - SetWindowDirty(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_DEPOT_LIST, vt, GetTileOwner(d->xy), d->index).Pack()); + SetWindowDirty(GetWindowClassForVehicleType(d->veh_type), VehicleListIdentifier(VL_DEPOT_LIST, d->veh_type, d->owner, d->index).Pack()); } return CommandCost(); } diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 4f400de33bb5b..845d94ccdb83b 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -29,6 +29,7 @@ #include "zoom_func.h" #include "error.h" #include "depot_cmd.h" +#include "station_base.h" #include "train_cmd.h" #include "vehicle_cmd.h" #include "core/geometry_func.hpp" @@ -270,13 +271,13 @@ struct DepotWindow : Window { { assert(Depot::IsValidID(depot_id)); Depot *depot = Depot::Get(depot_id); - assert(IsCompanyBuildableVehicleType(GetDepotVehicleType(depot->xy))); + assert(IsCompanyBuildableVehicleType(depot->veh_type)); this->sel = INVALID_VEHICLE; this->vehicle_over = INVALID_VEHICLE; this->generate_list = true; this->hovered_widget = -1; - this->type = GetDepotVehicleType(depot->xy); + this->type = depot->veh_type; this->num_columns = 1; // for non-trains this gets set in FinishInitNested() this->unitnumber_digits = 2; @@ -292,7 +293,7 @@ struct DepotWindow : Window { this->SetupWidgetData(this->type); this->FinishInitNested(depot_id); - this->owner = GetTileOwner(depot->xy); + this->owner = depot->owner; OrderBackup::Reset(); } @@ -428,7 +429,7 @@ struct DepotWindow : Window { if (widget != WID_D_CAPTION) return; SetDParam(0, this->type); - SetDParam(1, (this->type == VEH_AIRCRAFT) ? GetStationIndex(Depot::Get(this->window_number)->xy) : this->window_number); + SetDParam(1, (this->type == VEH_AIRCRAFT) ? Depot::Get(this->window_number)->station->index : this->window_number); } struct GetDepotVehiclePtData { @@ -1121,7 +1122,7 @@ static void DepotSellAllConfirmationCallback(Window *win, bool confirmed) if (confirmed) { assert(Depot::IsValidID(win->window_number)); Depot *d = Depot::Get(win->window_number); - Command::Post(d->xy, GetDepotVehicleType(d->xy)); + Command::Post(d->xy, d->veh_type); } } @@ -1135,7 +1136,7 @@ void ShowDepotWindow(DepotID depot_id) if (BringWindowToFrontById(WC_VEHICLE_DEPOT, depot_id) != nullptr) return; Depot *d = Depot::Get(depot_id); - switch (GetDepotVehicleType(d->xy)) { + switch (d->veh_type) { default: NOT_REACHED(); case VEH_TRAIN: new DepotWindow(_train_depot_desc, depot_id); break; case VEH_ROAD: new DepotWindow(_road_depot_desc, depot_id); break; diff --git a/src/economy.cpp b/src/economy.cpp index cd990942399de..5c0d4f7bec7a2 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -55,6 +55,7 @@ #include "timer/timer.h" #include "timer/timer_game_calendar.h" #include "timer/timer_game_economy.h" +#include "depot_base.h" #include "table/strings.h" #include "table/pricebase.h" @@ -374,6 +375,12 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner) } if (new_owner == INVALID_OWNER) RebuildSubsidisedSourceAndDestinationCache(); + for (Depot *dep : Depot::Iterate()) { + if (dep->owner == old_owner && new_owner != INVALID_OWNER) { + dep->owner = new_owner; + } + } + /* Take care of rating and transport rights in towns */ for (Town *t : Town::Iterate()) { /* If a company takes over, give the ratings to that company. */ diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 070b0d84b1d84..9252e3d516e07 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -789,28 +789,11 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se if (dp == nullptr) return CMD_ERROR; - ret = CheckOwnership(GetTileOwner(dp->xy)); + ret = CheckOwnership(dp->owner); if (ret.Failed()) return ret; - 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; - } + if (v->type != dp->veh_type) return CMD_ERROR; + if (v->type == VEH_AIRCRAFT && !CanVehicleUseStation(v, Station::GetByTile(dp->xy))) return CMD_ERROR; } if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) return CMD_ERROR; diff --git a/src/rail.h b/src/rail.h index 149996aae0cdc..f15b9d67039ba 100644 --- a/src/rail.h +++ b/src/rail.h @@ -337,6 +337,19 @@ inline bool HasPowerOnRail(RailType enginetype, RailType tiletype) return HasBit(GetRailTypeInfo(enginetype)->powered_railtypes, tiletype); } +/** + * Checks if an engine with a given \a enginetype is powered on \a rail_types. + * This would normally just be an equality check, + * but for electric rails (which also support non-electric vehicles). + * @param enginetype The RailType of the engine we are considering. + * @param rail_types The RailTypes we are considering. + * @return Whether the engine got power on this tile. + */ +static inline bool HasPowerOnRails(RailType enginetype, RailTypes rail_types) +{ + return (GetRailTypeInfo(enginetype)->powered_railtypes & rail_types) != 0; +} + /** * Test if a RailType disallows build of level crossings. * @param rt The RailType to check. diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index a44d5d5521056..91dff791dbb21 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1010,7 +1010,7 @@ CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType rai if (rotate_existing_depot) { SetRailDepotExitDirection(tile, dir); } else { - Depot *d = new Depot(tile); + Depot *d = new Depot(tile, VEH_TRAIN, _current_company); d->build_date = TimerGameCalendar::date; MakeRailDepot(tile, _current_company, d->index, dir, railtype); diff --git a/src/road.h b/src/road.h index 44f4fcd198e2a..c2591d2f1d907 100644 --- a/src/road.h +++ b/src/road.h @@ -244,6 +244,19 @@ inline bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype) return HasBit(GetRoadTypeInfo(enginetype)->powered_roadtypes, tiletype); } +/** + * Checks if an engine with a given \a enginetype is powered on \a road_types. + * This would normally just be an equality check, + * but for electrified roads (which also support non-electric vehicles). + * @param enginetype The RoadType of the engine we are considering. + * @param rail_types The RoadTypes we are considering. + * @return Whether the engine got power on this tile. + */ +static inline bool HasPowerOnRoads(RoadType enginetype, RoadTypes road_types) +{ + return (GetRoadTypeInfo(enginetype)->powered_roadtypes & road_types) != 0; +} + /** * Returns the cost of building the specified roadtype. * @param roadtype The roadtype being built. diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index c673fc991a2a1..17ce071c410c1 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -1188,7 +1188,7 @@ CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, if (rotate_existing_depot) { SetRoadDepotExitDirection(tile, dir); } else { - Depot *dep = new Depot(tile); + Depot *dep = new Depot(tile, VEH_ROAD, _current_company); dep->build_date = TimerGameCalendar::date; MakeRoadDepot(tile, _current_company, dep->index, dir, rt); MakeDefaultName(dep); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 7f35427e0a1eb..b52bef2d83651 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -294,6 +294,10 @@ static void InitializeWindowsAndCaches() } } + for (Depot *dep : Depot::Iterate()) { + dep->RescanDepotTiles(); + } + RecomputePrices(); GroupStatistics::UpdateAfterLoad(); @@ -2814,6 +2818,36 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_ADD_MEMBERS_TO_DEPOT_STRUCT)) { + for (Depot *depot : Depot::Iterate()) { + if (!IsDepotTile(depot->xy) || GetDepotIndex(depot->xy) != depot->index) { + /* It can happen there is no depot here anymore (TTO/TTD savegames) */ + depot->veh_type = VEH_INVALID; + depot->owner = INVALID_OWNER; + delete depot; + continue; + } + + depot->owner = GetTileOwner(depot->xy); + depot->veh_type = GetDepotVehicleType(depot->xy); + switch (depot->veh_type) { + case VEH_SHIP: + depot->AfterAddRemove(TileArea(depot->xy, 2, 2), true); + break; + case VEH_ROAD: + case VEH_TRAIN: + depot->AfterAddRemove(TileArea(depot->xy, 1, 1), true); + break; + case VEH_AIRCRAFT: + assert(IsHangarTile(depot->xy)); + depot->station = Station::GetByTile(depot->xy); + break; + default: + break; + } + } + } + /* 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/depot_sl.cpp b/src/saveload/depot_sl.cpp index 2a7859211ce3e..1987605ea3487 100644 --- a/src/saveload/depot_sl.cpp +++ b/src/saveload/depot_sl.cpp @@ -27,6 +27,12 @@ static const SaveLoad _depot_desc[] = { SLE_CONDVAR(Depot, town_cn, SLE_UINT16, SLV_141, SL_MAX_VERSION), SLE_CONDSSTR(Depot, name, SLE_STR, SLV_141, SL_MAX_VERSION), SLE_CONDVAR(Depot, build_date, SLE_INT32, SLV_142, SL_MAX_VERSION), + SLE_CONDVAR(Depot, owner, SLE_UINT8, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION), + SLE_CONDVAR(Depot, veh_type, SLE_UINT8, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION), + SLE_CONDVAR(Depot, ta.tile, SLE_UINT32, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION), + 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), }; struct DEPTChunkHandler : ChunkHandler { diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index 538c1336c37dc..dc48d09399a0a 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -690,6 +690,8 @@ static bool LoadOldDepot(LoadgameState *ls, int num) if (d->xy != 0) { d->town = RemapTown(d->xy); } else { + d->owner = INVALID_OWNER; + d->veh_type = VEH_INVALID; delete d; } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 8f549c0ad4c5c..422104cadba44 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -387,6 +387,7 @@ enum SaveLoadVersion : uint16_t { SLV_DEPOTID_IN_HANGAR_ORDERS, ///< 340 PR#10691 Go to hangar orders store the DepotID instead of StationID. SLV_DEPOTID_BACKUP_ORDERS, ///< 341 PR#XXXXX Backup orders are indexed through DepotIDs. + SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, ///< 342 PR#XXXXX Add some members to depot struct. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/script/api/script_depotlist.cpp b/src/script/api/script_depotlist.cpp index eb43139165d1a..7997d399e782b 100644 --- a/src/script/api/script_depotlist.cpp +++ b/src/script/api/script_depotlist.cpp @@ -17,33 +17,16 @@ ScriptDepotList::ScriptDepotList(ScriptTile::TransportType transport_type) { EnforceDeityOrCompanyModeValid_Void(); - ::TileType tile_type; - switch (transport_type) { - default: return; + static_assert(VEH_TRAIN == (int)ScriptTile::TRANSPORT_RAIL); + static_assert(VEH_ROAD == (int)ScriptTile::TRANSPORT_ROAD); + static_assert(VEH_SHIP == (int)ScriptTile::TRANSPORT_WATER); - case ScriptTile::TRANSPORT_ROAD: tile_type = ::MP_ROAD; break; - case ScriptTile::TRANSPORT_RAIL: tile_type = ::MP_RAILWAY; break; - case ScriptTile::TRANSPORT_WATER: tile_type = ::MP_WATER; break; - - case ScriptTile::TRANSPORT_AIR: { - /* Hangars are not seen as real depots by the depot code. */ - bool is_deity = ScriptCompanyMode::IsDeity(); - CompanyID owner = ScriptObject::GetCompany(); - for (const Station *st : Station::Iterate()) { - if (is_deity || st->owner == owner) { - for (uint i = 0; i < st->airport.GetNumHangars(); i++) { - this->AddItem(st->airport.GetHangarTile(i).base()); - } - } - } - return; - } - } - - /* Handle 'standard' depots. */ bool is_deity = ScriptCompanyMode::IsDeity(); CompanyID owner = ScriptObject::GetCompany(); for (const Depot *depot : Depot::Iterate()) { - if ((is_deity || ::GetTileOwner(depot->xy) == owner) && ::IsTileType(depot->xy, tile_type)) this->AddItem(depot->xy.base()); + if (depot->veh_type != (VehicleType)transport_type || + (!is_deity && ::GetTileOwner(depot->xy) != owner)) continue; + + this->AddItem(depot->xy.base()); } } diff --git a/src/station.cpp b/src/station.cpp index 0ee8f2a60593a..0d4198ac6cb88 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -737,9 +737,17 @@ void Airport::AddHangar() assert(Depot::CanAllocateItem()); assert(this->GetNumHangars() > 0); Station *st = Station::GetByTile(this->GetHangarTile(0)); - this->hangar = new Depot(this->GetHangarTile(0)); + this->hangar = new Depot(this->GetHangarTile(0), VEH_AIRCRAFT, st->owner, st); this->hangar->build_date = st->build_date; this->hangar->town = st->town; + + this->hangar->ta.tile = st->airport.tile; + this->hangar->ta.w = st->airport.w; + this->hangar->ta.h = st->airport.h; + + for (uint i = 0; i < this->GetNumHangars(); i++) { + this->hangar->depot_tiles.push_back(this->GetHangarTile(i)); + } } /** diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 37c7f08fd380c..2e8535fd6123b 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -134,7 +134,7 @@ CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis) } if (flags & DC_EXEC) { - Depot *depot = new Depot(tile); + Depot *depot = new Depot(tile, VEH_SHIP, _current_company); depot->build_date = TimerGameCalendar::date; uint new_water_infra = 2 * LOCK_DEPOT_TILE_FACTOR; From d23b05a6dfdd19afabd6054bce2189582ccf4d2c Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 22 Apr 2023 13:01:37 +0200 Subject: [PATCH 007/143] Codechange: BuildDepotVehicleList through a DepotId instead of a TileIndex. --- src/depot_gui.cpp | 2 +- src/vehicle_cmd.cpp | 6 +++--- src/vehiclelist.cpp | 18 ++++++++++++------ src/vehiclelist.h | 3 ++- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 845d94ccdb83b..a33da664501d4 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -714,7 +714,7 @@ struct DepotWindow : Window { if (this->generate_list) { /* Generate the vehicle list * It's ok to use the wagon pointers for non-trains as they will be ignored */ - BuildDepotVehicleList(this->type, Depot::Get(this->window_number)->xy, &this->vehicle_list, &this->wagon_list); + BuildDepotVehicleList(this->type, this->window_number, &this->vehicle_list, &this->wagon_list); this->generate_list = false; DepotSortList(&this->vehicle_list); diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index ebe5ecc1f5c4b..16ec09955111c 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -668,7 +668,7 @@ CommandCost CmdMassStartStopVehicle(DoCommandFlag flags, TileIndex tile, bool do } else { if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR; /* Get the list of vehicles in the depot */ - BuildDepotVehicleList(vli.vtype, tile, &list, nullptr); + BuildDepotVehicleList(vli.vtype, GetDepotIndex(tile), &list, nullptr); } for (const Vehicle *v : list) { @@ -700,7 +700,7 @@ CommandCost CmdDepotSellAllVehicles(DoCommandFlag flags, TileIndex tile, Vehicle if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR; /* Get the list of vehicles in the depot */ - BuildDepotVehicleList(vehicle_type, tile, &list, &list); + BuildDepotVehicleList(vehicle_type, GetDepotIndex(tile), &list, &list); CommandCost last_error = CMD_ERROR; bool had_success = false; @@ -733,7 +733,7 @@ CommandCost CmdDepotMassAutoReplace(DoCommandFlag flags, TileIndex tile, Vehicle if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR; /* Get the list of vehicles in the depot */ - BuildDepotVehicleList(vehicle_type, tile, &list, &list, true); + BuildDepotVehicleList(vehicle_type, GetDepotIndex(tile), &list, &list, true); for (const Vehicle *v : list) { /* Ensure that the vehicle completely in the depot */ diff --git a/src/vehiclelist.cpp b/src/vehiclelist.cpp index 48214d93843b4..01bbc757df7f8 100644 --- a/src/vehiclelist.cpp +++ b/src/vehiclelist.cpp @@ -13,6 +13,7 @@ #include "vehiclelist.h" #include "vehiclelist_func.h" #include "group.h" +#include "depot_base.h" #include "safeguards.h" @@ -95,19 +96,24 @@ static Vehicle *BuildDepotVehicleListProc(Vehicle *v, void *data) /** * Generate a list of vehicles inside a depot. - * @param type Type of vehicle - * @param tile The tile the depot is located on - * @param engines Pointer to list to add vehicles to - * @param wagons Pointer to list to add wagons to (can be nullptr) + * @param type Type of vehicle + * @param depot_id The id of the depot + * @param engines Pointer to list to add vehicles to + * @param wagons Pointer to list to add wagons to (can be nullptr) * @param individual_wagons If true add every wagon to \a wagons which is not attached to an engine. If false only add the first wagon of every row. */ -void BuildDepotVehicleList(VehicleType type, TileIndex tile, VehicleList *engines, VehicleList *wagons, bool individual_wagons) +void BuildDepotVehicleList(VehicleType type, DepotID depot_id, VehicleList *engines, VehicleList *wagons, bool individual_wagons) { + assert(Depot::IsValidID(depot_id)); + Depot *dep = Depot::Get(depot_id); engines->clear(); if (wagons != nullptr && wagons != engines) wagons->clear(); BuildDepotVehicleListData bdvld{engines, wagons, type, individual_wagons}; - FindVehicleOnPos(tile, &bdvld, BuildDepotVehicleListProc); + + for (TileIndex tile : dep->depot_tiles) { + FindVehicleOnPos(tile, &bdvld, BuildDepotVehicleListProc); + } } /** diff --git a/src/vehiclelist.h b/src/vehiclelist.h index 037953f25db71..9bb3aa6dcfabf 100644 --- a/src/vehiclelist.h +++ b/src/vehiclelist.h @@ -12,6 +12,7 @@ #include "vehicle_type.h" #include "company_type.h" +#include "depot_type.h" #include "tile_type.h" /** Vehicle List type flags */ @@ -54,7 +55,7 @@ struct VehicleListIdentifier { typedef std::vector VehicleList; bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &identifier); -void BuildDepotVehicleList(VehicleType type, TileIndex tile, VehicleList *engine_list, VehicleList *wagon_list, bool individual_wagons = false); +void BuildDepotVehicleList(VehicleType type, DepotID depot_id, VehicleList *engine_list, VehicleList *wagon_list, bool individual_wagons = false); uint GetUnitNumberDigits(VehicleList &vehicles); #endif /* VEHICLELIST_H */ From 2dea592f3e758bbf759d3cb9833f44a3a0cb4d5c Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 17 Jul 2024 00:23:40 +0200 Subject: [PATCH 008/143] Add: Settings for controlling depot spread. # Conflicts: # src/saveload/saveload.h --- src/depot.cpp | 3 +++ src/depot_type.h | 2 ++ src/lang/english.txt | 7 +++++++ src/saveload/afterload.cpp | 5 +++++ src/saveload/saveload.h | 1 + src/settings_gui.cpp | 6 ++++++ src/settings_type.h | 7 +++++++ src/table/settings/game_settings.ini | 19 +++++++++++++++++++ 8 files changed, 50 insertions(+) diff --git a/src/depot.cpp b/src/depot.cpp index bc5f62c8bda20..dfcdc5c1fd663 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -68,6 +68,9 @@ CommandCost Depot::BeforeAddTiles(TileArea ta) ta.Add(TileAddXY(this->ta.tile, this->ta.w - 1, this->ta.h - 1)); } + if ((ta.w > _settings_game.depot.depot_spread) || (ta.h > _settings_game.depot.depot_spread)) { + return_cmd_error(STR_ERROR_DEPOT_TOO_SPREAD_OUT); + } return CommandCost(); } diff --git a/src/depot_type.h b/src/depot_type.h index 4e61c1bcbd5e7..4eaf93473ce54 100644 --- a/src/depot_type.h +++ b/src/depot_type.h @@ -17,4 +17,6 @@ static const DepotID INVALID_DEPOT = UINT16_MAX; static const uint MAX_LENGTH_DEPOT_NAME_CHARS = 32; ///< The maximum length of a depot name in characters including '\0' +static const uint DEF_MAX_DEPOT_SPREAD = 12; + #endif /* DEPOT_TYPE_H */ diff --git a/src/lang/english.txt b/src/lang/english.txt index 87a8d3c594425..fd806beb75c4f 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1615,6 +1615,11 @@ STR_CONFIG_SETTING_SE_FLAT_WORLD_HEIGHT :The height leve STR_CONFIG_SETTING_EDGES_NOT_EMPTY :{WHITE}One or more tiles at the northern edge are not empty STR_CONFIG_SETTING_EDGES_NOT_WATER :{WHITE}One or more tiles at one of the edges is not water +STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS :Allow to join depot parts not directly adjacent: {STRING2} +STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS_HELPTEXT :Allow adding parts to a depot without directly touching the existing parts. Needs Ctrl+Click while placing the new parts +STR_CONFIG_SETTING_DEPOT_SPREAD :Maximum depot spread: {STRING2} +STR_CONFIG_SETTING_DEPOT_SPREAD_HELPTEXT :Maximum area the parts of a single depot may be spread out on + STR_CONFIG_SETTING_STATION_SPREAD :Maximum station spread: {STRING2} STR_CONFIG_SETTING_STATION_SPREAD_HELPTEXT :Maximum area the parts of a single station may be spread out on. Note that high values will slow the game @@ -2137,6 +2142,7 @@ STR_CONFIG_SETTING_ENVIRONMENT_TREES :Trees STR_CONFIG_SETTING_AI :Competitors STR_CONFIG_SETTING_AI_NPC :Computer players STR_CONFIG_SETTING_NETWORK :Network +STR_CONFIG_SETTING_DEPOTS :Depots STR_CONFIG_SETTING_REVERSE_AT_SIGNALS :Automatic reversing at signals: {STRING2} STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Allow trains to reverse on a signal, if they waited there a long time @@ -5132,6 +5138,7 @@ STR_ERROR_REAR_ENGINE_FOLLOW_FRONT :{WHITE}The rear STR_ERROR_UNABLE_TO_FIND_ROUTE_TO :{WHITE}Unable to find route to local depot STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT :{WHITE}Unable to find local depot +STR_ERROR_DEPOT_TOO_SPREAD_OUT :{WHITE}... depot too spread out STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Wrong depot type # Depot unbunching related errors diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index b52bef2d83651..578e461b8f9f3 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -799,6 +799,11 @@ bool AfterLoadGame() _settings_game.linkgraph.recalc_time *= CalendarTime::SECONDS_PER_DAY; } + if (IsSavegameVersionBefore(SLV_DEPOT_SPREAD)) { + _settings_game.depot.depot_spread = DEF_MAX_DEPOT_SPREAD; + _settings_game.depot.distant_join_depots = true; + } + /* Load the sprites */ GfxLoadSprites(); LoadStringWidthTable(); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 422104cadba44..22cd1c980ad19 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -388,6 +388,7 @@ enum SaveLoadVersion : uint16_t { SLV_DEPOTID_IN_HANGAR_ORDERS, ///< 340 PR#10691 Go to hangar orders store the DepotID instead of StationID. SLV_DEPOTID_BACKUP_ORDERS, ///< 341 PR#XXXXX Backup orders are indexed through DepotIDs. SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, ///< 342 PR#XXXXX Add some members to depot struct. + SLV_DEPOT_SPREAD, ///< 343 PR#XXXXX Add a setting for max depot spread. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index fb5c08ec8668b..1b3fd4a8fe7e9 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2145,6 +2145,12 @@ static SettingsContainer &GetSettingsTree() SettingsPage *limitations = main->Add(new SettingsPage(STR_CONFIG_SETTING_LIMITATIONS)); { + SettingsPage *depots = limitations->Add(new SettingsPage(STR_CONFIG_SETTING_DEPOTS)); + { + depots->Add(new SettingEntry("depot.depot_spread")); + depots->Add(new SettingEntry("depot.distant_join_depots")); + } + limitations->Add(new SettingEntry("construction.command_pause_level")); limitations->Add(new SettingEntry("construction.autoslope")); limitations->Add(new SettingEntry("construction.extra_dynamite")); diff --git a/src/settings_type.h b/src/settings_type.h index ae6f22c42e950..6938578466837 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -570,6 +570,12 @@ struct StationSettings { uint8_t station_spread; ///< amount a station may spread }; +/** Settings related to depots. */ +struct DepotSettings { + uint8_t depot_spread; ///< amount a depot may spread + bool distant_join_depots; ///< allow to join non-adjacent depots +}; + /** Default settings for vehicles. */ struct VehicleDefaultSettings { bool servint_ispercent; ///< service intervals are in percents @@ -603,6 +609,7 @@ struct GameSettings { EconomySettings economy; ///< settings to change the economy LinkGraphSettings linkgraph; ///< settings for link graph calculations StationSettings station; ///< settings related to station management + DepotSettings depot; ///< settings related to depot management LocaleSettings locale; ///< settings related to used currency/unit system in the current game }; diff --git a/src/table/settings/game_settings.ini b/src/table/settings/game_settings.ini index 07adda5cc9d50..6426080857465 100644 --- a/src/table/settings/game_settings.ini +++ b/src/table/settings/game_settings.ini @@ -145,6 +145,25 @@ str = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS_HELPTEXT post_cb = [](auto) { CloseWindowById(WC_SELECT_STATION, 0); } +[SDT_BOOL] +var = depot.distant_join_depots +from = SLV_DEPOT_SPREAD +def = true +str = STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS +strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS_HELPTEXT + +[SDT_VAR] +var = depot.depot_spread +type = SLE_UINT8 +from = SLV_DEPOT_SPREAD +def = DEF_MAX_DEPOT_SPREAD +min = 1 +max = 64 +str = STR_CONFIG_SETTING_DEPOT_SPREAD +strhelp = STR_CONFIG_SETTING_DEPOT_SPREAD_HELPTEXT +strval = STR_CONFIG_SETTING_TILE_LENGTH +cat = SC_BASIC + [SDT_OMANY] var = vehicle.road_side type = SLE_UINT8 From c616c14198b6ba9ae19691c1ea9e037aacb85f63 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 25 Aug 2023 16:42:58 +0200 Subject: [PATCH 009/143] Feature: Add a window for joining depots. --- src/depot.cpp | 1 + src/depot_cmd.cpp | 66 ++++++++ src/depot_func.h | 8 + src/depot_gui.cpp | 241 +++++++++++++++++++++++++++ src/depot_type.h | 1 + src/dock_gui.cpp | 8 + src/lang/english.txt | 6 + src/rail_gui.cpp | 8 + src/road_gui.cpp | 8 + src/table/settings/game_settings.ini | 2 + src/widgets/depot_widget.h | 7 + src/window_type.h | 6 + 12 files changed, 362 insertions(+) diff --git a/src/depot.cpp b/src/depot.cpp index dfcdc5c1fd663..542abc5f6208f 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -104,6 +104,7 @@ void Depot::AfterAddRemove(TileArea ta, bool adding) this->RescanDepotTiles(); assert(!this->depot_tiles.empty()); this->xy = this->depot_tiles[0]; + InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type); } else { delete this; } diff --git a/src/depot_cmd.cpp b/src/depot_cmd.cpp index 5b29591616455..32cd01558b475 100644 --- a/src/depot_cmd.cpp +++ b/src/depot_cmd.cpp @@ -75,3 +75,69 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str } return CommandCost(); } + +/** + * Look for or check depot to join to, building a new one if necessary. + * @param ta The area of the new depot. + * @param veh_type The vehicle type of the new depot. + * @param join_to DepotID of the depot to join to. + * If INVALID_DEPOT, look whether it is possible to join to an existing depot. + * If NEW_DEPOT, directly create a new depot. + * @param depot The pointer to the depot. + * @param adjacent Whether adjacent depots are allowed + * @return command cost with the error or 'okay' + */ +CommandCost FindJoiningDepot(TileArea ta, VehicleType veh_type, DepotID &join_to, Depot *&depot, bool adjacent, DoCommandFlag flags) +{ + /* Look for a joining depot if needed. */ + if (join_to == INVALID_DEPOT) { + assert(depot == nullptr); + DepotID closest_depot = INVALID_DEPOT; + + TileArea check_area(ta); + check_area.Expand(1); + + /* Check around to see if there's any depot there. */ + for (TileIndex tile_cur : check_area) { + if (IsValidTile(tile_cur) && IsDepotTile(tile_cur)) { + Depot *d = Depot::GetByTile(tile_cur); + assert(d != nullptr); + if (d->veh_type != veh_type) continue; + if (d->owner != _current_company) continue; + + if (closest_depot == INVALID_DEPOT) { + closest_depot = d->index; + } else if (closest_depot != d->index) { + if (!adjacent) return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING_DEPOT); + } + } + } + + if (closest_depot != INVALID_DEPOT) { + assert(Depot::IsValidID(closest_depot)); + depot = Depot::Get(closest_depot); + } + + join_to = depot == nullptr ? NEW_DEPOT : depot->index; + } + + /* At this point, join_to is NEW_DEPOT or a valid DepotID. */ + + if (join_to == NEW_DEPOT) { + /* New depot needed. */ + if (!Depot::CanAllocateItem()) return CMD_ERROR; + if (flags & DC_EXEC) { + depot = new Depot(ta.tile, veh_type, _current_company); + depot->build_date = TimerGameCalendar::date; + } + } else { + /* Joining depots. */ + assert(Depot::IsValidID(join_to)); + depot = Depot::Get(join_to); + assert(depot->owner == _current_company); + assert(depot->veh_type == veh_type); + return depot->BeforeAddTiles(ta); + } + + return CommandCost(); +} diff --git a/src/depot_func.h b/src/depot_func.h index 7b3b6a9eeb0a2..d4d5ab69aa62f 100644 --- a/src/depot_func.h +++ b/src/depot_func.h @@ -10,8 +10,10 @@ #ifndef DEPOT_FUNC_H #define DEPOT_FUNC_H +#include "depot_type.h" #include "vehicle_type.h" #include "slope_func.h" +#include "command_type.h" void ShowDepotWindow(DepotID depot_id); void InitDepotWindowBlockSizes(); @@ -33,4 +35,10 @@ inline bool CanBuildDepotByTileh(DiagDirection direction, Slope tileh) return IsSteepSlope(tileh) ? (tileh & entrance_corners) == entrance_corners : (tileh & entrance_corners) != 0; } +struct Depot; +CommandCost FindJoiningDepot(TileArea ta, VehicleType veh_type, DepotID &join_to, Depot *&depot, bool adjacent, DoCommandFlag flags); + +using DepotPickerCmdProc = std::function; +void ShowSelectDepotIfNeeded(TileArea ta, DepotPickerCmdProc proc, VehicleType veh_type); + #endif /* DEPOT_FUNC_H */ diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index a33da664501d4..9055a39705a9f 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -33,6 +33,7 @@ #include "train_cmd.h" #include "vehicle_cmd.h" #include "core/geometry_func.hpp" +#include "depot_func.h" #include "widgets/depot_widget.h" @@ -1167,3 +1168,243 @@ void DeleteDepotHighlightOfVehicle(const Vehicle *v) if (w->sel == v->index) ResetObjectToPlace(); } } + +static std::vector _depots_nearby_list; + +/** Structure with user-data for AddNearbyDepot. */ +struct AddNearbyDepotData { + TileArea search_area; ///< Search area. + VehicleType type; ///< Vehicle type of the searched depots. +}; + +/** + * Add depot on this tile to _depots_nearby_list if it's fully within the + * depot spread. + * @param tile Tile just being checked + * @param user_data Pointer to TileArea context + */ +static bool AddNearbyDepot(TileIndex tile, void *user_data) +{ + AddNearbyDepotData *andd = (AddNearbyDepotData *)user_data; + + /* Check if own depot and if we stay within station spread */ + if (!IsDepotTile(tile)) return false; + Depot *dep = Depot::GetByTile(tile); + if (dep->owner != _local_company || dep->veh_type != andd->type || + (find(_depots_nearby_list.begin(), _depots_nearby_list.end(), dep->index) != _depots_nearby_list.end())) { + return false; + } + + CommandCost cost = dep->BeforeAddTiles(andd->search_area); + if (cost.Succeeded()) { + _depots_nearby_list.push_back(dep->index); + } + + return false; +} + +/** + * Circulate around the to-be-built depot to find depots we could join. + * Make sure that only depots are returned where joining wouldn't exceed + * depot spread and are our own depot. + * @param ta Base tile area of the to-be-built depot + * @param veh_type Vehicle type depots to look for + * @param distant_join Search for adjacent depots (false) or depots fully + * within depot spread + */ +static const Depot *FindDepotsNearby(TileArea ta, VehicleType veh_type, bool distant_join) +{ + _depots_nearby_list.clear(); + _depots_nearby_list.push_back(NEW_DEPOT); + + /* Check the inside, to return, if we sit on another big depot */ + Depot *depot; + for (TileIndex t : ta) { + if (!IsDepotTile(t)) continue; + depot = Depot::GetByTile(t); + if (depot->veh_type == veh_type && depot->owner == _current_company) return depot; + } + + /* Only search tiles where we have a chance to stay within the depot spread. + * The complete check needs to be done in the callback as we don't know the + * extent of the found depot, yet. */ + if (distant_join && std::min(ta.w, ta.h) >= _settings_game.depot.depot_spread) return nullptr; + uint max_dist = distant_join ? _settings_game.depot.depot_spread - std::min(ta.w, ta.h) : 1; + + AddNearbyDepotData andd; + andd.search_area = ta; + andd.type = veh_type; + + TileIndex tile = TileAddByDir(andd.search_area.tile, DIR_N); + CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyDepot, &andd); + + return nullptr; +} + +/** + * Check whether we need to show the depot selection window. + * @param ta Tile area of the to-be-built depot. + * @param proc The procedure for the depot picker. + * @param veh_type the vehicle type of the depot. + * @return whether we need to show the depot selection window. + */ +static bool DepotJoinerNeeded(TileArea ta, VehicleType veh_type) +{ + /* If a window is already opened and we didn't ctrl-click, + * return true (i.e. just flash the old window) */ + Window *selection_window = FindWindowById(WC_SELECT_DEPOT, veh_type); + if (selection_window != nullptr) { + /* Abort current distant-join and start new one */ + selection_window->Close(); + UpdateTileSelection(); + } + + /* Only show the popup if we press ctrl. */ + if (!_ctrl_pressed) return false; + + /* Test for adjacent depot or depot below selection. + * If adjacent-stations is disabled and we are building next to a depot, do not show the selection window. + * but join the other depot immediately. */ + return FindDepotsNearby(ta, veh_type, false) == nullptr; +} + +/** + * Window for selecting depots to (distant) join to. + */ +struct SelectDepotWindow : Window { + DepotPickerCmdProc select_depot_proc; ///< The procedure params + TileArea area; ///< Location of new depot + Scrollbar *vscroll; ///< Vertical scrollbar for the window + + SelectDepotWindow(WindowDesc &desc, TileArea ta, DepotPickerCmdProc& proc, VehicleType veh_type) : + Window(desc), + select_depot_proc(std::move(proc)), + area(ta) + { + this->CreateNestedTree(); + this->vscroll = this->GetScrollbar(WID_JD_SCROLLBAR); + this->FinishInitNested(veh_type); + this->OnInvalidateData(0); + } + + void UpdateWidgetSize(int widget, Dimension &size, const Dimension &padding, [[maybe_unused]] Dimension &fill, Dimension &resize) override + { + if (widget != WID_JD_PANEL) return; + + resize.height = GetCharacterHeight(FS_NORMAL); + size.height = 5 * resize.height + padding.height; + + /* Determine the widest string. */ + Dimension d = GetStringBoundingBox(STR_JOIN_DEPOT_CREATE_SPLITTED_DEPOT); + for (const auto &depot : _depots_nearby_list) { + if (depot == NEW_DEPOT) continue; + const Depot *dep = Depot::Get(depot); + SetDParam(0, this->window_number); + SetDParam(1, dep->index); + d = maxdim(d, GetStringBoundingBox(STR_DEPOT_LIST_DEPOT)); + } + + d.height = 5 * resize.height; + d.width += padding.width; + d.height += padding.height; + size = d; + } + + void DrawWidget(const Rect &r, int widget) const override + { + if (widget != WID_JD_PANEL) return; + + Rect tr = r.Shrink(WidgetDimensions::scaled.framerect); + + auto [first, last] = this->vscroll->GetVisibleRangeIterators(_depots_nearby_list); + for (auto it = first; it != last; ++it, tr.top += this->resize.step_height) { + if (*it == NEW_DEPOT) { + DrawString(tr, STR_JOIN_DEPOT_CREATE_SPLITTED_DEPOT); + } else { + SetDParam(0, this->window_number); + SetDParam(1, *it); + [[maybe_unused]] Depot *depot = Depot::GetIfValid(*it); + assert(depot != nullptr); + DrawString(tr, STR_DEPOT_LIST_DEPOT); + } + } + } + + void OnClick(Point pt, int widget, [[maybe_unused]] int click_count) override + { + if (widget != WID_JD_PANEL) return; + + auto it = this->vscroll->GetScrolledItemFromWidget(_depots_nearby_list, pt.y, this, WID_JD_PANEL, WidgetDimensions::scaled.framerect.top); + if (it == _depots_nearby_list.end()) return; + + /* Execute stored Command */ + this->select_depot_proc(*it); + + InvalidateWindowData(WC_SELECT_DEPOT, window_number); + this->Close(); + } + + void OnRealtimeTick([[maybe_unused]] uint delta_ms) override + { + if (_thd.dirty & 2) { + _thd.dirty &= ~2; + this->SetDirty(); + } + } + + void OnResize() override + { + this->vscroll->SetCapacityFromWidget(this, WID_JD_PANEL, WidgetDimensions::scaled.framerect.Vertical()); + } + + /** + * Some data on this window has become invalid. + * @param data Information about the changed data. + * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details. + */ + void OnInvalidateData([[maybe_unused]] int data = 0, bool gui_scope = true) override + { + if (!gui_scope) return; + FindDepotsNearby(this->area, (VehicleType)this->window_number, true); + this->vscroll->SetCount((uint)_depots_nearby_list.size()); + this->SetDirty(); + } +}; + +static const NWidgetPart _nested_select_depot_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_JD_CAPTION), SetDataTip(STR_JOIN_DEPOT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_JD_PANEL), SetResize(1, 0), SetScrollbar(WID_JD_SCROLLBAR), EndContainer(), + NWidget(NWID_VERTICAL), + NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_JD_SCROLLBAR), + NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _select_depot_desc( + WDP_AUTO, "build_depot_join", 200, 180, + WC_SELECT_DEPOT, WC_NONE, + WDF_CONSTRUCTION, + _nested_select_depot_widgets +); + +/** + * Show the depot selection window when needed. If not, build the depot. + * @param ta Area to build the depot in. + * @param proc Details of the procedure for the depot picker. + * @param veh_type Vehicle type of the depot to be built. + */ +void ShowSelectDepotIfNeeded(TileArea ta, DepotPickerCmdProc proc, VehicleType veh_type) +{ + if (DepotJoinerNeeded(ta, veh_type)) { + if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); + new SelectDepotWindow(_select_depot_desc, ta, proc, veh_type); + } else { + proc(INVALID_DEPOT); + } +} diff --git a/src/depot_type.h b/src/depot_type.h index 4eaf93473ce54..a03a33b4f8753 100644 --- a/src/depot_type.h +++ b/src/depot_type.h @@ -14,6 +14,7 @@ typedef uint16_t DepotID; ///< Type for the unique identifier of depots. struct Depot; static const DepotID INVALID_DEPOT = UINT16_MAX; +static const DepotID NEW_DEPOT = INVALID_DEPOT - 1; static const uint MAX_LENGTH_DEPOT_NAME_CHARS = 32; ///< The maximum length of a depot name in characters including '\0' diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index ffbf5aa63e3c2..ccf35c3e449a7 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -275,6 +275,7 @@ struct BuildDocksToolbarWindow : Window { CloseWindowById(WC_BUILD_STATION, TRANSPORT_WATER); CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_WATER); CloseWindowById(WC_SELECT_STATION, 0); + CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP); CloseWindowByClass(WC_BUILD_BRIDGE); } @@ -529,6 +530,12 @@ struct BuildDocksDepotWindow : public PickerWindowBase { UpdateDocksDirection(); } + void Close([[maybe_unused]] int data = 0) override + { + CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP); + this->PickerWindowBase::Close(); + } + void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { switch (widget) { @@ -569,6 +576,7 @@ struct BuildDocksDepotWindow : public PickerWindowBase { switch (widget) { case WID_BDD_X: case WID_BDD_Y: + CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP); this->RaiseWidget(WID_BDD_X + _ship_depot_direction); _ship_depot_direction = (widget == WID_BDD_X ? AXIS_X : AXIS_Y); this->LowerWidget(WID_BDD_X + _ship_depot_direction); diff --git a/src/lang/english.txt b/src/lang/english.txt index fd806beb75c4f..d3d14db9422df 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2769,6 +2769,11 @@ STR_JOIN_STATION_CREATE_SPLITTED_STATION :{YELLOW}Build a STR_JOIN_WAYPOINT_CAPTION :{WHITE}Join waypoint STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT :{YELLOW}Build a separate waypoint +# Join depot window +STR_JOIN_DEPOT_CAPTION :{WHITE}Join depot +STR_JOIN_DEPOT_CREATE_SPLITTED_DEPOT :{YELLOW}Build a separate depot +STR_DEPOT_LIST_DEPOT :{YELLOW}{DEPOT} + # Generic toolbar STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE :{BLACK}Disabled as currently no vehicles are available for this infrastructure @@ -5120,6 +5125,7 @@ STR_ERROR_CAN_T_BUILD_ROAD_DEPOT :{WHITE}Can't bu STR_ERROR_CAN_T_BUILD_TRAM_DEPOT :{WHITE}Can't build tram vehicle depot here... STR_ERROR_CAN_T_BUILD_SHIP_DEPOT :{WHITE}Can't build ship depot here... +STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING_DEPOT :{WHITE}... adjoins more than one existing depot STR_ERROR_CAN_T_RENAME_DEPOT :{WHITE}Can't rename depot... STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT :{WHITE}... must be stopped inside a depot diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 06819e4c3dd9b..016ad51c592ff 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -804,6 +804,7 @@ struct BuildRailToolbarWindow : Window { CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_RAIL); CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_RAIL); CloseWindowById(WC_SELECT_STATION, 0); + CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN); CloseWindowByClass(WC_BUILD_BRIDGE); } @@ -1705,6 +1706,12 @@ struct BuildRailDepotWindow : public PickerWindowBase { this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction); } + void Close([[maybe_unused]] int data = 0) override + { + CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN); + this->PickerWindowBase::Close(); + } + void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { if (!IsInsideMM(widget, WID_BRAD_DEPOT_NE, WID_BRAD_DEPOT_NW + 1)) return; @@ -1734,6 +1741,7 @@ struct BuildRailDepotWindow : public PickerWindowBase { case WID_BRAD_DEPOT_SE: case WID_BRAD_DEPOT_SW: case WID_BRAD_DEPOT_NW: + CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN); this->RaiseWidget(WID_BRAD_DEPOT_NE + _build_depot_direction); _build_depot_direction = (DiagDirection)(widget - WID_BRAD_DEPOT_NE); this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction); diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 27066a2144955..3f9b9251cf101 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -687,6 +687,7 @@ struct BuildRoadToolbarWindow : Window { CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD); CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_ROAD); CloseWindowById(WC_SELECT_STATION, 0); + CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD); CloseWindowByClass(WC_BUILD_BRIDGE); } @@ -1106,6 +1107,12 @@ struct BuildRoadDepotWindow : public PickerWindowBase { this->FinishInitNested(TRANSPORT_ROAD); } + void Close([[maybe_unused]] int data = 0) override + { + CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD); + this->PickerWindowBase::Close(); + } + void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { if (!IsInsideMM(widget, WID_BROD_DEPOT_NE, WID_BROD_DEPOT_NW + 1)) return; @@ -1135,6 +1142,7 @@ struct BuildRoadDepotWindow : public PickerWindowBase { case WID_BROD_DEPOT_NE: case WID_BROD_DEPOT_SW: case WID_BROD_DEPOT_SE: + CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD); this->RaiseWidget(WID_BROD_DEPOT_NE + _road_depot_orientation); _road_depot_orientation = (DiagDirection)(widget - WID_BROD_DEPOT_NE); this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation); diff --git a/src/table/settings/game_settings.ini b/src/table/settings/game_settings.ini index 6426080857465..286c517fee6b5 100644 --- a/src/table/settings/game_settings.ini +++ b/src/table/settings/game_settings.ini @@ -151,6 +151,7 @@ from = SLV_DEPOT_SPREAD def = true str = STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS_HELPTEXT +post_cb = [](auto) { CloseWindowByClass(WC_SELECT_DEPOT); } [SDT_VAR] var = depot.depot_spread @@ -162,6 +163,7 @@ max = 64 str = STR_CONFIG_SETTING_DEPOT_SPREAD strhelp = STR_CONFIG_SETTING_DEPOT_SPREAD_HELPTEXT strval = STR_CONFIG_SETTING_TILE_LENGTH +post_cb = [](auto) { CloseWindowByClass(WC_SELECT_DEPOT); } cat = SC_BASIC [SDT_OMANY] diff --git a/src/widgets/depot_widget.h b/src/widgets/depot_widget.h index f94f1263d2436..5c98203a0338b 100644 --- a/src/widgets/depot_widget.h +++ b/src/widgets/depot_widget.h @@ -32,4 +32,11 @@ enum DepotWidgets : WidgetID { WID_D_START_ALL, ///< Start all button. }; +/** Widgets of the #SelectDepotWindow class. */ +enum JoinDepotWidgets { + WID_JD_CAPTION, // Caption of the window. + WID_JD_PANEL, // Main panel. + WID_JD_SCROLLBAR, // Scrollbar of the panel. +}; + #endif /* WIDGETS_DEPOT_WIDGET_H */ diff --git a/src/window_type.h b/src/window_type.h index 4437d26b3f408..5ec2d3c3325a5 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -241,6 +241,12 @@ enum WindowClass { */ WC_SELECT_STATION, + /** + * Select depot (when joining depots); %Window numbers: + * - #Vehicle type = #JoinDepotWidgets + */ + WC_SELECT_DEPOT, + /** * News window; %Window numbers: * - 0 = #NewsWidgets From ba4573b0561aa5547f59c9e9b5ff4b0f9310092e Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 25 Aug 2023 16:43:49 +0200 Subject: [PATCH 010/143] Feature: Highlight tiles of a depot and adjacent depot tiles when building. --- src/depot_func.h | 3 ++ src/depot_gui.cpp | 105 +++++++++++++++++++++++++++++++++++++ src/dock_gui.cpp | 10 ++++ src/lang/english.txt | 2 + src/rail_gui.cpp | 9 ++++ src/road_gui.cpp | 9 ++++ src/viewport.cpp | 38 ++++++++++++++ src/viewport_func.h | 3 ++ src/widgets/depot_widget.h | 1 + 9 files changed, 180 insertions(+) diff --git a/src/depot_func.h b/src/depot_func.h index d4d5ab69aa62f..f214e6d848997 100644 --- a/src/depot_func.h +++ b/src/depot_func.h @@ -41,4 +41,7 @@ CommandCost FindJoiningDepot(TileArea ta, VehicleType veh_type, DepotID &join_to using DepotPickerCmdProc = std::function; void ShowSelectDepotIfNeeded(TileArea ta, DepotPickerCmdProc proc, VehicleType veh_type); +struct Window; +void CheckRedrawDepotHighlight(const Window *w, VehicleType veh_type); + #endif /* DEPOT_FUNC_H */ diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 9055a39705a9f..151efe3c23ae9 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -80,6 +80,7 @@ static constexpr NWidgetPart _nested_train_depot_widgets[] = { NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_D_BUILD), SetDataTip(0x0, STR_NULL), SetFill(1, 1), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_D_CLONE), SetDataTip(0x0, STR_NULL), SetFill(1, 1), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_D_HIGHLIGHT), SetDataTip(STR_BUTTON_HIGHLIGHT_DEPOT, STR_TOOLTIP_HIGHLIGHT_DEPOT), SetFill(1, 1), SetResize(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_D_VEHICLE_LIST), SetDataTip(0x0, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetFill(0, 1), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_STOP_ALL), SetDataTip(SPR_FLAG_VEH_STOPPED, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_START_ALL), SetDataTip(SPR_FLAG_VEH_RUNNING, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1), @@ -303,6 +304,7 @@ struct DepotWindow : Window { CloseWindowById(WC_BUILD_VEHICLE, this->window_number); CloseWindowById(GetWindowClassForVehicleType(this->type), VehicleListIdentifier(VL_DEPOT_LIST, this->type, this->owner, this->window_number).Pack(), false); OrderBackup::Reset(this->window_number); + SetViewportHighlightDepot(this->window_number, false); this->Window::Close(); } @@ -712,6 +714,9 @@ struct DepotWindow : Window { void OnPaint() override { + extern DepotID _viewport_highlight_depot; + this->SetWidgetLoweredState(WID_D_HIGHLIGHT, _viewport_highlight_depot == this->window_number); + if (this->generate_list) { /* Generate the vehicle list * It's ok to use the wagon pointers for non-trains as they will be ignored */ @@ -804,6 +809,11 @@ struct DepotWindow : Window { ShowQueryString(STR_DEPOT_NAME, STR_DEPOT_RENAME_DEPOT_CAPTION, MAX_LENGTH_DEPOT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS); break; + case WID_D_HIGHLIGHT: + this->SetWidgetDirty(WID_D_HIGHLIGHT); + SetViewportHighlightDepot(this->window_number, !this->IsWidgetLowered(WID_D_HIGHLIGHT)); + break; + case WID_D_STOP_ALL: case WID_D_START_ALL: { VehicleListIdentifier vli(VL_DEPOT_LIST, this->type, this->owner); @@ -1285,6 +1295,15 @@ struct SelectDepotWindow : Window { this->vscroll = this->GetScrollbar(WID_JD_SCROLLBAR); this->FinishInitNested(veh_type); this->OnInvalidateData(0); + + _thd.freeze = true; + } + + ~SelectDepotWindow() + { + SetViewportHighlightDepot(INVALID_DEPOT, true); + + _thd.freeze = false; } void UpdateWidgetSize(int widget, Dimension &size, const Dimension &padding, [[maybe_unused]] Dimension &fill, Dimension &resize) override @@ -1369,6 +1388,19 @@ struct SelectDepotWindow : Window { this->vscroll->SetCount((uint)_depots_nearby_list.size()); this->SetDirty(); } + + void OnMouseOver(Point pt, int widget) override + { + if (widget != WID_JD_PANEL) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + return; + } + + /* Highlight depot under cursor */ + auto it = this->vscroll->GetScrolledItemFromWidget(_depots_nearby_list, pt.y, this, WID_JD_PANEL, WidgetDimensions::scaled.framerect.top); + SetViewportHighlightDepot(*it, true); + } + }; static const NWidgetPart _nested_select_depot_widgets[] = { @@ -1408,3 +1440,76 @@ void ShowSelectDepotIfNeeded(TileArea ta, DepotPickerCmdProc proc, VehicleType v proc(INVALID_DEPOT); } } + +/** + * Find depots adjacent to the current tile highlight area, so that all depot tiles + * can be highlighted. + * @param v_type Vehicle type to check. + */ +static void HighlightSingleAdjacentDepot(VehicleType v_type) +{ + /* With distant join we don't know which depot will be selected, so don't show any */ + if (_ctrl_pressed) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + return; + } + + /* Tile area for TileHighlightData */ + TileArea location(TileVirtXY(_thd.pos.x, _thd.pos.y), _thd.size.x / TILE_SIZE - 1, _thd.size.y / TILE_SIZE - 1); + + /* If the current tile is already a depot, then it must be the nearest depot. */ + if (IsDepotTypeTile(location.tile, (TransportType)v_type) && + GetTileOwner(location.tile) == _local_company) { + SetViewportHighlightDepot(GetDepotIndex(location.tile), true); + return; + } + + /* Extended area by one tile */ + uint x = TileX(location.tile); + uint y = TileY(location.tile); + + int max_c = 1; + TileArea ta(TileXY(std::max(0, x - max_c), std::max(0, y - max_c)), TileXY(std::min(Map::MaxX(), x + location.w + max_c), std::min(Map::MaxY(), y + location.h + max_c))); + + DepotID adjacent = INVALID_DEPOT; + + for (TileIndex tile : ta) { + if (IsDepotTile(tile) && GetTileOwner(tile) == _local_company) { + Depot *depot = Depot::GetByTile(tile); + if (depot == nullptr) continue; + if (depot->veh_type != v_type) continue; + if (adjacent != INVALID_DEPOT && depot->index != adjacent) { + /* Multiple nearby, distant join is required. */ + adjacent = INVALID_DEPOT; + break; + } + adjacent = depot->index; + } + } + SetViewportHighlightDepot(adjacent, true); +} + +/** + * Check whether we need to redraw the depot highlight. + * If it is needed actually make the window for redrawing. + * @param w the window to check. + * @param veh_type vehicle type to check. + */ +void CheckRedrawDepotHighlight(const Window *w, VehicleType veh_type) +{ + /* Test if ctrl state changed */ + static bool _last_ctrl_pressed; + if (_ctrl_pressed != _last_ctrl_pressed) { + _thd.dirty = 0xff; + _last_ctrl_pressed = _ctrl_pressed; + } + + if (_thd.dirty & 1) { + _thd.dirty &= ~1; + w->SetDirty(); + + if (_thd.drawstyle == HT_RECT) { + HighlightSingleAdjacentDepot(veh_type); + } + } +} diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index ccf35c3e449a7..7ba951d141f1d 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -32,6 +32,7 @@ #include "waypoint_cmd.h" #include "timer/timer.h" #include "timer/timer_game_calendar.h" +#include "depot_func.h" #include "widgets/dock_widget.h" @@ -112,6 +113,8 @@ struct BuildDocksToolbarWindow : Window { { if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_DT_STATION)) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); + if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_DT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + this->Window::Close(); } @@ -270,6 +273,8 @@ struct BuildDocksToolbarWindow : Window { { if (_game_mode != GM_EDITOR && this->IsWidgetLowered(WID_DT_STATION)) SetViewportCatchmentStation(nullptr, true); + if (_game_mode != GM_EDITOR && this->IsWidgetLowered(WID_DT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + this->RaiseButtons(); CloseWindowById(WC_BUILD_STATION, TRANSPORT_WATER); @@ -586,6 +591,11 @@ struct BuildDocksDepotWindow : public PickerWindowBase { break; } } + + void OnRealtimeTick([[maybe_unused]] uint delta_ms) override + { + CheckRedrawDepotHighlight(this, VEH_SHIP); + } }; static constexpr NWidgetPart _nested_build_docks_depot_widgets[] = { diff --git a/src/lang/english.txt b/src/lang/english.txt index d3d14db9422df..f30e938945185 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -278,6 +278,8 @@ STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Select f STR_BUTTON_SORT_BY :{BLACK}Sort by STR_BUTTON_CATCHMENT :{BLACK}Coverage STR_TOOLTIP_CATCHMENT :{BLACK}Toggle coverage area display +STR_BUTTON_HIGHLIGHT_DEPOT :{BLACK}Highlight +STR_TOOLTIP_HIGHLIGHT_DEPOT :{BLACK}Toggle highlight on viewport for this depot STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Close window STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Window title - drag this to move window diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 016ad51c592ff..dcb0780919ee8 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -41,6 +41,7 @@ #include "timer/timer.h" #include "timer/timer_game_calendar.h" #include "picker_gui.h" +#include "depot_func.h" #include "station_map.h" #include "tunnelbridge_map.h" @@ -454,6 +455,7 @@ struct BuildRailToolbarWindow : Window { if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); CloseWindowById(WC_SELECT_STATION, 0); + if (this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); this->Window::Close(); } @@ -795,6 +797,8 @@ struct BuildRailToolbarWindow : Window { if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) SetViewportCatchmentStation(nullptr, true); if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true); + if (this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + this->RaiseButtons(); this->DisableWidget(WID_RAT_REMOVE); this->SetWidgetDirty(WID_RAT_REMOVE); @@ -1750,6 +1754,11 @@ struct BuildRailDepotWindow : public PickerWindowBase { break; } } + + void OnRealtimeTick([[maybe_unused]] uint delta_ms) override + { + CheckRedrawDepotHighlight(this, VEH_TRAIN); + } }; /** Nested widget definition of the build rail depot window */ diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 3f9b9251cf101..14764444b8d25 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -43,6 +43,7 @@ #include "picker_gui.h" #include "timer/timer.h" #include "timer/timer_game_calendar.h" +#include "depot_func.h" #include "widgets/road_widget.h" @@ -368,6 +369,7 @@ struct BuildRoadToolbarWindow : Window { { if (_game_mode == GM_NORMAL && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); + if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_ROT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); this->Window::Close(); } @@ -673,6 +675,8 @@ struct BuildRoadToolbarWindow : Window { { if (_game_mode != GM_EDITOR && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true); + if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_ROT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + this->RaiseButtons(); this->SetWidgetDisabledState(WID_ROT_REMOVE, true); this->SetWidgetDirty(WID_ROT_REMOVE); @@ -1154,6 +1158,11 @@ struct BuildRoadDepotWindow : public PickerWindowBase { break; } } + + void OnRealtimeTick([[maybe_unused]] uint delta_ms) override + { + CheckRedrawDepotHighlight(this, VEH_ROAD); + } }; static constexpr NWidgetPart _nested_build_road_depot_widgets[] = { diff --git a/src/viewport.cpp b/src/viewport.cpp index 1aa848058ee82..fbe06c45e3af7 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -90,6 +90,7 @@ #include "network/network_func.h" #include "framerate_type.h" #include "viewport_cmd.h" +#include "depot_map.h" #include #include @@ -1002,6 +1003,7 @@ enum TileHighlightType { const Station *_viewport_highlight_station; ///< Currently selected station for coverage area highlight const Waypoint *_viewport_highlight_waypoint; ///< Currently selected waypoint for coverage area highlight const Town *_viewport_highlight_town; ///< Currently selected town for coverage area highlight +DepotID _viewport_highlight_depot = INVALID_DEPOT; ///< Currently selected depot for depot highlight /** * Get tile highlight type of coverage area for a given tile. @@ -1010,6 +1012,10 @@ const Town *_viewport_highlight_town; ///< Currently selected town for coverage */ static TileHighlightType GetTileHighlightType(TileIndex t) { + if (_viewport_highlight_depot != INVALID_DEPOT) { + if (IsDepotTile(t) && GetDepotIndex(t) == _viewport_highlight_depot) return THT_WHITE; + } + if (_viewport_highlight_station != nullptr) { if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station->index) return THT_WHITE; if (_viewport_highlight_station->TileIsInCatchment(t)) return THT_BLUE; @@ -3589,6 +3595,9 @@ void MarkCatchmentTilesDirty() } MarkWholeScreenDirty(); } + if (_viewport_highlight_depot != INVALID_DEPOT) { + MarkWholeScreenDirty(); + } } static void SetWindowDirtyForViewportCatchment() @@ -3596,6 +3605,7 @@ static void SetWindowDirtyForViewportCatchment() if (_viewport_highlight_station != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station->index); if (_viewport_highlight_waypoint != nullptr) SetWindowDirty(WC_WAYPOINT_VIEW, _viewport_highlight_waypoint->index); if (_viewport_highlight_town != nullptr) SetWindowDirty(WC_TOWN_VIEW, _viewport_highlight_town->index); + if (_viewport_highlight_depot != INVALID_DEPOT) SetWindowDirty(WC_VEHICLE_DEPOT, _viewport_highlight_depot); } static void ClearViewportCatchment() @@ -3604,6 +3614,7 @@ static void ClearViewportCatchment() _viewport_highlight_station = nullptr; _viewport_highlight_waypoint = nullptr; _viewport_highlight_town = nullptr; + _viewport_highlight_depot = INVALID_DEPOT; } /** @@ -3665,3 +3676,30 @@ void SetViewportCatchmentTown(const Town *t, bool sel) } if (_viewport_highlight_town != nullptr) SetWindowDirty(WC_TOWN_VIEW, _viewport_highlight_town->index); } + +static void MarkDepotTilesDirty() +{ + if (_viewport_highlight_depot != INVALID_DEPOT) { + MarkWholeScreenDirty(); + return; + } +} + +/** + * Select or deselect depot to highlight. + * @param *dep Depot in question + * @param sel Select or deselect given depot + */ +void SetViewportHighlightDepot(const DepotID dep, bool sel) +{ + SetWindowDirtyForViewportCatchment(); + if (sel && _viewport_highlight_depot != dep) { + ClearViewportCatchment(); + _viewport_highlight_depot = dep; + MarkDepotTilesDirty(); + } else if (!sel && _viewport_highlight_depot == dep) { + MarkDepotTilesDirty(); + _viewport_highlight_depot = INVALID_DEPOT; + } + if (_viewport_highlight_depot != INVALID_DEPOT) SetWindowDirty(WC_VEHICLE_DEPOT, _viewport_highlight_depot); +} diff --git a/src/viewport_func.h b/src/viewport_func.h index 5b1537478c78d..94e132f88ac75 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -16,6 +16,7 @@ #include "tile_map.h" #include "station_type.h" #include "vehicle_type.h" +#include "depot_type.h" static const int TILE_HEIGHT_STEP = 50; ///< One Z unit tile height difference is displayed as 50m. @@ -102,6 +103,8 @@ struct Town; void SetViewportCatchmentStation(const Station *st, bool sel); void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel); void SetViewportCatchmentTown(const Town *t, bool sel); +void SetViewportHighlightDepot(const DepotID dep, bool sel); + void MarkCatchmentTilesDirty(); template diff --git a/src/widgets/depot_widget.h b/src/widgets/depot_widget.h index 5c98203a0338b..8797fb36ff811 100644 --- a/src/widgets/depot_widget.h +++ b/src/widgets/depot_widget.h @@ -27,6 +27,7 @@ enum DepotWidgets : WidgetID { WID_D_LOCATION, ///< Location button. WID_D_SHOW_RENAME, ///< Show rename panel. WID_D_RENAME, ///< Rename button. + WID_D_HIGHLIGHT, ///< Highlight button. WID_D_VEHICLE_LIST, ///< List of vehicles. WID_D_STOP_ALL, ///< Stop all button. WID_D_START_ALL, ///< Start all button. From 3eff38e46d20f7716b70c124af392b57ed1fa2bf Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 31 Mar 2021 23:20:44 +0200 Subject: [PATCH 011/143] Add: Add new viewport place methods for rectangles with one side with a fixed length. --- src/tilehighlight_func.h | 2 ++ src/tilehighlight_type.h | 5 +++-- src/viewport.cpp | 29 +++++++++++++++++++++++------ src/viewport_type.h | 2 ++ 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/tilehighlight_func.h b/src/tilehighlight_func.h index 572c5bd43ec6a..770013bc59ed1 100644 --- a/src/tilehighlight_func.h +++ b/src/tilehighlight_func.h @@ -26,6 +26,8 @@ void VpStartDragging(ViewportDragDropSelectionProcess process); void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process); void VpSetPresizeRange(TileIndex from, TileIndex to); void VpSetPlaceSizingLimit(int limit); +void VpSetPlaceFixedSize(uint8_t fixed_size); +void VpResetFixedSize(); void UpdateTileSelection(); diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h index a19eef5aacb9b..a7a43d179e853 100644 --- a/src/tilehighlight_type.h +++ b/src/tilehighlight_type.h @@ -55,11 +55,12 @@ struct TileHighlightData { Point new_pos; ///< New value for \a pos; used to determine whether to redraw the selection. Point new_size; ///< New value for \a size; used to determine whether to redraw the selection. Point new_outersize; ///< New value for \a outersize; used to determine whether to redraw the selection. - uint8_t dirty; ///< Whether the build station window needs to redraw due to the changed selection. + uint8_t dirty; ///< Whether the build station window needs to redraw due to the changed selection. Point selstart; ///< The location where the dragging started. Point selend; ///< The location where the drag currently ends. - uint8_t sizelimit; ///< Whether the selection is limited in length, and what the maximum length is. + uint8_t sizelimit; ///< Whether the selection is limited in length, and what the maximum length is. + uint8_t fixed_size; ///< The fixed length for one of the sides. HighLightStyle drawstyle; ///< Lower bits 0-3 are reserved for detailed highlight information. HighLightStyle next_drawstyle; ///< Queued, but not yet drawn style. diff --git a/src/viewport.cpp b/src/viewport.cpp index fbe06c45e3af7..5e60dae16a758 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -2680,6 +2680,8 @@ void UpdateTileSelection() } _thd.new_pos.x = x1 & ~TILE_UNIT_MASK; _thd.new_pos.y = y1 & ~TILE_UNIT_MASK; + if (_thd.select_method == VPM_LIMITED_X_FIXED_Y) _thd.new_size.y = (TILE_SIZE * _thd.fixed_size) & ~TILE_UNIT_MASK; + if (_thd.select_method == VPM_LIMITED_Y_FIXED_X) _thd.new_size.x = (TILE_SIZE * _thd.fixed_size) & ~TILE_UNIT_MASK; } } @@ -2772,6 +2774,15 @@ void VpSetPlaceSizingLimit(int limit) _thd.sizelimit = limit; } +void VpSetPlaceFixedSize(uint8_t fixed) +{ + _thd.fixed_size = fixed; +} + +void VpResetFixedSize() { + VpSetPlaceFixedSize(1); +} + /** * Highlights all tiles between a set of two tiles. Used in dock and tunnel placement * @param from TileIndex of the first tile to highlight @@ -3243,7 +3254,7 @@ void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method) sx = _thd.selstart.x; sy = _thd.selstart.y; - int limit = 0; + int limit = -1; switch (method) { case VPM_X_OR_Y: // drag in X or Y direction @@ -3256,28 +3267,33 @@ void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method) } goto calc_heightdiff_single_direction; + case VPM_LIMITED_Y_FIXED_X: case VPM_X_LIMITED: // Drag in X direction (limited size). limit = (_thd.sizelimit - 1) * TILE_SIZE; [[fallthrough]]; case VPM_FIX_X: // drag in Y direction - x = sx; + x = sx + (method == VPM_LIMITED_Y_FIXED_X ? (TILE_SIZE * (_thd.fixed_size - 1)) : 0) ; style = HT_DIR_Y; goto calc_heightdiff_single_direction; + case VPM_LIMITED_X_FIXED_Y: case VPM_Y_LIMITED: // Drag in Y direction (limited size). limit = (_thd.sizelimit - 1) * TILE_SIZE; [[fallthrough]]; case VPM_FIX_Y: // drag in X direction - y = sy; + y = sy + (method == VPM_LIMITED_X_FIXED_Y ? (TILE_SIZE * (_thd.fixed_size - 1)) : 0) ; style = HT_DIR_X; calc_heightdiff_single_direction:; - if (limit > 0) { - x = sx + Clamp(x - sx, -limit, limit); - y = sy + Clamp(y - sy, -limit, limit); + if (limit >= 0) { + if (method != VPM_LIMITED_X_FIXED_Y) y = sy + Clamp(y - sy, -limit, limit); + if (method != VPM_LIMITED_Y_FIXED_X) x = sx + Clamp(x - sx, -limit, limit); } + + if (method == VPM_LIMITED_Y_FIXED_X || method == VPM_LIMITED_X_FIXED_Y) goto measure_area; + if (_settings_client.gui.measure_tooltip) { TileIndex t0 = TileVirtXY(sx, sy); TileIndex t1 = TileVirtXY(x, y); @@ -3307,6 +3323,7 @@ calc_heightdiff_single_direction:; [[fallthrough]]; case VPM_X_AND_Y: // drag an X by Y area +measure_area: if (_settings_client.gui.measure_tooltip) { static const StringID measure_strings_area[] = { STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF diff --git a/src/viewport_type.h b/src/viewport_type.h index 0fde051f38db1..4616bdfd9b9d3 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -99,6 +99,8 @@ enum ViewportPlaceMethod { VPM_FIX_VERTICAL = 6, ///< drag only in vertical direction VPM_X_LIMITED = 7, ///< Drag only in X axis with limited size VPM_Y_LIMITED = 8, ///< Drag only in Y axis with limited size + VPM_LIMITED_Y_FIXED_X = 9, ///< Drag only in Y axis with limited size and a fixed value for X axis + VPM_LIMITED_X_FIXED_Y = 10, ///< Drag only in X axis with limited size and a fixed value for Y axis VPM_RAILDIRS = 0x40, ///< all rail directions VPM_SIGNALDIRS = 0x80, ///< similar to VMP_RAILDIRS, but with different cursor }; From db61c38a017ed739a1c2b86442fce6607fa8734d Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Mon, 8 Apr 2024 19:03:17 +0200 Subject: [PATCH 012/143] Feature: Allow building depots by drag and drop and joining them if they have the same transport type. --- src/dock_gui.cpp | 28 ++++++- src/rail_cmd.cpp | 132 ++++++++++++++++++------------- src/rail_cmd.h | 4 +- src/rail_gui.cpp | 44 ++++++++--- src/road_cmd.cpp | 94 ++++++++++++---------- src/road_cmd.h | 5 +- src/road_gui.cpp | 28 +++++-- src/roadveh_cmd.cpp | 42 +++++++++- src/script/api/script_marine.cpp | 2 +- src/script/api/script_rail.cpp | 2 +- src/script/api/script_road.cpp | 2 +- src/train_gui.cpp | 4 +- src/viewport.cpp | 2 +- src/viewport_type.h | 1 + src/water_cmd.cpp | 100 ++++++++++++----------- src/water_cmd.h | 2 +- 16 files changed, 320 insertions(+), 172 deletions(-) diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index 7ba951d141f1d..2cc37266a5610 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -167,7 +167,9 @@ struct BuildDocksToolbarWindow : Window { break; case WID_DT_DEPOT: // Build depot button - if (HandlePlacePushButton(this, WID_DT_DEPOT, SPR_CURSOR_SHIP_DEPOT, HT_RECT)) ShowBuildDocksDepotPicker(this); + if (HandlePlacePushButton(this, widget, SPR_CURSOR_SHIP_DEPOT, HT_RECT)) { + ShowBuildDocksDepotPicker(this); + } break; case WID_DT_STATION: // Build station button @@ -207,9 +209,16 @@ struct BuildDocksToolbarWindow : Window { PlaceProc_DemolishArea(tile); break; - case WID_DT_DEPOT: // Build depot button - Command::Post(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, tile, _ship_depot_direction); + case WID_DT_DEPOT: { // Build depot button + CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP); + + ViewportPlaceMethod vpm = _ship_depot_direction != AXIS_X ? VPM_LIMITED_X_FIXED_Y : VPM_LIMITED_Y_FIXED_X; + VpSetPlaceSizingLimit(_settings_game.depot.depot_spread); + VpStartPlaceSizing(tile, vpm, DDSP_BUILD_DEPOT); + /* Select tiles now to prevent selection from flickering. */ + VpSelectTilesWithMethod(pt.x, pt.y, vpm); break; + } case WID_DT_STATION: { // Build station button /* Determine the watery part of the dock. */ @@ -263,6 +272,15 @@ struct BuildDocksToolbarWindow : Window { case DDSP_CREATE_RIVER: Command::Post(STR_ERROR_CAN_T_PLACE_RIVERS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_RIVER, _ctrl_pressed); break; + case DDSP_BUILD_DEPOT: { + bool adjacent = _ctrl_pressed; + auto proc = [=](DepotID join_to) -> bool { + return Command::Post(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, start_tile, _ship_depot_direction, adjacent, join_to, end_tile); + }; + + ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_SHIP); + break; + } default: break; } @@ -520,10 +538,13 @@ struct BuildDocksDepotWindow : public PickerWindowBase { private: static void UpdateDocksDirection() { + VpSetPlaceFixedSize(2); if (_ship_depot_direction != AXIS_X) { SetTileSelectSize(1, 2); + _thd.select_method = VPM_LIMITED_X_FIXED_Y; } else { SetTileSelectSize(2, 1); + _thd.select_method = VPM_LIMITED_Y_FIXED_X; } } @@ -538,6 +559,7 @@ struct BuildDocksDepotWindow : public PickerWindowBase { void Close([[maybe_unused]] int data = 0) override { CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP); + VpResetFixedSize(); this->PickerWindowBase::Close(); } diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 91dff791dbb21..89554c655c342 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -952,22 +952,32 @@ CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, Tile /** * Build a train depot * @param flags operation to perform - * @param tile position of the train depot + * @param tile first position of the train depot * @param railtype rail type * @param dir entrance direction + * @param adjacent allow adjacent depots + * @param join_to depot to join to + * @param end_tile end tile of the area to be built * @return the cost of this operation or an error * * @todo When checking for the tile slope, * distinguish between "Flat land required" and "land sloped in wrong direction" */ -CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir) +CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile) { /* check railtype and valid direction for depot (0 through 3), 4 in total */ if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR; - Slope tileh = GetTileSlope(tile); - CommandCost cost(EXPENSES_CONSTRUCTION); + TileArea ta(tile, end_tile); + Depot *depot = nullptr; + + /* Create a new depot or find a depot to join to. */ + CommandCost ret = FindJoiningDepot(ta, VEH_TRAIN, join_to, depot, adjacent, flags); + if (ret.Failed()) return ret; + + uint8_t num_new_depot_tiles = 0; + uint8_t num_rotated_depot_tiles = 0; /* Prohibit construction if * The tile is non-flat AND @@ -975,58 +985,59 @@ CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType rai * 2) the tile is steep i.e. spans two height levels * 3) the exit points in the wrong direction */ - - if (tileh != SLOPE_FLAT) { - if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) { - return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + for (Tile t : ta) { + if (IsBridgeAbove(t)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + + Slope tileh = GetTileSlope(t); + if (tileh != SLOPE_FLAT) { + if (!_settings_game.construction.build_on_slopes || + !CanBuildDepotByTileh(dir, tileh)) { + return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + } + cost.AddCost(_price[PR_BUILD_FOUNDATION]); } - cost.AddCost(_price[PR_BUILD_FOUNDATION]); - } - - /* Allow the user to rotate the depot instead of having to destroy it and build it again */ - bool rotate_existing_depot = false; - if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) { - CommandCost ret = CheckTileOwnership(tile); - if (ret.Failed()) return ret; - if (dir == GetRailDepotDirection(tile)) return CommandCost(); + /* Check whether a depot tile exists and it needs to be rotated. */ + if (IsRailDepotTile(t) && GetDepotIndex(t) == join_to && railtype == GetRailType(t)) { + if (dir == GetRailDepotDirection(t)) continue; - ret = EnsureNoVehicleOnGround(tile); - if (ret.Failed()) return ret; - - rotate_existing_depot = true; - } + ret = EnsureNoVehicleOnGround(t); + if (ret.Failed()) return ret; - if (!rotate_existing_depot) { - cost.AddCost(Command::Do(flags, tile)); - if (cost.Failed()) return cost; + num_rotated_depot_tiles++; + if (flags & DC_EXEC) { + SetRailDepotExitDirection(t, dir); + } + } else { + cost.AddCost(Command::Do(flags, t)); + if (cost.Failed()) return cost; - if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + num_new_depot_tiles++; + if (flags & DC_EXEC) { + MakeRailDepot(t, _current_company, depot->index, dir, railtype); + MarkTileDirtyByTile(t); + } + } - if (!Depot::CanAllocateItem()) return CMD_ERROR; + if (flags & DC_EXEC) { + AddSideToSignalBuffer(t, INVALID_DIAGDIR, _current_company); + YapfNotifyTrackLayoutChange(t, DiagDirToDiagTrack(dir)); + MarkTileDirtyByTile(t); + } } - if (flags & DC_EXEC) { - if (rotate_existing_depot) { - SetRailDepotExitDirection(tile, dir); - } else { - Depot *d = new Depot(tile, VEH_TRAIN, _current_company); - d->build_date = TimerGameCalendar::date; - - MakeRailDepot(tile, _current_company, d->index, dir, railtype); - MakeDefaultName(d); + if (num_new_depot_tiles + num_rotated_depot_tiles == 0) return CommandCost(); - Company::Get(_current_company)->infrastructure.rail[railtype]++; - DirtyCompanyInfrastructureWindows(_current_company); - } + cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN] * (num_new_depot_tiles + num_rotated_depot_tiles)); + cost.AddCost(RailBuildCost(railtype) * (num_new_depot_tiles + num_rotated_depot_tiles)); - MarkTileDirtyByTile(tile); - AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_company); - YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir)); + if (flags & DC_EXEC) { + Company::Get(_current_company)->infrastructure.rail[railtype] += num_new_depot_tiles; + DirtyCompanyInfrastructureWindows(_current_company); + depot->AfterAddRemove(ta, true); + if (join_to == NEW_DEPOT) MakeDefaultName(depot); } - cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN]); - cost.AddCost(RailBuildCost(railtype)); return cost; } @@ -1558,6 +1569,7 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s if (area_start >= Map::Size()) return CMD_ERROR; TrainList affected_trains; + std::vector affected_depots; CommandCost cost(EXPENSES_CONSTRUCTION); CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert. @@ -1653,11 +1665,11 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s /* notify YAPF about the track layout change */ YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile)); - /* Update build vehicle window related to this depot */ - DepotID depot_id = GetDepotIndex(tile); - InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); - InvalidateWindowData(WC_BUILD_VEHICLE, depot_id); + if (find(affected_depots.begin(), affected_depots.end(), (tile)) == affected_depots.end()) { + affected_depots.push_back(GetDepotIndex(tile)); + } } + found_convertible_track = true; cost.AddCost(RailConvertCost(type, totype)); break; @@ -1755,6 +1767,13 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s } if (flags & DC_EXEC) { + /* Update affected depots. */ + for (auto &depot_tile : affected_depots) { + Depot *dep = Depot::Get(depot_tile); + dep->RescanDepotTiles(); + InvalidateWindowData(WC_VEHICLE_DEPOT, dep->index); + } + /* Railtype changed, update trains as when entering different track */ for (Train *v : affected_trains) { v->ConsistChanged(CCF_TRACK); @@ -1775,9 +1794,11 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) if (ret.Failed()) return ret; if (flags & DC_EXEC) { - /* read variables before the depot is removed */ + Depot *depot = Depot::GetByTile(tile); + Company *c = Company::GetIfValid(depot->owner); + assert(c != nullptr); + DiagDirection dir = GetRailDepotDirection(tile); - Owner owner = GetTileOwner(tile); Train *v = nullptr; if (HasDepotReservation(tile)) { @@ -1785,14 +1806,17 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) if (v != nullptr) FreeTrainTrackReservation(v); } - Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--; - DirtyCompanyInfrastructureWindows(owner); + c->infrastructure.rail[GetRailType(tile)]--; + DirtyCompanyInfrastructureWindows(c->index); - delete Depot::GetByTile(tile); DoClearSquare(tile); - AddSideToSignalBuffer(tile, dir, owner); + + AddSideToSignalBuffer(tile, dir, c->index); + YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir)); if (v != nullptr) TryPathReserve(v, true); + + depot->AfterAddRemove(TileArea(tile), false); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]); diff --git a/src/rail_cmd.h b/src/rail_cmd.h index b9f6c0cc034a2..ce10c4fd7783b 100644 --- a/src/rail_cmd.h +++ b/src/rail_cmd.h @@ -19,7 +19,7 @@ CommandCost CmdBuildRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileI CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, Track track); CommandCost CmdBuildSingleRail(DoCommandFlag flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals); CommandCost CmdRemoveSingleRail(DoCommandFlag flags, TileIndex tile, Track track); -CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir); +CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, DepotID depot_id, TileIndex end_tile); CommandCost CmdBuildSingleSignal(DoCommandFlag flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy); CommandCost CmdRemoveSingleSignal(DoCommandFlag flags, TileIndex tile, Track track); CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal); @@ -40,6 +40,6 @@ DEF_CMD_TRAIT(CMD_REMOVE_SIGNAL_TRACK, CmdRemoveSignalTrack, CMD_AUTO, CommandCallback CcPlaySound_CONSTRUCTION_RAIL; CommandCallback CcStation; CommandCallback CcBuildRailTunnel; -void CcRailDepot(Commands cmd, const CommandCost &result, TileIndex tile, RailType rt, DiagDirection dir); +void CcRailDepot(Commands cmd, const CommandCost &result, TileIndex tile, RailType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile); #endif /* RAIL_CMD_H */ diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index dcb0780919ee8..271700e8c88d4 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -117,7 +117,7 @@ static void GenericPlaceRail(TileIndex tile, Track track) */ static void PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track) { - if (GetRailTileType(tile) == RAIL_TILE_DEPOT) return; + if (IsRailDepot(tile)) return; if (GetRailTileType(tile) == RAIL_TILE_SIGNALS && !_settings_client.gui.auto_remove_signals) return; if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) return; @@ -138,24 +138,26 @@ static const DiagDirection _place_depot_extra_dir[12] = { DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_NW, DIAGDIR_NE, }; -void CcRailDepot(Commands, const CommandCost &result, TileIndex tile, RailType, DiagDirection dir) +void CcRailDepot(Commands, const CommandCost &result, TileIndex start_tile, RailType, DiagDirection dir, bool, DepotID, TileIndex end_tile) { if (result.Failed()) return; - if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, tile); + if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, start_tile); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); - tile += TileOffsByDiagDir(dir); + TileArea ta(start_tile, end_tile); + for (TileIndex t : ta) { + TileIndex tile = t + TileOffsByDiagDir(dir); - if (IsTileType(tile, MP_RAILWAY)) { - PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]); + if (IsTileType(tile, MP_RAILWAY)) { + PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]); - /* Don't place the rail straight out of the depot of there is another depot across from it. */ - Tile double_depot_tile = tile + TileOffsByDiagDir(dir); - bool is_double_depot = IsValidTile(double_depot_tile) && IsRailDepotTile(double_depot_tile); - if (!is_double_depot) PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 4], _place_depot_extra_track[dir + 4]); + Tile double_depot_tile = tile + TileOffsByDiagDir(dir); + bool is_double_depot = IsValidTile(double_depot_tile) && IsRailDepotTile(double_depot_tile); + if (!is_double_depot) PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 4], _place_depot_extra_track[dir + 4]); - PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 8], _place_depot_extra_track[dir + 8]); + PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 8], _place_depot_extra_track[dir + 8]); + } } } @@ -690,9 +692,14 @@ struct BuildRailToolbarWindow : Window { PlaceProc_DemolishArea(tile); break; - case WID_RAT_BUILD_DEPOT: - Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, tile, _cur_railtype, _build_depot_direction); + case WID_RAT_BUILD_DEPOT: { + CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN); + + ViewportPlaceMethod vpm = (DiagDirToAxis(_build_depot_direction) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED; + VpStartPlaceSizing(tile, vpm, DDSP_BUILD_DEPOT); + VpSetPlaceSizingLimit(_settings_game.depot.depot_spread); break; + } case WID_RAT_BUILD_WAYPOINT: PlaceRail_Waypoint(tile); @@ -788,6 +795,17 @@ struct BuildRailToolbarWindow : Window { } } break; + + case DDSP_BUILD_DEPOT: { + bool adjacent = _ctrl_pressed; + + auto proc = [=](DepotID join_to) -> bool { + return Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, start_tile, _cur_railtype, _build_depot_direction, adjacent, join_to, end_tile); + }; + + ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_TRAIN); + break; + } } } } diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 17ce071c410c1..3dd28cd934f69 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -1141,66 +1141,79 @@ std::tuple CmdRemoveLongRoad(DoCommandFlag flags, TileIndex * @param flags operation to perform * @param rt road type * @param dir entrance direction + * @param adjacent allow adjacent depots + * @param depot_id depot to join to + * @param end_tile end tile of the depot to be built * @return the cost of this operation or an error * * @todo When checking for the tile slope, * distinguish between "Flat land required" and "land sloped in wrong direction" */ -CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir) +CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile) { if (!ValParamRoadType(rt) || !IsValidDiagDirection(dir)) return CMD_ERROR; - CommandCost cost(EXPENSES_CONSTRUCTION); + TileArea ta(tile, end_tile); + assert(ta.w == 1 || ta.h == 1); - Slope tileh = GetTileSlope(tile); - if (tileh != SLOPE_FLAT) { - if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) { - return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); - } - cost.AddCost(_price[PR_BUILD_FOUNDATION]); - } - - /* Allow the user to rotate the depot instead of having to destroy it and build it again */ - bool rotate_existing_depot = false; - if (IsRoadDepotTile(tile) && (HasRoadTypeTram(tile) ? rt == GetRoadTypeTram(tile) : rt == GetRoadTypeRoad(tile))) - { - CommandCost ret = CheckTileOwnership(tile); - if (ret.Failed()) return ret; + /* Create a new depot or find a depot to join to. */ + Depot *depot = nullptr; + CommandCost ret = FindJoiningDepot(ta, VEH_ROAD, join_to, depot, adjacent, flags); + if (ret.Failed()) return ret; - if (dir == GetRoadDepotDirection(tile)) return CommandCost(); + uint8_t num_new_depot_tiles = 0; + uint8_t num_rotated_depot_tiles = 0; - ret = EnsureNoVehicleOnGround(tile); - if (ret.Failed()) return ret; + CommandCost cost(EXPENSES_CONSTRUCTION); + for (Tile t : ta) { + if (IsBridgeAbove(t)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); - rotate_existing_depot = true; - } + Slope tileh = GetTileSlope(t); + if (tileh != SLOPE_FLAT) { + if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) { + return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + } + cost.AddCost(_price[PR_BUILD_FOUNDATION]); + } - if (!rotate_existing_depot) { - cost.AddCost(Command::Do(flags, tile)); - if (cost.Failed()) return cost; + /* Check whether a depot tile exists and it needs to be rotated. */ + if (IsRoadDepotTile(t) && + GetDepotIndex(t) == join_to && + (HasRoadTypeTram(t) ? rt == GetRoadTypeTram(t) : rt == GetRoadTypeRoad(t))) { + if (dir == GetRoadDepotDirection(t)) continue; - if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + ret = EnsureNoVehicleOnGround(t); + if (ret.Failed()) return ret; - if (!Depot::CanAllocateItem()) return CMD_ERROR; - } + num_rotated_depot_tiles++; + if (flags & DC_EXEC) { + SetRoadDepotExitDirection(t, dir); + MarkTileDirtyByTile(t); + } - if (flags & DC_EXEC) { - if (rotate_existing_depot) { - SetRoadDepotExitDirection(tile, dir); } else { - Depot *dep = new Depot(tile, VEH_ROAD, _current_company); - dep->build_date = TimerGameCalendar::date; - MakeRoadDepot(tile, _current_company, dep->index, dir, rt); - MakeDefaultName(dep); + cost.AddCost(Command::Do(flags, t)); + if (cost.Failed()) return cost; - /* A road depot has two road bits. */ - UpdateCompanyRoadInfrastructure(rt, _current_company, ROAD_DEPOT_TRACKBIT_FACTOR); + num_new_depot_tiles++; + if (flags & DC_EXEC) { + MakeRoadDepot(t, _current_company, depot->index, dir, rt); + MarkTileDirtyByTile(t); + } } + } - MarkTileDirtyByTile(tile); + if (num_new_depot_tiles + num_rotated_depot_tiles == 0) return CommandCost(); + + if (flags & DC_EXEC) { + /* A road depot has two road bits. */ + UpdateCompanyRoadInfrastructure(rt, _current_company, num_new_depot_tiles * ROAD_DEPOT_TRACKBIT_FACTOR); + + depot->AfterAddRemove(ta, true); + if (join_to == NEW_DEPOT) MakeDefaultName(depot); } - cost.AddCost(_price[PR_BUILD_DEPOT_ROAD]); + cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * (num_new_depot_tiles + num_rotated_depot_tiles)); return cost; } @@ -1215,7 +1228,8 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags) if (ret.Failed()) return ret; if (flags & DC_EXEC) { - Company *c = Company::GetIfValid(GetTileOwner(tile)); + Depot *depot = Depot::GetByTile(tile); + Company *c = Company::GetIfValid(depot->owner); if (c != nullptr) { /* A road depot has two road bits. */ RoadType rt = GetRoadTypeRoad(tile); @@ -1224,8 +1238,8 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags) DirtyCompanyInfrastructureWindows(c->index); } - delete Depot::GetByTile(tile); DoClearSquare(tile); + depot->AfterAddRemove(TileArea(tile), false); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); diff --git a/src/road_cmd.h b/src/road_cmd.h index 71883ddadac77..2ae7e170e69a9 100644 --- a/src/road_cmd.h +++ b/src/road_cmd.h @@ -13,6 +13,7 @@ #include "direction_type.h" #include "road_type.h" #include "command_type.h" +#include "depot_type.h" enum RoadStopClassID : uint16_t; @@ -22,7 +23,7 @@ void UpdateNearestTownForRoadTiles(bool invalidate); CommandCost CmdBuildLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, DisallowedRoadDirections drd, bool start_half, bool end_half, bool is_ai); std::tuple CmdRemoveLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, bool start_half, bool end_half); CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, RoadType rt, DisallowedRoadDirections toggle_drd, TownID town_id); -CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir); +CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile); CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RoadType to_type); DEF_CMD_TRAIT(CMD_BUILD_LONG_ROAD, CmdBuildLongRoad, CMD_AUTO | CMD_NO_WATER | CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION) @@ -33,7 +34,7 @@ DEF_CMD_TRAIT(CMD_CONVERT_ROAD, CmdConvertRoad, 0, CommandCallback CcPlaySound_CONSTRUCTION_OTHER; CommandCallback CcBuildRoadTunnel; -void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex tile, RoadType rt, DiagDirection dir); +void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex start_tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile); void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8_t width, uint8_t length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, RoadStopClassID spec_class, uint16_t spec_index, StationID, bool); #endif /* ROAD_CMD_H */ diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 14764444b8d25..53a71d0d2cbef 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -171,13 +171,17 @@ void ConnectRoadToStructure(TileIndex tile, DiagDirection direction) } } -void CcRoadDepot(Commands, const CommandCost &result, TileIndex tile, RoadType, DiagDirection dir) +void CcRoadDepot(Commands, const CommandCost &result, TileIndex start_tile, RoadType, DiagDirection dir, bool, DepotID, TileIndex end_tile) { if (result.Failed()) return; - if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile); + if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, start_tile); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); - ConnectRoadToStructure(tile, dir); + + TileArea ta(start_tile, end_tile); + for (TileIndex tile : ta) { + ConnectRoadToStructure(tile, dir); + } } /** @@ -638,8 +642,10 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_DEPOT: - Command::Post(this->rti->strings.err_depot, CcRoadDepot, - tile, _cur_roadtype, _road_depot_orientation); + CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD); + + VpSetPlaceSizingLimit(_settings_game.depot.depot_spread); + VpStartPlaceSizing(tile, (DiagDirToAxis(_road_depot_orientation) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_DEPOT); break; case WID_ROT_BUILD_WAYPOINT: @@ -810,6 +816,18 @@ struct BuildRoadToolbarWindow : Window { } break; + case DDSP_BUILD_DEPOT: { + bool adjacent = _ctrl_pressed; + StringID error_string = this->rti->strings.err_depot; + + auto proc = [=](DepotID join_to) -> bool { + return Command::Post(error_string, CcRoadDepot, start_tile, _cur_roadtype, _road_depot_orientation, adjacent, join_to, end_tile); + }; + + ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_ROAD); + break; + } + case DDSP_CONVERT_ROAD: Command::Post(rti->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype); break; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index beb641b9dfdb3..5c88f10c445a5 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -37,6 +37,7 @@ #include "framerate_type.h" #include "roadveh_cmd.h" #include "road_cmd.h" +#include "depot_base.h" #include "table/strings.h" @@ -250,6 +251,41 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length) v->vcache.cached_max_speed = (max_speed != 0) ? max_speed * 4 : RoadVehInfo(v->engine_type)->max_speed; } +/** + * Find an adequate tile for placing an engine. + * @param[in,out] tile A tile of the depot. + * @param e Engine to be built. + * @param flags Flags of the command. + * @return CommandCost() or an error message if the depot is not appropriate. + */ +CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, const Engine *e, DoCommandFlag flags) +{ + assert(IsRoadDepotTile(tile)); + + Depot *dep = Depot:: GetByTile(tile); + + /* Check that the vehicle can drive on some tile of the depot */ + RoadType rt = e->u.road.roadtype; + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + if ((dep->r_types.road_types & rti->powered_roadtypes) == 0) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE); + + /* Use same tile if possible when replacing. */ + if (flags & DC_AUTOREPLACE) { + /* Use same tile if possible when replacing. */ + if (HasTileAnyRoadType(tile, rti->powered_roadtypes)) return CommandCost(); + } + + tile = INVALID_TILE; + for (auto &depot_tile : dep->depot_tiles) { + if (!HasTileAnyRoadType(depot_tile, rti->powered_roadtypes)) continue; + tile = depot_tile; + break; + } + + assert(tile != INVALID_TILE); + return CommandCost(); +} + /** * Build a road vehicle. * @param flags type of operation. @@ -260,10 +296,12 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length) */ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret) { - /* Check that the vehicle can drive on the road in question */ + assert(IsRoadDepotTile(tile)); RoadType rt = e->u.road.roadtype; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); - if (!HasTileAnyRoadType(tile, rti->powered_roadtypes)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE); + + CommandCost check = FindDepotTileForPlacingEngine(tile, e, flags); + if (check.Failed()) return check; if (flags & DC_EXEC) { const RoadVehicleInfo *rvi = &e->u.road; diff --git a/src/script/api/script_marine.cpp b/src/script/api/script_marine.cpp index 6c04e1904630b..5e6d60a6c6b37 100644 --- a/src/script/api/script_marine.cpp +++ b/src/script/api/script_marine.cpp @@ -83,7 +83,7 @@ EnforcePrecondition(false, ::IsValidTile(front)); EnforcePrecondition(false, (::TileX(front) == ::TileX(tile)) != (::TileY(front) == ::TileY(tile))); - return ScriptObject::Command::Do(tile, ::TileX(front) == ::TileX(tile) ? AXIS_Y : AXIS_X); + return ScriptObject::Command::Do(tile, ::TileX(front) == ::TileX(tile) ? AXIS_Y : AXIS_X, false, INVALID_DEPOT, tile); } /* static */ bool ScriptMarine::BuildDock(TileIndex tile, StationID station_id) diff --git a/src/script/api/script_rail.cpp b/src/script/api/script_rail.cpp index ac1b9fc5a9b5d..5ac4a9b059da1 100644 --- a/src/script/api/script_rail.cpp +++ b/src/script/api/script_rail.cpp @@ -145,7 +145,7 @@ DiagDirection entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? DIAGDIR_SE : DIAGDIR_NW) : (::TileX(tile) < ::TileX(front) ? DIAGDIR_SW : DIAGDIR_NE); - return ScriptObject::Command::Do(tile, (::RailType)ScriptObject::GetRailType(), entrance_dir); + return ScriptObject::Command::Do(tile, (::RailType)ScriptObject::GetRailType(), entrance_dir, false, INVALID_DEPOT, tile); } /* static */ bool ScriptRail::BuildRailStation(TileIndex tile, RailTrack direction, SQInteger num_platforms, SQInteger platform_length, StationID station_id) diff --git a/src/script/api/script_road.cpp b/src/script/api/script_road.cpp index 25b7fc2631c98..9b29e2d4b9e38 100644 --- a/src/script/api/script_road.cpp +++ b/src/script/api/script_road.cpp @@ -535,7 +535,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD DiagDirection entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? DIAGDIR_SE : DIAGDIR_NW) : (::TileX(tile) < ::TileX(front) ? DIAGDIR_SW : DIAGDIR_NE); - return ScriptObject::Command::Do(tile, ScriptObject::GetRoadType(), entrance_dir); + return ScriptObject::Command::Do(tile, ScriptObject::GetRoadType(), entrance_dir, false, INVALID_DEPOT, tile); } /* static */ bool ScriptRoad::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id) diff --git a/src/train_gui.cpp b/src/train_gui.cpp index d62d3dce55abb..51ad7a9d0069b 100644 --- a/src/train_gui.cpp +++ b/src/train_gui.cpp @@ -15,6 +15,7 @@ #include "vehicle_func.h" #include "zoom_func.h" #include "train_cmd.h" +#include "depot_map.h" #include "table/strings.h" @@ -29,11 +30,12 @@ void CcBuildWagon(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray, TileIndex tile, EngineID, bool, CargoID, ClientID) { if (result.Failed()) return; + DepotID depot_id = GetDepotIndex(tile); /* find a locomotive in the depot. */ const Vehicle *found = nullptr; for (const Train *t : Train::Iterate()) { - if (t->IsFrontEngine() && t->tile == tile && t->IsStoppedInDepot()) { + if (t->IsFrontEngine() && t->IsStoppedInDepot() && GetDepotIndex(t->tile) == depot_id) { if (found != nullptr) return; // must be exactly one. found = t; } diff --git a/src/viewport.cpp b/src/viewport.cpp index 5e60dae16a758..6a9da5e44d023 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1013,7 +1013,7 @@ DepotID _viewport_highlight_depot = INVALID_DEPOT; ///< Currently selected depot static TileHighlightType GetTileHighlightType(TileIndex t) { if (_viewport_highlight_depot != INVALID_DEPOT) { - if (IsDepotTile(t) && GetDepotIndex(t) == _viewport_highlight_depot) return THT_WHITE; + if (IsDepotTile(t) && GetDepotIndex(t) == _viewport_highlight_depot) return THT_BLUE; } if (_viewport_highlight_station != nullptr) { diff --git a/src/viewport_type.h b/src/viewport_type.h index 4616bdfd9b9d3..6a305b2ebaef7 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -122,6 +122,7 @@ enum ViewportDragDropSelectionProcess { DDSP_PLANT_TREES, ///< Plant trees DDSP_BUILD_BRIDGE, ///< Bridge placement DDSP_BUILD_OBJECT, ///< Build an object + DDSP_BUILD_DEPOT, ///< Depot placement /* Rail specific actions */ DDSP_PLACE_RAIL, ///< Rail placement diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 2e8535fd6123b..c24cf6bd24877 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -94,66 +94,75 @@ static void MarkCanalsAndRiversAroundDirty(TileIndex tile) /** * Build a ship depot. * @param flags type of operation - * @param tile tile where ship depot is built + * @param tile first tile where ship depot is built * @param axis depot orientation (Axis) + * @param adjacent allow adjacent depots + * @param join_to depot to join to + * @param end_tile end tile of area to be built * @return the cost of this operation or an error */ -CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis) +CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis, bool adjacent, DepotID join_to, TileIndex end_tile) { if (!IsValidAxis(axis)) return CMD_ERROR; TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); - if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) { - return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); - } + TileArea complete_area(tile, end_tile); + complete_area.Add(tile2); + assert(complete_area.w == 2 || complete_area.h == 2); - if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + TileArea northern_tiles(complete_area.tile); + northern_tiles.Add(complete_area.tile + (axis == AXIS_X ? TileDiffXY(0, complete_area.h - 1) : TileDiffXY(complete_area.w - 1, 0))); - if (!IsTileFlat(tile) || !IsTileFlat(tile2)) { - /* Prevent depots on rapids */ - return_cmd_error(STR_ERROR_SITE_UNSUITABLE); - } + /* Create a new depot or find a depot to join to. */ + Depot *depot = nullptr; + CommandCost ret = FindJoiningDepot(complete_area, VEH_SHIP, join_to, depot, adjacent, flags); + if (ret.Failed()) return ret; - if (!Depot::CanAllocateItem()) return CMD_ERROR; + /* Get the cost of building all the ship depots. */ + CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP] * northern_tiles.w * northern_tiles.h); - WaterClass wc1 = GetWaterClass(tile); - WaterClass wc2 = GetWaterClass(tile2); - CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]); + /* Update infrastructure counts after the tile clears earlier. + * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total. + * See: MakeWaterKeepingClass() */ + uint new_water_infra = 0; - bool add_cost = !IsWaterTile(tile); - CommandCost ret = Command::Do(flags | DC_AUTO, tile); - if (ret.Failed()) return ret; - if (add_cost) { - cost.AddCost(ret); - } - add_cost = !IsWaterTile(tile2); - ret = Command::Do(flags | DC_AUTO, tile2); - if (ret.Failed()) return ret; - if (add_cost) { - cost.AddCost(ret); - } + for (TileIndex t : complete_area) { + /* Build water depots in water valid tiles... */ + if (!IsValidTile(t) || !HasTileWaterGround(t)) return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); - if (flags & DC_EXEC) { - Depot *depot = new Depot(tile, VEH_SHIP, _current_company); - depot->build_date = TimerGameCalendar::date; + /* ... with no bridges above... */ + if (IsBridgeAbove(t)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + + /* ... and preventing depots on rapids. */ + if (!IsTileFlat(t)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); - uint new_water_infra = 2 * LOCK_DEPOT_TILE_FACTOR; - /* Update infrastructure counts after the tile clears earlier. - * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total. - * See: MakeWaterKeepingClass() */ - if (wc1 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile) && GetWaterClass(tile) == WATER_CLASS_CANAL && IsTileOwner(tile, _current_company))) new_water_infra++; - if (wc2 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile2) && GetWaterClass(tile2) == WATER_CLASS_CANAL && IsTileOwner(tile2, _current_company))) new_water_infra++; + /* Keep original water class before clearing tile. */ + WaterClass wc = GetWaterClass(t); - Company::Get(_current_company)->infrastructure.water += new_water_infra; + /* Clear the tile. */ + bool add_cost = !IsWaterTile(t); + CommandCost ret = Command::Do(flags | DC_AUTO, t); + if (ret.Failed()) return ret; + if (add_cost) cost.AddCost(ret); + + if (wc == WATER_CLASS_CANAL && !(HasTileWaterClass(t) && GetWaterClass(t) == WATER_CLASS_CANAL && IsTileOwner(t, _current_company))) new_water_infra++; + + if (flags & DC_EXEC) { + DepotPart dp = northern_tiles.Contains(t) ? DEPOT_PART_NORTH : DEPOT_PART_SOUTH; + MakeShipDepot(t, _current_company, depot->index, dp, axis, wc); + CheckForDockingTile(t); + MarkTileDirtyByTile(t); + } + } + + if (flags & DC_EXEC) { + Company::Get(_current_company)->infrastructure.water += new_water_infra + + complete_area.w * complete_area.h * LOCK_DEPOT_TILE_FACTOR; DirtyCompanyInfrastructureWindows(_current_company); - MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1); - MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2); - CheckForDockingTile(tile); - CheckForDockingTile(tile2); - MarkTileDirtyByTile(tile); - MarkTileDirtyByTile(tile2); MakeDefaultName(depot); + depot->AfterAddRemove(complete_area, true); + if (join_to == NEW_DEPOT) MakeDefaultName(depot); } return cost; @@ -275,9 +284,8 @@ static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags) bool do_clear = (flags & DC_FORCE_CLEAR_TILE) != 0; if (flags & DC_EXEC) { - delete Depot::GetByTile(tile); - - Company *c = Company::GetIfValid(GetTileOwner(tile)); + Depot *depot = Depot::GetByTile(tile); + Company *c = Company::GetIfValid(depot->owner); if (c != nullptr) { c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR; if (do_clear && GetWaterClass(tile) == WATER_CLASS_CANAL) c->infrastructure.water--; @@ -286,6 +294,8 @@ static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags) if (!do_clear) MakeWaterKeepingClass(tile, GetTileOwner(tile)); MakeWaterKeepingClass(tile2, GetTileOwner(tile2)); + + depot->AfterAddRemove(TileArea(tile, tile2), false); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]); diff --git a/src/water_cmd.h b/src/water_cmd.h index 1c56790327dea..60e00d4a4cdf8 100644 --- a/src/water_cmd.h +++ b/src/water_cmd.h @@ -13,7 +13,7 @@ #include "command_type.h" #include "water_map.h" -CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis); +CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis, bool adjacent, DepotID depot_id, TileIndex end_tile); CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, WaterClass wc, bool diagonal); CommandCost CmdBuildLock(DoCommandFlag flags, TileIndex tile); From 2ec7b8ec14cea9453a40788fd3dc5c1ee6a5614b Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 17:47:22 +0200 Subject: [PATCH 013/143] Add: Allow removing company rail depots in an area. --- src/command_type.h | 1 + src/lang/english.txt | 1 + src/rail_cmd.cpp | 26 ++++++++++++++++++++++++++ src/rail_cmd.h | 2 ++ src/rail_gui.cpp | 31 ++++++++++++++++++++----------- src/script/api/script_rail.cpp | 9 +++++++++ src/script/api/script_rail.hpp | 12 ++++++++++++ src/viewport_type.h | 1 + 8 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/command_type.h b/src/command_type.h index 2fd494b5cac06..467c0fc2b2634 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -193,6 +193,7 @@ enum Commands : uint16_t { CMD_BUILD_BRIDGE, ///< build a bridge CMD_BUILD_RAIL_STATION, ///< build a rail station CMD_BUILD_TRAIN_DEPOT, ///< build a train depot + CMD_REMOVE_TRAIN_DEPOT, ///< remove a train depot CMD_BUILD_SINGLE_SIGNAL, ///< build a signal CMD_REMOVE_SINGLE_SIGNAL, ///< remove a signal CMD_TERRAFORM_LAND, ///< terraform a tile diff --git a/src/lang/english.txt b/src/lang/english.txt index f30e938945185..31eb43322dcff 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5123,6 +5123,7 @@ STR_ERROR_BUOY_IS_IN_USE :{WHITE}... buoy # Depot related errors STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT :{WHITE}Can't build train depot here... +STR_ERROR_CAN_T_REMOVE_TRAIN_DEPOT :{WHITE}Can't remove train depot here... STR_ERROR_CAN_T_BUILD_ROAD_DEPOT :{WHITE}Can't build road vehicle depot here... STR_ERROR_CAN_T_BUILD_TRAM_DEPOT :{WHITE}Can't build tram vehicle depot here... STR_ERROR_CAN_T_BUILD_SHIP_DEPOT :{WHITE}Can't build ship depot here... diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 89554c655c342..b2172560b5413 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1785,6 +1785,8 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) { + assert(IsRailDepotTile(tile)); + if (_current_company != OWNER_WATER) { CommandCost ret = CheckTileOwnership(tile); if (ret.Failed()) return ret; @@ -1822,6 +1824,30 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]); } +/** + * Remove train depots from an area + * @param flags operation to perform + * @param start_tile start tile of the area + * @param end_tile end tile of the area + * @return the cost of this operation or an error + */ +CommandCost CmdRemoveTrainDepot(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile) +{ + assert(IsValidTile(start_tile)); + assert(IsValidTile(end_tile)); + + CommandCost cost(EXPENSES_CONSTRUCTION); + TileArea ta(start_tile, end_tile); + for (TileIndex t : ta) { + if (!IsRailDepotTile(t)) continue; + CommandCost ret = RemoveTrainDepot(t, flags); + if (ret.Failed()) return ret; + cost.AddCost(ret); + } + + return cost; +} + static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags) { CommandCost cost(EXPENSES_CONSTRUCTION); diff --git a/src/rail_cmd.h b/src/rail_cmd.h index ce10c4fd7783b..775bfa7ed7653 100644 --- a/src/rail_cmd.h +++ b/src/rail_cmd.h @@ -20,6 +20,7 @@ CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, Tile CommandCost CmdBuildSingleRail(DoCommandFlag flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals); CommandCost CmdRemoveSingleRail(DoCommandFlag flags, TileIndex tile, Track track); CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, DepotID depot_id, TileIndex end_tile); +CommandCost CmdRemoveTrainDepot(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile); CommandCost CmdBuildSingleSignal(DoCommandFlag flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy); CommandCost CmdRemoveSingleSignal(DoCommandFlag flags, TileIndex tile, Track track); CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal); @@ -31,6 +32,7 @@ DEF_CMD_TRAIT(CMD_REMOVE_RAILROAD_TRACK, CmdRemoveRailroadTrack, CMD_AUTO, DEF_CMD_TRAIT(CMD_BUILD_SINGLE_RAIL, CmdBuildSingleRail, CMD_AUTO | CMD_NO_WATER, CMDT_LANDSCAPE_CONSTRUCTION) DEF_CMD_TRAIT(CMD_REMOVE_SINGLE_RAIL, CmdRemoveSingleRail, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION) DEF_CMD_TRAIT(CMD_BUILD_TRAIN_DEPOT, CmdBuildTrainDepot, CMD_AUTO | CMD_NO_WATER, CMDT_LANDSCAPE_CONSTRUCTION) +DEF_CMD_TRAIT(CMD_REMOVE_TRAIN_DEPOT, CmdRemoveTrainDepot, CMD_AUTO | CMD_NO_WATER, CMDT_LANDSCAPE_CONSTRUCTION) DEF_CMD_TRAIT(CMD_BUILD_SINGLE_SIGNAL, CmdBuildSingleSignal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION) DEF_CMD_TRAIT(CMD_REMOVE_SINGLE_SIGNAL, CmdRemoveSingleSignal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION) DEF_CMD_TRAIT(CMD_CONVERT_RAIL, CmdConvertRail, 0, CMDT_LANDSCAPE_CONSTRUCTION) diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 271700e8c88d4..226691f8ecc51 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -320,6 +320,7 @@ void CcBuildRailTunnel(Commands, const CommandCost &result, TileIndex tile) static void ToggleRailButton_Remove(Window *w) { CloseWindowById(WC_SELECT_STATION, 0); + CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN); w->ToggleWidgetLoweredState(WID_RAT_REMOVE); w->SetWidgetDirty(WID_RAT_REMOVE); _remove_button_clicked = w->IsWidgetLowered(WID_RAT_REMOVE); @@ -336,8 +337,9 @@ static bool RailToolbar_CtrlChanged(Window *w) if (w->IsWidgetDisabled(WID_RAT_REMOVE)) return false; /* allow ctrl to switch remove mode only for these widgets */ + for (WidgetID i = WID_RAT_BUILD_NS; i <= WID_RAT_BUILD_STATION; i++) { - if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_WAYPOINT) && w->IsWidgetLowered(i)) { + if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_DEPOT) && w->IsWidgetLowered(i)) { ToggleRailButton_Remove(w); return true; } @@ -537,6 +539,7 @@ struct BuildRailToolbarWindow : Window { case WID_RAT_BUILD_EW: case WID_RAT_BUILD_Y: case WID_RAT_AUTORAIL: + case WID_RAT_BUILD_DEPOT: case WID_RAT_BUILD_WAYPOINT: case WID_RAT_BUILD_STATION: case WID_RAT_BUILD_SIGNALS: @@ -696,7 +699,7 @@ struct BuildRailToolbarWindow : Window { CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN); ViewportPlaceMethod vpm = (DiagDirToAxis(_build_depot_direction) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED; - VpStartPlaceSizing(tile, vpm, DDSP_BUILD_DEPOT); + VpStartPlaceSizing(tile, vpm, _remove_button_clicked ? DDSP_REMOVE_DEPOT : DDSP_BUILD_DEPOT); VpSetPlaceSizingLimit(_settings_game.depot.depot_spread); break; } @@ -796,16 +799,19 @@ struct BuildRailToolbarWindow : Window { } break; - case DDSP_BUILD_DEPOT: { - bool adjacent = _ctrl_pressed; + case DDSP_BUILD_DEPOT: + if (_remove_button_clicked) { + Command::Post(STR_ERROR_CAN_T_REMOVE_TRAIN_DEPOT, CcPlaySound_CONSTRUCTION_RAIL, start_tile, end_tile); + } else { + bool adjacent = _ctrl_pressed; - auto proc = [=](DepotID join_to) -> bool { - return Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, start_tile, _cur_railtype, _build_depot_direction, adjacent, join_to, end_tile); - }; + auto proc = [=](DepotID join_to) -> bool { + return Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, start_tile, _cur_railtype, _build_depot_direction, adjacent, join_to, end_tile); + }; - ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_TRAIN); + ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_TRAIN); + } break; - } } } } @@ -838,8 +844,11 @@ struct BuildRailToolbarWindow : Window { EventState OnCTRLStateChange() override { - /* do not toggle Remove button by Ctrl when placing station */ - if (!this->IsWidgetLowered(WID_RAT_BUILD_STATION) && !this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT) && RailToolbar_CtrlChanged(this)) return ES_HANDLED; + /* do not toggle Remove button by Ctrl when placing station or depot */ + if (!this->IsWidgetLowered(WID_RAT_BUILD_STATION) && + !this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT) && + !this->IsWidgetLowered(WID_RAT_BUILD_DEPOT) && + RailToolbar_CtrlChanged(this)) return ES_HANDLED; return ES_NOT_HANDLED; } diff --git a/src/script/api/script_rail.cpp b/src/script/api/script_rail.cpp index 5ac4a9b059da1..f0c79fcff927b 100644 --- a/src/script/api/script_rail.cpp +++ b/src/script/api/script_rail.cpp @@ -148,6 +148,15 @@ return ScriptObject::Command::Do(tile, (::RailType)ScriptObject::GetRailType(), entrance_dir, false, INVALID_DEPOT, tile); } +/* static */ bool ScriptRail::RemoveRailDepot(TileIndex start_tile, TileIndex end_tile) +{ + EnforceCompanyModeValid(false); + EnforcePrecondition(false, ::IsValidTile(start_tile)); + EnforcePrecondition(false, ::IsValidTile(end_tile)); + + return ScriptObject::Command::Do(start_tile, end_tile); +} + /* static */ bool ScriptRail::BuildRailStation(TileIndex tile, RailTrack direction, SQInteger num_platforms, SQInteger platform_length, StationID station_id) { EnforceCompanyModeValid(false); diff --git a/src/script/api/script_rail.hpp b/src/script/api/script_rail.hpp index 2a55a72048fb3..71e5e8032f5df 100644 --- a/src/script/api/script_rail.hpp +++ b/src/script/api/script_rail.hpp @@ -240,6 +240,18 @@ class ScriptRail : public ScriptObject { */ static bool BuildRailDepot(TileIndex tile, TileIndex front); + /** + * Removes rail depots from an area. + * @param start_tile Start tile of the area. + * @param end_tile End tile of the area. + * @pre ScriptMap::IsValidTile(start_tile). + * @pre ScriptMap::IsValidTile(end_tile). + * @game @pre Valid ScriptCompanyMode active in scope. + * @exception ScriptError::ERR_FLAT_LAND_REQUIRED + * @return Whether all depot tiles of the owner in the area have been/can be cleared or not. + */ + static bool RemoveRailDepot(TileIndex start_tile, TileIndex end_tile); + /** * Build a rail station. * @param tile Place to build the station. diff --git a/src/viewport_type.h b/src/viewport_type.h index 6a305b2ebaef7..e5ef362fa9c9e 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -123,6 +123,7 @@ enum ViewportDragDropSelectionProcess { DDSP_BUILD_BRIDGE, ///< Bridge placement DDSP_BUILD_OBJECT, ///< Build an object DDSP_BUILD_DEPOT, ///< Depot placement + DDSP_REMOVE_DEPOT, ///< Depot removal /* Rail specific actions */ DDSP_PLACE_RAIL, ///< Rail placement From 0b718d5daf723d7555be16348021971ff95e6408 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 4 Mar 2023 10:27:39 +0100 Subject: [PATCH 014/143] Add: Adapt pathfinding in YAPF and NPF for depots. --- src/pathfinder/pathfinder_func.h | 32 +++++++++++++++++++++++++++ src/pathfinder/yapf/yapf_destrail.hpp | 18 ++++++++++++++- src/pathfinder/yapf/yapf_road.cpp | 13 +++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/pathfinder/pathfinder_func.h b/src/pathfinder/pathfinder_func.h index 444b100ce7b0a..781d35645895a 100644 --- a/src/pathfinder/pathfinder_func.h +++ b/src/pathfinder/pathfinder_func.h @@ -12,6 +12,7 @@ #include "../tile_cmd.h" #include "../waypoint_base.h" +#include "../depot_base.h" /** * Calculates the tile of given station that is closest to a given tile @@ -47,6 +48,37 @@ inline TileIndex CalcClosestStationTile(StationID station, TileIndex tile, Stati return TileXY(x, y); } +/** + * Calculates the tile of a depot that is closest to a given tile. + * @param depot_id The depot to calculate the distance to. + * @param tile The tile from where to calculate the distance. + * @return The closest depot tile to the given tile. + */ +static inline TileIndex CalcClosestDepotTile(DepotID depot_id, TileIndex tile) +{ + assert(Depot::IsValidID(depot_id)); + const Depot *dep = Depot::Get(depot_id); + + /* If tile area is empty, use the xy tile. */ + if (dep->ta.tile == INVALID_TILE) { + assert(dep->xy != INVALID_TILE); + return dep->xy; + } + + TileIndex best_tile = INVALID_TILE; + uint best_distance = UINT_MAX; + + for (auto const &depot_tile : dep->depot_tiles) { + uint new_distance = DistanceManhattan(depot_tile, tile); + if (new_distance < best_distance) { + best_tile = depot_tile; + best_distance = new_distance; + } + } + + return best_tile; +} + /** * Wrapper around GetTileTrackStatus() and TrackStatusToTrackdirBits(), as for * single tram bits GetTileTrackStatus() returns 0. The reason for this is diff --git a/src/pathfinder/yapf/yapf_destrail.hpp b/src/pathfinder/yapf/yapf_destrail.hpp index f39a8a2c4b40e..f6b871ea2c656 100644 --- a/src/pathfinder/yapf/yapf_destrail.hpp +++ b/src/pathfinder/yapf/yapf_destrail.hpp @@ -118,6 +118,7 @@ class CYapfDestinationTileOrStationRailT : public CYapfDestinationRailBase { TileIndex m_destTile; TrackdirBits m_destTrackdirs; StationID m_dest_station_id; + DepotID m_dest_depot_id; bool m_any_depot; /** to access inherited path finder */ @@ -149,14 +150,25 @@ class CYapfDestinationTileOrStationRailT : public CYapfDestinationRailBase { break; case OT_GOTO_DEPOT: + m_dest_station_id = INVALID_STATION; + if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) { m_any_depot = true; + m_dest_depot_id = INVALID_DEPOT; + m_destTile = v->dest_tile; + m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_RAIL, 0)); + } else { + m_dest_depot_id = v->current_order.GetDestination(); + assert(Depot::IsValidID(m_dest_depot_id)); + m_destTile = CalcClosestDepotTile(m_dest_depot_id, v->tile); + m_destTrackdirs = INVALID_TRACKDIR_BIT; } - [[fallthrough]]; + break; default: m_destTile = v->dest_tile; m_dest_station_id = INVALID_STATION; + m_dest_depot_id = INVALID_DEPOT; m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_RAIL, 0)); break; } @@ -176,6 +188,10 @@ class CYapfDestinationTileOrStationRailT : public CYapfDestinationRailBase { return HasStationTileRail(tile) && (GetStationIndex(tile) == m_dest_station_id) && (GetRailStationTrack(tile) == TrackdirToTrack(td)); + } else if (m_dest_depot_id != INVALID_DEPOT) { + return IsRailDepotTile(tile) + && (GetDepotIndex(tile) == m_dest_depot_id) + && (GetRailDepotTrack(tile) == TrackdirToTrack(td)); } if (m_any_depot) { diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index 209b64b52afe4..910751f227c92 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -234,7 +234,9 @@ class CYapfDestinationTileRoadT TileIndex m_destTile; TrackdirBits m_destTrackdirs; StationID m_dest_station; + DepotID m_dest_depot; StationType m_station_type; + bool m_bus; bool m_non_artic; public: @@ -252,8 +254,14 @@ class CYapfDestinationTileRoadT m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_station_type); m_non_artic = !v->HasArticulatedPart(); m_destTrackdirs = INVALID_TRACKDIR_BIT; + } else if (v->current_order.IsType(OT_GOTO_DEPOT)) { + m_dest_station = INVALID_STATION; + m_dest_depot = v->current_order.GetDestination(); + m_destTile = CalcClosestDepotTile(m_dest_depot, v->tile); + m_destTrackdirs = INVALID_TRACKDIR_BIT; } else { m_dest_station = INVALID_STATION; + m_dest_depot = INVALID_DEPOT; m_destTile = v->dest_tile; m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype))); } @@ -287,6 +295,11 @@ class CYapfDestinationTileRoadT (m_non_artic || IsDriveThroughStopTile(tile)); } + if (m_dest_depot != INVALID_DEPOT) { + return IsRoadDepotTile(tile) && + GetDepotIndex(tile) == m_dest_depot; + } + return tile == m_destTile && HasTrackdir(m_destTrackdirs, trackdir); } From ef6844931b990369fbc860f759335f2f160bf705 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 10 May 2023 20:22:04 +0200 Subject: [PATCH 015/143] Codechange: Adapt some functions that located the depot with its tile. --- src/autoreplace_cmd.cpp | 4 ++ src/depot.cpp | 33 +++++++++++++++ src/depot_base.h | 3 ++ src/lang/english.txt | 1 + src/order_cmd.cpp | 2 +- src/roadveh_cmd.cpp | 4 +- src/ship_cmd.cpp | 18 ++++---- src/train.h | 2 + src/train_cmd.cpp | 94 +++++++++++++++++++++++++++++++++++------ 9 files changed, 137 insertions(+), 24 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 757f18d371da3..026fe129cbe52 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -519,6 +519,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon { Vehicle *old_head = *chain; assert(old_head->IsPrimaryVehicle()); + TileIndex tile = old_head->tile; CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0); @@ -661,6 +662,9 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon if ((flags & DC_EXEC) != 0) CheckCargoCapacity(new_head); } + assert(IsValidTile(tile)); + if (!HasCompatibleDepotTile(tile, Train::From(new_head))) cost.MakeError(STR_ERROR_UNABLE_TO_FIND_APPROPRIATE_DEPOT_TILE); + /* If we are not in DC_EXEC undo everything, i.e. rearrange old vehicles. * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time. * Note: The vehicle attach callback is disabled here :) */ diff --git a/src/depot.cpp b/src/depot.cpp index 542abc5f6208f..0ba6ee3cd3d7e 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -16,6 +16,7 @@ #include "vehicle_gui.h" #include "vehiclelist.h" #include "command_func.h" +#include "vehicle_base.h" #include "safeguards.h" @@ -41,6 +42,15 @@ Depot::~Depot() /* Clear the order backup. */ OrderBackup::Reset(this->index, false); + /* Make sure no vehicle is going to the old depot. */ + for (Vehicle *v : Vehicle::Iterate()) { + if (v->First() != v) continue; + if (!v->current_order.IsType(OT_GOTO_DEPOT)) continue; + if (v->current_order.GetDestination() != this->index) continue; + + v->current_order.MakeDummy(); + } + /* Clear the depot from all order-lists */ RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->index); @@ -53,6 +63,29 @@ Depot::~Depot() this->veh_type, this->owner, this->index).Pack()); } +/** + * Of all the depot parts a depot has, return the best destination for a vehicle. + * @param v The vehicle. + * @param dep The depot vehicle \a v is heading for. + * @return The closest part of depot to vehicle v. + */ +TileIndex Depot::GetBestDepotTile(Vehicle *v) const +{ + assert(this->veh_type == v->type); + TileIndex best_depot = INVALID_TILE; + uint best_distance = UINT_MAX; + + for (const auto &tile : this->depot_tiles) { + uint new_distance = DistanceManhattan(v->tile, tile); + if (new_distance < best_distance) { + best_depot = tile; + best_distance = new_distance; + } + } + + return best_depot; +} + /** * Check we can add some tiles to this depot. * @param ta The affected tile area. diff --git a/src/depot_base.h b/src/depot_base.h index 9c8a732afd2b8..41512153bf137 100644 --- a/src/depot_base.h +++ b/src/depot_base.h @@ -28,6 +28,7 @@ typedef Pool DepotPool; extern DepotPool _depot_pool; class CommandCost; +struct Vehicle; struct Depot : DepotPool::PoolItem<&_depot_pool> { /* DepotID index member of DepotPool is 2 bytes. */ @@ -64,6 +65,8 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> { return Depot::Get(GetDepotIndex(tile)); } + TileIndex GetBestDepotTile(Vehicle *v) const; + /** * Is the "type" of depot the same as the given depot, * i.e. are both a rail, road or ship depots? diff --git a/src/lang/english.txt b/src/lang/english.txt index 31eb43322dcff..2b251064e4c3a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5146,6 +5146,7 @@ STR_ERROR_CAN_T_MOVE_VEHICLE :{WHITE}Can't mo STR_ERROR_REAR_ENGINE_FOLLOW_FRONT :{WHITE}The rear engine will always follow its front counterpart STR_ERROR_UNABLE_TO_FIND_ROUTE_TO :{WHITE}Unable to find route to local depot STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT :{WHITE}Unable to find local depot +STR_ERROR_UNABLE_TO_FIND_APPROPRIATE_DEPOT_TILE :{WHITE}Unable to find appropriate depot tile STR_ERROR_DEPOT_TOO_SPREAD_OUT :{WHITE}... depot too spread out STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Wrong depot type diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 9252e3d516e07..203d7784f24c7 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -2033,7 +2033,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool v->IncrementRealOrderIndex(); } else { if (v->type != VEH_AIRCRAFT) { - v->SetDestTile(Depot::Get(order->GetDestination())->xy); + v->SetDestTile(Depot::Get(order->GetDestination())->GetBestDepotTile(v)); } else { Aircraft *a = Aircraft::From(v); DestinationID destination_depot = a->current_order.GetDestination(); diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 5c88f10c445a5..96adeb23abf7c 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -1056,7 +1056,9 @@ bool RoadVehLeaveDepot(RoadVehicle *v, bool first) if (first) { /* We are leaving a depot, but have to go to the exact same one; re-enter */ - if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) { + if (v->current_order.IsType(OT_GOTO_DEPOT) && + IsRoadDepotTile(v->tile) && + v->current_order.GetDestination() == GetDepotIndex(v->tile)) { VehicleEnterDepot(v); return true; } diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 8577169c3269a..498817772dcc1 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -187,14 +187,14 @@ 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; + const TileIndex tile = depot->xy; - if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) { - const uint dist_sq = DistanceSquare(tile, v->tile); - if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance && - visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) { - best_dist_sq = dist_sq; - best_depot = depot; - } + const uint dist_sq = DistanceSquare(tile, v->tile); + if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance && + visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) { + best_dist_sq = dist_sq; + best_depot = depot; } } @@ -222,7 +222,7 @@ static void CheckIfShipNeedsService(Vehicle *v) } v->current_order.MakeGoToDepot(depot->index, ODTFB_SERVICE); - v->SetDestTile(depot->xy); + v->SetDestTile(depot->GetBestDepotTile(v)); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); } @@ -962,5 +962,5 @@ ClosestDepot Ship::FindClosestDepot() const Depot *depot = FindClosestShipDepot(this, MAX_SHIP_DEPOT_SEARCH_DISTANCE); if (depot == nullptr) return ClosestDepot(); - return ClosestDepot(depot->xy, depot->index); + return ClosestDepot(depot->GetBestDepotTile(this), depot->index); } diff --git a/src/train.h b/src/train.h index bbf1e04365e0b..be4307307cd3c 100644 --- a/src/train.h +++ b/src/train.h @@ -353,4 +353,6 @@ struct Train final : public GroundVehicle { } }; +bool HasCompatibleDepotTile(TileIndex tile, const Train *t); + #endif /* TRAIN_H */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 9b77dddd04d91..d6c65140cadde 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -7,6 +7,7 @@ /** @file train_cmd.cpp Handling of trains. */ +#include "depot_map.h" #include "stdafx.h" #include "error.h" #include "articulated_vehicles.h" @@ -38,6 +39,7 @@ #include "misc_cmd.h" #include "timer/timer_game_calendar.h" #include "timer/timer_game_economy.h" +#include "depot_base.h" #include "table/strings.h" #include "table/train_sprites.h" @@ -604,6 +606,65 @@ void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, } } + +/** + * Check if a train chain is compatible with a depot tile. + * @param tile Tile to check. + * @param t Train chain to check. + * @return Whether the full train chain is compatible with this tile. + */ +bool IsVehicleCompatibleWithDepotTile(TileIndex tile, const Train *t) +{ + assert(IsRailDepotTile(tile)); + for (const Train *u = t; u != nullptr; u = u->Next()) { + RailType rail_type = Engine::Get(u->engine_type)->u.rail.railtype; + if (!IsCompatibleRail(rail_type, GetRailType(tile))) return false; + } + + return true; +} + +/** + * Check if a depot has a tile where a train chain can be stored. + * @param tile A tile of the depot. + * @param t The train to check. + * @return True iff the depot has a tile compatible with the chain. + */ +bool HasCompatibleDepotTile(TileIndex tile, const Train *t) +{ + assert(IsRailDepotTile(tile)); + Depot *dep = Depot::GetByTile(tile); + + for (auto &depot_tile : dep->depot_tiles) { + if (IsVehicleCompatibleWithDepotTile(depot_tile, t)) return true; + } + + return false; +} + +/** + * Find a tile of a depot compatible with the rail type of a rail vehicle. + * @param depot_id Index of the depot. + * @param rail_type Rail type of the new vehicle. + * @param is_engine Whether the vehicle is an engine. + * @return A compatible tile of the depot or INVALID_TILE if no compatible tile is found. + */ +TileIndex FindCompatibleDepotTile(DepotID depot_id, RailType rail_type, bool is_engine) +{ + assert(Depot::IsValidID(depot_id)); + Depot *depot = Depot::Get(depot_id); + + for (auto &dep_tile : depot->depot_tiles) { + if (is_engine) { + if (HasPowerOnRail(rail_type, GetRailType(dep_tile))) return dep_tile; + } else { + if (IsCompatibleRail(rail_type, GetRailType(dep_tile))) return dep_tile; + } + } + + return INVALID_TILE; +} + /** * Build a railroad wagon. * @param flags type of operation. @@ -616,9 +677,11 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const { const RailVehicleInfo *rvi = &e->u.rail; assert(IsRailDepotTile(tile)); + DepotID depot_id = GetDepotIndex(tile); - /* Check that the wagon can drive on the track in question */ - if (!IsCompatibleRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR; + /* Find a good tile to place the wagon. */ + tile = FindCompatibleDepotTile(depot_id, rvi->railtype, false); + if (tile == INVALID_TILE) return CMD_ERROR; if (flags & DC_EXEC) { Train *v = new Train(); @@ -646,7 +709,7 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const v->SetWagon(); v->SetFreeWagon(); - InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(tile)); + InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); v->cargo_type = e->GetDefaultCargoType(); assert(IsValidCargoID(v->cargo_type)); @@ -674,7 +737,8 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const /* Try to connect the vehicle to one of free chains of wagons. */ for (Train *w : Train::Iterate()) { - if (w->tile == tile && ///< Same depot + if (!IsRailDepotTile(w->tile)) continue; + if (GetDepotIndex(w->tile) == depot_id && ///< Same depot w->IsFreeWagon() && ///< A free wagon chain w->engine_type == e->index && ///< Same type w->First() != v && ///< Don't connect to ourself @@ -693,9 +757,10 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const void NormalizeTrainVehInDepot(const Train *u) { assert(u->IsEngine()); + DepotID dep_id = GetDepotIndex(u->tile); for (const Train *v : Train::Iterate()) { - if (v->IsFreeWagon() && v->tile == u->tile && - v->track == TRACK_BIT_DEPOT) { + if (v->IsFreeWagon() && v->IsInDepot() && + GetDepotIndex(v->tile) == dep_id) { if (Command::Do(DC_EXEC, v->index, u->index, true).Failed()) { break; } @@ -749,13 +814,14 @@ static void AddRearEngineToMultiheadedTrain(Train *v) */ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret) { + assert(IsRailDepotTile(tile)); const RailVehicleInfo *rvi = &e->u.rail; if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret); - /* Check if depot and new engine uses the same kind of tracks * - * We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */ - if (!HasPowerOnRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR; + /* Find a good tile to place the engine and get power on it. */ + tile = FindCompatibleDepotTile(GetDepotIndex(tile), rvi->railtype, true); + if (tile == INVALID_TILE) return CMD_ERROR; if (flags & DC_EXEC) { DiagDirection dir = GetRailDepotDirection(tile); @@ -825,10 +891,10 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin static Train *FindGoodVehiclePos(const Train *src) { EngineID eng = src->engine_type; - TileIndex tile = src->tile; + DepotID dep_id = GetDepotIndex(src->tile); for (Train *dst : Train::Iterate()) { - if (dst->IsFreeWagon() && dst->tile == tile && !(dst->vehstatus & VS_CRASHED)) { + if (dst->IsFreeWagon() && !(dst->vehstatus & VS_CRASHED) && GetDepotIndex(dst->tile) == dep_id) { /* check so all vehicles in the line have the same engine. */ Train *t = dst; while (t->engine_type == eng) { @@ -1230,7 +1296,7 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID Train *dst_head; if (dst != nullptr) { dst_head = dst->First(); - if (dst_head->tile != src_head->tile) return CMD_ERROR; + if (GetDepotIndex(dst_head->tile) != GetDepotIndex(src_head->tile)) return CMD_ERROR; /* Now deal with articulated part of destination wagon */ dst = dst->GetLastEnginePart(); } else { @@ -2303,7 +2369,9 @@ static bool CheckTrainStayInDepot(Train *v) } /* We are leaving a depot, but have to go to the exact same one; re-enter. */ - if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) { + if (v->current_order.IsType(OT_GOTO_DEPOT) && + IsRailDepotTile(v->tile) && + v->current_order.GetDestination() == GetDepotIndex(v->tile)) { /* Service when depot has no reservation. */ if (!HasDepotReservation(v->tile)) VehicleEnterDepot(v); return true; From b171ab6d61f0bcf3fcb855c9196f247c534e5208 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 1 Apr 2021 21:19:02 +0200 Subject: [PATCH 016/143] Codechange: Add additional rules for placing trains in depots according to railtypes. --- src/lang/english.txt | 1 + src/train_cmd.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 2b251064e4c3a..36f555620ad8a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5138,6 +5138,7 @@ STR_ERROR_AIRCRAFT_MUST_BE_STOPPED_INSIDE_HANGAR :{WHITE}... must STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT :{WHITE}Trains can only be altered when stopped inside a depot STR_ERROR_TRAIN_TOO_LONG :{WHITE}Train too long +STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT :{WHITE}Train chain is incompatible with any tile of this depot STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE :{WHITE}Can't reverse direction of vehicle... STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS :{WHITE}... consists of multiple units STR_ERROR_INCOMPATIBLE_RAIL_TYPES :Incompatible rail types diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index d6c65140cadde..52afef664f785 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1051,6 +1051,82 @@ static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *origina return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME); } +/** + * Check if a train can be placed in a depot tile. + * @param train The train. + * @param tile The tile to check whether it is possible to place the train. + * @return whether it found a depot tile in which to place the train. + */ +bool CheckPlacement(const Train *train, TileIndex tile) +{ + assert(train != nullptr); + assert(IsRailDepotTile(tile)); + + RailType rt = GetRailType(tile); + for (const Train *t = train; t != nullptr; t = t->Next()) { + RailType rail_type = Engine::Get(t->engine_type)->u.rail.railtype; + if (!IsCompatibleRail(rail_type, rt)) return false; + } + + return true; +} + +/** + * Find a valid tile before placing a train in the depot. + * @param t The train to place in a rail depot tile. + * @return a compatible tile, if any, preferabily the one the first vehicle is or INVALID_TILE if none found. + */ +TileIndex LookForTileInDepot(const Train *train) +{ + assert(train != nullptr); + assert(IsRailDepotTile(train->tile)); + TileIndex best_tile = INVALID_TILE; + + /* First candidate is the original position of the train. */ + if (CheckPlacement(train, train->tile)) { + if (HasPowerOnRail(train->railtype, GetRailType(train->tile))) return train->tile; + best_tile = train->tile; + } + + /* Check all depot tiles. */ + Depot *depot = Depot::GetByTile(train->tile); + for (std::vector::iterator it = depot->depot_tiles.begin(); it != depot->depot_tiles.end(); ++it) { + if (CheckPlacement(train, *it)) { + if (HasPowerOnRail(train->railtype, GetRailType(*it))) return *it; + if (best_tile == INVALID_TILE) best_tile = *it; + } + } + + return best_tile; +} + +/** + * Find an appropriate depot tile for a train and place + * all the vehicle chain in the same depot tile. + * @param train The train to place. + */ +void PlaceOnRailDepot(Train *train) +{ + assert(train->First() == train); + + TileIndex depot_tile = LookForTileInDepot(train); + assert(depot_tile != INVALID_TILE); + + DiagDirection diag_dir = GetRailDepotDirection(depot_tile); + int x = TileX(depot_tile) * TILE_SIZE + _vehicle_initial_x_fract[diag_dir]; + int y = TileY(depot_tile) * TILE_SIZE + _vehicle_initial_y_fract[diag_dir]; + for (Train *t = train; t != nullptr; t = t->Next()) { + t->tile = depot_tile; + t->direction = DiagDirToDir(diag_dir); + t->vehstatus |= VS_HIDDEN; + t->track = TRACK_BIT_DEPOT; + t->x_pos = x; + t->y_pos = y; + t->z_pos = GetSlopePixelZ(x, y); + t->UpdatePosition(); + } +} + /** * Check whether the train parts can be attached. * @param t the train to check @@ -1061,6 +1137,8 @@ static CommandCost CheckTrainAttachment(Train *t) /* No multi-part train, no need to check. */ if (t == nullptr || t->Next() == nullptr) return CommandCost(); + if (LookForTileInDepot(t) == INVALID_TILE) return_cmd_error(STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT); + /* The maximum length for a train. For each part we decrease this by one * and if the result is negative the train is simply too long. */ int allowed_len = _settings_game.vehicle.max_train_length * TILE_SIZE - t->gcache.cached_veh_length; @@ -1426,8 +1504,15 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID CheckCargoCapacity(dst_head); } - if (src_head != nullptr) src_head->First()->MarkDirty(); - if (dst_head != nullptr) dst_head->First()->MarkDirty(); + if (src_head != nullptr) { + PlaceOnRailDepot(src_head->First()); + src_head->First()->MarkDirty(); + } + + if (dst_head != nullptr) { + PlaceOnRailDepot(dst_head->First()); + dst_head->First()->MarkDirty(); + } /* We are undoubtedly changing something in the depot and train list. */ InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(src->tile)); From f68638a67913207072813483e058f4169127c09b Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 10 May 2023 20:22:04 +0200 Subject: [PATCH 017/143] Feature: Allow vehicle replacements even if new road or rail type is not compatible. --- src/autoreplace_cmd.cpp | 17 ++++++++++++----- src/lang/english.txt | 5 +++++ src/saveload/afterload.cpp | 5 +++++ src/saveload/saveload.h | 1 + src/settings_gui.cpp | 3 +++ src/settings_table.cpp | 16 ++++++++++++++++ src/settings_type.h | 3 +++ src/table/settings/game_settings.ini | 22 ++++++++++++++++++++++ 8 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 026fe129cbe52..52f4588fbc513 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -71,7 +71,10 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company) switch (type) { case VEH_TRAIN: { /* make sure the railtypes are compatible */ - if ((GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) return false; + if (!_settings_game.depot.allow_no_comp_railtype_replacements && + (GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) { + return false; + } /* make sure we do not replace wagons with engines or vice versa */ if ((e_from->u.rail.railveh_type == RAILVEH_WAGON) != (e_to->u.rail.railveh_type == RAILVEH_WAGON)) return false; @@ -79,11 +82,15 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company) } case VEH_ROAD: - /* make sure the roadtypes are compatible */ - if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) return false; + if (!_settings_game.depot.allow_no_comp_roadtype_replacements) { + /* make sure the roadtypes are compatible */ + if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) { + return false; + } - /* make sure that we do not replace a tram with a normal road vehicles or vice versa */ - if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false; + /* make sure that we do not replace a tram with a normal road vehicles or vice versa */ + if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false; + } break; case VEH_AIRCRAFT: diff --git a/src/lang/english.txt b/src/lang/english.txt index 36f555620ad8a..b00cb375770c6 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1464,6 +1464,7 @@ STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :Allow construct STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD :Allow drive-through road stops on roads owned by competitors: {STRING2} STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Allow construction of drive-through road stops on roads owned by other companies STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Changing this setting is not possible when there are vehicles +STR_CONFIG_SETTING_REPLACEMENTS_DIFF_TYPE :{WHITE}Disabling this setting is not possible during a game STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :Infrastructure maintenance: {STRING2} STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :When enabled, infrastructure causes maintenance costs. The cost grows over-proportional with the network size, thus affecting bigger companies more than smaller ones @@ -1621,6 +1622,10 @@ STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS :Allow to join d STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS_HELPTEXT :Allow adding parts to a depot without directly touching the existing parts. Needs Ctrl+Click while placing the new parts STR_CONFIG_SETTING_DEPOT_SPREAD :Maximum depot spread: {STRING2} STR_CONFIG_SETTING_DEPOT_SPREAD_HELPTEXT :Maximum area the parts of a single depot may be spread out on +STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL :Allow replacing rail vehicles with incompatible rail types: {STRING2} +STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL_HELPTEXT :Allow replacing rail vehicles even if they are not compatible by rail type +STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_ROAD :Allow replacing road vehicles with incompatible road types: {STRING2} +STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_ROAD_HELPTEXT :Allow replacing road vehicles even if they are not compatible by road type STR_CONFIG_SETTING_STATION_SPREAD :Maximum station spread: {STRING2} STR_CONFIG_SETTING_STATION_SPREAD_HELPTEXT :Maximum area the parts of a single station may be spread out on. Note that high values will slow the game diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 578e461b8f9f3..d621a0596200c 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -804,6 +804,11 @@ bool AfterLoadGame() _settings_game.depot.distant_join_depots = true; } + if (IsSavegameVersionBefore(SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS)) { + _settings_game.depot.allow_no_comp_railtype_replacements = false; + _settings_game.depot.allow_no_comp_roadtype_replacements = false; + } + /* Load the sprites */ GfxLoadSprites(); LoadStringWidthTable(); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 22cd1c980ad19..d67f9a5befb57 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -389,6 +389,7 @@ enum SaveLoadVersion : uint16_t { SLV_DEPOTID_BACKUP_ORDERS, ///< 341 PR#XXXXX Backup orders are indexed through DepotIDs. SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, ///< 342 PR#XXXXX Add some members to depot struct. SLV_DEPOT_SPREAD, ///< 343 PR#XXXXX Add a setting for max depot spread. + SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS, ///< 344 PR#XXXXX Allow incompatible vehicle replacements. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 1b3fd4a8fe7e9..739a339a7a773 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2149,6 +2149,9 @@ static SettingsContainer &GetSettingsTree() { depots->Add(new SettingEntry("depot.depot_spread")); depots->Add(new SettingEntry("depot.distant_join_depots")); + + depots->Add(new SettingEntry("depot.allow_no_comp_railtype_replacements")); + depots->Add(new SettingEntry("depot.allow_no_comp_roadtype_replacements")); } limitations->Add(new SettingEntry("construction.command_pause_level")); diff --git a/src/settings_table.cpp b/src/settings_table.cpp index 0fd874a38c007..0f262ebfe3344 100644 --- a/src/settings_table.cpp +++ b/src/settings_table.cpp @@ -380,6 +380,22 @@ static void SpriteZoomMinChanged(int32_t) MarkWholeScreenDirty(); } +static bool CheckDifferentRailRoadTypesReplacements(int32_t &new_value) +{ + if (_game_mode == GM_NORMAL) { + if (new_value == 0) { + ShowErrorMessage(STR_CONFIG_SETTING_REPLACEMENTS_DIFF_TYPE, INVALID_STRING_ID, WL_ERROR); + return false; + } + } + return true; +} + +static void InvalidateReplacementWindows(int32_t) +{ + InvalidateWindowClassesData(WC_REPLACE_VEHICLE); +} + /** * Update any possible saveload window and delete any newgrf dialogue as * its widget parts might change. Reinit all windows as it allows access to the diff --git a/src/settings_type.h b/src/settings_type.h index 6938578466837..4d1451e0ae2b4 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -574,6 +574,9 @@ struct StationSettings { struct DepotSettings { uint8_t depot_spread; ///< amount a depot may spread bool distant_join_depots; ///< allow to join non-adjacent depots + + bool allow_no_comp_railtype_replacements; ///< allow replacing rail vehicles even if rail type is not compatible + bool allow_no_comp_roadtype_replacements; ///< allow replacing road vehicles even if road type is not compatible }; /** Default settings for vehicles. */ diff --git a/src/table/settings/game_settings.ini b/src/table/settings/game_settings.ini index 286c517fee6b5..269da0c70bdd2 100644 --- a/src/table/settings/game_settings.ini +++ b/src/table/settings/game_settings.ini @@ -21,6 +21,8 @@ static bool CheckRoadSide(int32_t &new_value); static bool CheckDynamicEngines(int32_t &new_value); static void StationCatchmentChanged(int32_t new_value); static void MaxVehiclesChanged(int32_t new_value); +static bool CheckDifferentRailRoadTypesReplacements(int32_t &new_value); +static void InvalidateReplacementWindows(int32_t new_value); static const SettingVariant _game_settings_table[] = { [post-amble] @@ -166,6 +168,26 @@ strval = STR_CONFIG_SETTING_TILE_LENGTH post_cb = [](auto) { CloseWindowByClass(WC_SELECT_DEPOT); } cat = SC_BASIC +[SDT_BOOL] +var = depot.allow_no_comp_railtype_replacements +from = SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS +def = false +str = STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL +strhelp = STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL_HELPTEXT +pre_cb = CheckDifferentRailRoadTypesReplacements +post_cb = InvalidateReplacementWindows +cat = SC_EXPERT + +[SDT_BOOL] +var = depot.allow_no_comp_roadtype_replacements +from = SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS +def = false +str = STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_ROAD +strhelp = STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_ROAD_HELPTEXT +pre_cb = CheckDifferentRailRoadTypesReplacements +post_cb = InvalidateReplacementWindows +cat = SC_EXPERT + [SDT_OMANY] var = vehicle.road_side type = SLE_UINT8 From b7491916c2ac47f52664088d9122d51c7391e3dc Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 10 May 2023 19:47:55 +0200 Subject: [PATCH 018/143] Change: Move some bits in water tiles for alignment purposes. --- docs/landscape.html | 26 +++++++++++++------------- docs/landscape_grid.html | 8 ++++---- src/saveload/map_sl.cpp | 11 +++++++++++ src/saveload/saveload.h | 2 ++ src/water_map.h | 6 +++--- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/docs/landscape.html b/docs/landscape.html index c48aedcebc2a3..231bfe6bec63b 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -1047,55 +1047,55 @@

Landscape

- 10..1B  + 40..4B  canal locks - + - + - + - + - + - + - + - + - + - + - + - +
10  40  middle part, (SW-NE direction)
11  41  middle part, (NW-SE direction)
12  42  middle part, (NE-SW direction)
13  43  middle part, (SE-NW direction)
14  44  lower part, (SW-NE direction)
15  45  lower part, (NW-SE direction)
16  46  lower part, (NE-SW direction)
17  47  lower part, (SE-NW direction)
18  48  upper part, (SW-NE direction)
19  49  upper part, (NW-SE direction)
1A  4A  upper part, (NE-SW direction)
1B  4B  upper part, (SE-NW direction)
diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index 603d78025b0d2..5121d5224f014 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -246,7 +246,7 @@

Landscape

OOOO OOOO OOOO OOOO OOOO OOOO OOOO OOOO - OOOO OOOX + OOOO OOOX OOOO OOOO OOOO OOOO OOOO OOOO OOOO OOOO @@ -254,18 +254,18 @@

Landscape

canal, river XXXX XXXX - OOOO OOOO + OOOO OOOO lock OOOO OOOO - OOO1 XX XX + O1OOXX XX shipdepot XXXX XXXX XXXX XXXX OOOO OOOO - 1OOO OOX X + 1OOOOOX X 8 diff --git a/src/saveload/map_sl.cpp b/src/saveload/map_sl.cpp index 3ce23a42b28f9..8d13947bc0b14 100644 --- a/src/saveload/map_sl.cpp +++ b/src/saveload/map_sl.cpp @@ -15,6 +15,7 @@ #include "../map_func.h" #include "../core/bitmath_func.hpp" #include "../fios.h" +#include "../tile_map.h" #include "../safeguards.h" @@ -243,6 +244,16 @@ struct MAP5ChunkHandler : ChunkHandler { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m5() = buf[j]; } + + if (IsSavegameVersionBefore(SLV_ALIGN_WATER_BITS)) { + /* Move some bits for alignment purposes. */ + for (TileIndex i = 0; i != size; i++) { + if (IsTileType(i, MP_WATER)) { + SB(Tile(i).m5(), 6, 1, GB(Tile(i).m5(), 4, 1)); + SB(Tile(i).m5(), 4, 1, 0); + } + } + } } void Save() const override diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index d67f9a5befb57..7f37026fa088f 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -391,6 +391,8 @@ enum SaveLoadVersion : uint16_t { SLV_DEPOT_SPREAD, ///< 343 PR#XXXXX Add a setting for max depot spread. SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS, ///< 344 PR#XXXXX Allow incompatible vehicle replacements. + SLV_ALIGN_WATER_BITS, ///< 315 PR#XXXXX Align some water bits in the map array. + SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/water_map.h b/src/water_map.h index 116a37f228651..36ac873e637b4 100644 --- a/src/water_map.h +++ b/src/water_map.h @@ -17,12 +17,12 @@ * Bit field layout of m5 for water tiles. */ enum WaterTileTypeBitLayout { - WBL_TYPE_BEGIN = 4, ///< Start of the 'type' bitfield. - WBL_TYPE_COUNT = 4, ///< Length of the 'type' bitfield. + WBL_TYPE_BEGIN = 6, ///< Start of the 'type' bitfield. + WBL_TYPE_COUNT = 2, ///< Length of the 'type' bitfield. WBL_TYPE_NORMAL = 0x0, ///< Clear water or coast ('type' bitfield). WBL_TYPE_LOCK = 0x1, ///< Lock ('type' bitfield). - WBL_TYPE_DEPOT = 0x8, ///< Depot ('type' bitfield). + WBL_TYPE_DEPOT = 0x2, ///< Depot ('type' bitfield). WBL_COAST_FLAG = 0, ///< Flag for coast. From 2ee7d28c631fbabe5dfa635c8d24c6acfe6f4351 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 27 Mar 2021 13:36:32 +0100 Subject: [PATCH 019/143] Codechange: Add and use GetWaterTileClass. --- src/water_map.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/water_map.h b/src/water_map.h index 36ac873e637b4..1e062109a3823 100644 --- a/src/water_map.h +++ b/src/water_map.h @@ -78,6 +78,16 @@ enum LockPart { bool IsPossibleDockingTile(Tile t); +/** + * Get the type of water tile: clear, lock or depot. + * @param t Water tile to query. + * @return WBL_TYPE_NORMAL, WBL_TYPE_LOCK or WBL_TYPE_DEPOT. + */ +static inline WaterTileTypeBitLayout GetWaterTileClass(Tile t) { + assert(IsTileType(t, MP_WATER)); + return static_cast(GB(t.m5(), WBL_TYPE_BEGIN, WBL_TYPE_COUNT)); +} + /** * Get the water tile type at a tile. * @param t Water tile to query. @@ -87,7 +97,7 @@ inline WaterTileType GetWaterTileType(Tile t) { assert(IsTileType(t, MP_WATER)); - switch (GB(t.m5(), WBL_TYPE_BEGIN, WBL_TYPE_COUNT)) { + switch (GetWaterTileClass(t)) { case WBL_TYPE_NORMAL: return HasBit(t.m5(), WBL_COAST_FLAG) ? WATER_TILE_COAST : WATER_TILE_CLEAR; case WBL_TYPE_LOCK: return WATER_TILE_LOCK; case WBL_TYPE_DEPOT: return WATER_TILE_DEPOT; @@ -224,7 +234,8 @@ inline bool IsCoastTile(Tile t) */ inline bool IsShipDepot(Tile t) { - return GetWaterTileType(t) == WATER_TILE_DEPOT; + assert(IsTileType(t, MP_WATER)); + return GetWaterTileClass(t) == WBL_TYPE_DEPOT; } /** @@ -305,7 +316,8 @@ inline TileIndex GetShipDepotNorthTile(Tile t) */ inline bool IsLock(Tile t) { - return GetWaterTileType(t) == WATER_TILE_LOCK; + assert(IsTileType(t, MP_WATER)); + return GetWaterTileClass(t) == WBL_TYPE_LOCK; } /** From cfbcd6979c38a9acd6c2be763f56769c8ef521f1 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 10 May 2023 19:52:49 +0200 Subject: [PATCH 020/143] Change: Change rail depot type value in order to align bits. --- docs/landscape.html | 2 +- docs/landscape_grid.html | 2 +- src/rail_map.h | 2 +- src/saveload/afterload.cpp | 9 +++++++++ src/saveload/saveload.h | 1 + 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/landscape.html b/docs/landscape.html index 231bfe6bec63b..40d73c7fe4c49 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -520,7 +520,7 @@

Landscape

  • m2 bit 11: opposite track is reserved, too
  • -
  • m5 bit 7 set, bit 6 set: railway depot +
  • m5 bit 7 set, bit 6 clear: railway depot
    • m2: Depot index
    • m5 bits 1..0: exit towards diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index 5121d5224f014..6e59a3acac673 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -118,7 +118,7 @@

      Landscape

      XXXX XXXX XXXX XXXX OOOO OOOO OOOO XXXX - 11OX OOXX + 1OOX OOXX 2 diff --git a/src/rail_map.h b/src/rail_map.h index 5ff2fb9ec36db..03bca7e6e3243 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -23,7 +23,7 @@ enum RailTileType { RAIL_TILE_NORMAL = 0, ///< Normal rail tile without signals RAIL_TILE_SIGNALS = 1, ///< Normal rail tile with signals - RAIL_TILE_DEPOT = 3, ///< Depot (one entrance) + RAIL_TILE_DEPOT = 2, ///< Depot }; /** diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index d621a0596200c..0205cdb260460 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -645,6 +645,15 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_DEPOTS_ALIGN_RAIL_DEPOT_BITS)) { + for (auto t : Map::Iterate()) { + if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 3) { + /* Change the rail type for depots from old value 3 to new value 2. */ + SB(t.m5(), 6, 2, RAIL_TILE_DEPOT); + } + } + } + /* in version 2.1 of the savegame, town owner was unified. */ if (IsSavegameVersionBefore(SLV_2, 1)) ConvertTownOwner(); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 7f37026fa088f..083872e8c6662 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -392,6 +392,7 @@ enum SaveLoadVersion : uint16_t { SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS, ///< 344 PR#XXXXX Allow incompatible vehicle replacements. SLV_ALIGN_WATER_BITS, ///< 315 PR#XXXXX Align some water bits in the map array. + SLV_DEPOTS_ALIGN_RAIL_DEPOT_BITS, ///< 316 PR#XXXXX Align one bit for rail depots. SL_MAX_VERSION, ///< Highest possible saveload version }; From 21cd227a688b7f81729da6d79f3fa7aca2854619 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sun, 27 Dec 2020 21:10:03 +0100 Subject: [PATCH 021/143] Codechange: Use bit alignment for detecting road, rail and water depots. --- src/depot_map.h | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/depot_map.h b/src/depot_map.h index 78a0f0273c742..51639a3b418a5 100644 --- a/src/depot_map.h +++ b/src/depot_map.h @@ -12,24 +12,25 @@ #include "station_map.h" +static const uint8_t DEPOT_TYPE = 0x02; + /** * Check if a tile is a depot and it is a depot of the given type. */ inline bool IsDepotTypeTile(Tile tile, TransportType type) { + if (type == TRANSPORT_AIR) return IsHangarTile(tile); + + if (GB(tile.m5(), 6, 2) != DEPOT_TYPE) return false; + switch (type) { default: NOT_REACHED(); case TRANSPORT_RAIL: - return IsRailDepotTile(tile); - + return IsTileType(tile, MP_RAILWAY); case TRANSPORT_ROAD: - return IsRoadDepotTile(tile); - + return IsTileType(tile, MP_ROAD); case TRANSPORT_WATER: - return IsShipDepotTile(tile); - - case TRANSPORT_AIR: - return IsHangarTile(tile); + return IsTileType(tile, MP_WATER); } } @@ -40,7 +41,11 @@ inline bool IsDepotTypeTile(Tile tile, TransportType type) */ inline bool IsDepotTile(Tile tile) { - return IsRailDepotTile(tile) || IsRoadDepotTile(tile) || IsShipDepotTile(tile) || IsHangarTile(tile); + TileType type = GetTileType(tile); + if (type == MP_STATION) return IsHangar(tile); + if (GB(tile.m5(), 6, 2) != DEPOT_TYPE) return false; + + return type == MP_RAILWAY || type == MP_ROAD || type == MP_WATER; } extern DepotID GetHangarIndex(TileIndex t); From e27e733187659aed169d0d6c017538a46d4c0e14 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 17:49:33 +0200 Subject: [PATCH 022/143] Codechange: Fix some CodeQL alerts. --- src/rail_cmd.cpp | 42 ++++++++++++++++++------------------------ src/road_cmd.cpp | 30 ++++++++++++------------------ src/roadveh_cmd.cpp | 35 ++++++++++++++++++----------------- 3 files changed, 48 insertions(+), 59 deletions(-) diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index b2172560b5413..2e02d8eb24c61 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1659,32 +1659,26 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s switch (tt) { case MP_RAILWAY: - switch (GetRailTileType(tile)) { - case RAIL_TILE_DEPOT: - if (flags & DC_EXEC) { - /* notify YAPF about the track layout change */ - YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile)); - - if (find(affected_depots.begin(), affected_depots.end(), (tile)) == affected_depots.end()) { - affected_depots.push_back(GetDepotIndex(tile)); - } - } - - found_convertible_track = true; - cost.AddCost(RailConvertCost(type, totype)); - break; + found_convertible_track = true; + if (GetRailTileType(tile) == RAIL_TILE_DEPOT) { + if (flags & DC_EXEC) { + /* notify YAPF about the track layout change */ + YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile)); - default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS - if (flags & DC_EXEC) { - /* notify YAPF about the track layout change */ - TrackBits tracks = GetTrackBits(tile); - while (tracks != TRACK_BIT_NONE) { - YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks)); - } + if (find(affected_depots.begin(), affected_depots.end(), (tile)) == affected_depots.end()) { + affected_depots.push_back(GetDepotIndex(tile)); } - found_convertible_track = true; - cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile))); - break; + } + cost.AddCost(RailConvertCost(type, totype)); + } else { // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS + if (flags & DC_EXEC) { + /* notify YAPF about the track layout change */ + TrackBits tracks = GetTrackBits(tile); + while (tracks != TRACK_BIT_NONE) { + YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks)); + } + } + cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile))); } break; diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 3dd28cd934f69..d277079070e8f 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -2270,27 +2270,21 @@ static const uint8_t _roadveh_enter_depot_dir[4] = { static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int, int) { - switch (GetRoadTileType(tile)) { - case ROAD_TILE_DEPOT: { - if (v->type != VEH_ROAD) break; + if (GetRoadTileType(tile) != ROAD_TILE_DEPOT || v->type != VEH_ROAD) return VETSB_CONTINUE; - RoadVehicle *rv = RoadVehicle::From(v); - if (rv->frame == RVC_DEPOT_STOP_FRAME && - _roadveh_enter_depot_dir[GetRoadDepotDirection(tile)] == rv->state) { - rv->state = RVSB_IN_DEPOT; - rv->vehstatus |= VS_HIDDEN; - rv->direction = ReverseDir(rv->direction); - if (rv->Next() == nullptr) VehicleEnterDepot(rv->First()); - rv->tile = tile; - - InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile)); - return VETSB_ENTERED_WORMHOLE; - } - break; - } + RoadVehicle *rv = RoadVehicle::From(v); + if (rv->frame == RVC_DEPOT_STOP_FRAME && + _roadveh_enter_depot_dir[GetRoadDepotDirection(tile)] == rv->state) { + rv->state = RVSB_IN_DEPOT; + rv->vehstatus |= VS_HIDDEN; + rv->direction = ReverseDir(rv->direction); + if (rv->Next() == nullptr) VehicleEnterDepot(rv->First()); + rv->tile = tile; - default: break; + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile)); + return VETSB_ENTERED_WORMHOLE; } + return VETSB_CONTINUE; } diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 96adeb23abf7c..9b0ffe5589c5d 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -654,29 +654,30 @@ struct RoadVehFindData { static Vehicle *EnumCheckRoadVehClose(Vehicle *v, void *data) { - static const int8_t dist_x[] = { -4, -8, -4, -1, 4, 8, 4, 1 }; - static const int8_t dist_y[] = { -4, -1, 4, 8, 4, 1, -4, -8 }; + if (v->type != VEH_ROAD || v->IsInDepot()) return nullptr; RoadVehFindData *rvf = (RoadVehFindData*)data; + if (abs(v->z_pos - rvf->veh->z_pos) >= 6 || + v->direction != rvf->dir || + rvf->veh->First() == v->First()) return nullptr; + + static const int8_t dist_x[] = { -4, -8, -4, -1, 4, 8, 4, 1 }; + static const int8_t dist_y[] = { -4, -1, 4, 8, 4, 1, -4, -8 }; short x_diff = v->x_pos - rvf->x; short y_diff = v->y_pos - rvf->y; - if (v->type == VEH_ROAD && - !v->IsInDepot() && - abs(v->z_pos - rvf->veh->z_pos) < 6 && - v->direction == rvf->dir && - rvf->veh->First() != v->First() && - (dist_x[v->direction] >= 0 || (x_diff > dist_x[v->direction] && x_diff <= 0)) && - (dist_x[v->direction] <= 0 || (x_diff < dist_x[v->direction] && x_diff >= 0)) && - (dist_y[v->direction] >= 0 || (y_diff > dist_y[v->direction] && y_diff <= 0)) && - (dist_y[v->direction] <= 0 || (y_diff < dist_y[v->direction] && y_diff >= 0))) { - uint diff = abs(x_diff) + abs(y_diff); - - if (diff < rvf->best_diff || (diff == rvf->best_diff && v->index < rvf->best->index)) { - rvf->best = v; - rvf->best_diff = diff; - } + /* Check if vehicle is not close. */ + if ((dist_x[v->direction] < 0 && (x_diff > 0 || x_diff <= dist_x[v->direction]))) return nullptr; + if ((dist_x[v->direction] > 0 && (x_diff < 0 || x_diff >= dist_x[v->direction]))) return nullptr; + if ((dist_y[v->direction] < 0 && (y_diff > 0 || y_diff <= dist_y[v->direction]))) return nullptr; + if ((dist_y[v->direction] > 0 && (y_diff < 0 || y_diff >= dist_y[v->direction]))) return nullptr; + + uint diff = abs(x_diff) + abs(y_diff); + + if (diff < rvf->best_diff || (diff == rvf->best_diff && v->index < rvf->best->index)) { + rvf->best = v; + rvf->best_diff = diff; } return nullptr; From 09b9f4bac25fec4cc37679e0fe3b194963ce3cad Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 10 May 2023 21:51:41 +0200 Subject: [PATCH 023/143] Change: Keep removed depots in the pool for a while. (based on patch by adf, #6328, #7051) --- src/build_vehicle_gui.cpp | 22 ++++++++++++---------- src/depot.cpp | 20 +++++++++++++++++++- src/depot_base.h | 18 +++++++++++++++++- src/depot_cmd.cpp | 15 +++++++++++++++ src/depot_gui.cpp | 1 + src/landscape.cpp | 2 ++ src/order_cmd.cpp | 2 +- src/saveload/afterload.cpp | 1 + src/saveload/depot_sl.cpp | 1 + src/saveload/saveload.h | 2 ++ src/script/api/script_depotlist.cpp | 2 +- src/ship_cmd.cpp | 2 +- src/timer/timer_game_tick.h | 1 + src/viewport.cpp | 4 ++++ 14 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 3d8ed88f52ce6..3efc4623dcfd4 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -1294,6 +1294,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; } @@ -1487,18 +1490,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 0ba6ee3cd3d7e..a67ff77a748a1 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) this->xy = this->depot_tiles[0]; InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type); } else { - delete this; + assert(this->IsInUse()); + this->Disuse(); } } diff --git a/src/depot_base.h b/src/depot_base.h index 41512153bf137..b6571211177ab 100644 --- a/src/depot_base.h +++ b/src/depot_base.h @@ -40,6 +40,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 { @@ -75,9 +76,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 32cd01558b475..1bbdfffe73d13 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 151efe3c23ae9..2273f0511c2dc 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 7964e3ba02b5a..5da1a2995f7b4 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 203d7784f24c7..63a5869acb08d 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 0205cdb260460..d64214f68d22a 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2843,6 +2843,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 1987605ea3487..4bc5dde166d96 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 083872e8c6662..a6691e3120462 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -394,6 +394,8 @@ enum SaveLoadVersion : uint16_t { SLV_ALIGN_WATER_BITS, ///< 315 PR#XXXXX Align some water bits in the map array. SLV_DEPOTS_ALIGN_RAIL_DEPOT_BITS, ///< 316 PR#XXXXX Align one bit for rail depots. + 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 7997d399e782b..0906a86da9a91 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 498817772dcc1..1695c4e153110 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 02ae2b16ff95b..7af0024bd4465 100644 --- a/src/timer/timer_game_tick.h +++ b/src/timer/timer_game_tick.h @@ -78,6 +78,7 @@ class Ticks { 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 6a9da5e44d023..189296239831c 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(); } } From 0262b6543565abb9cc0062b0943a06c5a05a215d Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Mon, 6 Dec 2021 14:15:51 +0100 Subject: [PATCH 024/143] Feature: Try to reuse a removed depot when placing a new one. (based on patch by adf88, #6328, #7051) --- src/depot.cpp | 33 ++++++++++++++++++++++++++++++--- src/depot_base.h | 2 ++ src/depot_cmd.cpp | 24 ++++++++++++++++++++++++ src/depot_gui.cpp | 9 +++++++++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/depot.cpp b/src/depot.cpp index a67ff77a748a1..db6b226adb487 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -65,6 +65,20 @@ Depot::~Depot() InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type); } +/** + * Cancel deletion of this depot (reuse it). + * @param xy New location of the depot. + * @see Depot::IsInUse + * @see Depot::Disuse + */ +void Depot::Reuse(TileIndex xy) +{ + this->delete_ctr = 0; + this->xy = xy; + this->ta.tile = xy; + this->ta.h = this->ta.w = 1; +} + /** * Schedule deletion of this depot. * @@ -73,6 +87,7 @@ Depot::~Depot() * placed again later without messing vehicle orders. * * @see Depot::IsInUse + * @see Depot::Reuse */ void Depot::Disuse() { @@ -112,13 +127,19 @@ CommandCost Depot::BeforeAddTiles(TileArea ta) { assert(ta.tile != INVALID_TILE); - if (this->ta.tile != INVALID_TILE) { + if (this->ta.tile != INVALID_TILE && this->IsInUse()) { /* Important when the old rect is completely inside the new rect, resp. the old one was empty. */ ta.Add(this->ta.tile); ta.Add(TileAddXY(this->ta.tile, this->ta.w - 1, this->ta.h - 1)); } - if ((ta.w > _settings_game.depot.depot_spread) || (ta.h > _settings_game.depot.depot_spread)) { + /* A max depot spread of 1 for VEH_SHIP is a special case, + * as ship depots consist of two tiles. */ + if (this->veh_type == VEH_SHIP && _settings_game.depot.depot_spread == 1) { + /* (ta.w, ta.h) must be equal to (1, 2) or (2, 1). + * This means that ta.w * ta.h must be equal to 2. */ + if (ta.w * ta.h != 2) return_cmd_error(STR_ERROR_DEPOT_TOO_SPREAD_OUT); + } else if (std::max(ta.w, ta.h) > _settings_game.depot.depot_spread) { return_cmd_error(STR_ERROR_DEPOT_TOO_SPREAD_OUT); } return CommandCost(); @@ -154,11 +175,17 @@ void Depot::AfterAddRemove(TileArea ta, bool adding) this->RescanDepotTiles(); assert(!this->depot_tiles.empty()); this->xy = this->depot_tiles[0]; - InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type); } else { assert(this->IsInUse()); this->Disuse(); + TileIndex old_tile = this->xy; + this->RescanDepotTiles(); + assert(this->depot_tiles.empty()); + this->xy = old_tile; } + + InvalidateWindowData(WC_VEHICLE_DEPOT, this->index); + InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type); } /** diff --git a/src/depot_base.h b/src/depot_base.h index b6571211177ab..ba09027befbab 100644 --- a/src/depot_base.h +++ b/src/depot_base.h @@ -86,12 +86,14 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> { * and the depot awaits to be deleted. * @return true iff still in use * @see Depot::Disuse + * @see Depot::Reuse */ inline bool IsInUse() const { return this->delete_ctr == 0; } + void Reuse(TileIndex xy); void Disuse(); /* Check we can add some tiles to this depot. */ diff --git a/src/depot_cmd.cpp b/src/depot_cmd.cpp index 1bbdfffe73d13..88959bf98e93e 100644 --- a/src/depot_cmd.cpp +++ b/src/depot_cmd.cpp @@ -77,6 +77,23 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str return CommandCost(); } +/** + * Find a demolished depot close to a tile. + * @param ta Tile area to search for. + * @param type Depot type. + * @param cid Previous owner of the depot. + * @return The index of a demolished nearby depot, or INVALID_DEPOT if none. + */ +DepotID FindDeletedDepotCloseTo(TileArea ta, VehicleType type, CompanyID cid) +{ + for (Depot *depot : Depot::Iterate()) { + if (depot->IsInUse() || depot->veh_type != type || depot->owner != cid) continue; + if (ta.Contains(depot->xy)) return depot->index; + } + + return INVALID_DEPOT; +} + void OnTick_Depot() { if (_game_mode == GM_EDITOR) return; @@ -128,6 +145,12 @@ CommandCost FindJoiningDepot(TileArea ta, VehicleType veh_type, DepotID &join_to } } + if (closest_depot == INVALID_DEPOT) { + /* Check for close unused depots. */ + check_area.Expand(7); // total distance of 8 + closest_depot = FindDeletedDepotCloseTo(check_area, veh_type, _current_company); + } + if (closest_depot != INVALID_DEPOT) { assert(Depot::IsValidID(closest_depot)); depot = Depot::Get(closest_depot); @@ -151,6 +174,7 @@ CommandCost FindJoiningDepot(TileArea ta, VehicleType veh_type, DepotID &join_to depot = Depot::Get(join_to); assert(depot->owner == _current_company); assert(depot->veh_type == veh_type); + if (!depot->IsInUse() && (flags & DC_EXEC)) depot->Reuse(ta.tile); return depot->BeforeAddTiles(ta); } diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 2273f0511c2dc..85d99eeaf1530 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -1249,6 +1249,15 @@ static const Depot *FindDepotsNearby(TileArea ta, VehicleType veh_type, bool dis TileIndex tile = TileAddByDir(andd.search_area.tile, DIR_N); CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyDepot, &andd); + /* Add reusable depots. */ + ta.Expand(8); + for (Depot *d : Depot::Iterate()) { + if (d->IsInUse()) continue; + if (d->veh_type != veh_type || d->owner != _current_company) continue; + if (!ta.Contains(d->xy)) continue; + _depots_nearby_list.push_back(d->index); + } + return nullptr; } From 05b8d6cb4c83eeab37b9406e00f35684535161da Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 10 May 2023 21:52:47 +0200 Subject: [PATCH 025/143] Feature: Show depot signs of removed depots. (based on patch by adf88, #6328, #5071) --- src/depot.cpp | 13 +++++++++ src/depot_base.h | 3 +++ src/depot_cmd.cpp | 26 ++++++++++++++++++ src/depot_func.h | 1 + src/lang/english.txt | 3 +++ src/saveload/afterload.cpp | 2 ++ src/station.cpp | 4 +++ src/town_cmd.cpp | 3 +++ src/viewport.cpp | 55 ++++++++++++++++++++++++++++++++++++++ src/viewport_kdtree.h | 7 +++++ 10 files changed, 117 insertions(+) diff --git a/src/depot.cpp b/src/depot.cpp index db6b226adb487..88518039cfb86 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -17,6 +17,7 @@ #include "vehiclelist.h" #include "command_func.h" #include "vehicle_base.h" +#include "viewport_kdtree.h" #include "safeguards.h" @@ -63,6 +64,10 @@ Depot::~Depot() this->veh_type, this->owner, this->index).Pack()); InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type); + + /* The sign will now disappear. */ + _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeDepot(this->index)); + this->sign.MarkDirty(); } /** @@ -77,6 +82,10 @@ void Depot::Reuse(TileIndex xy) this->xy = xy; this->ta.tile = xy; this->ta.h = this->ta.w = 1; + + /* Ensure the sign is not drawn */ + _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeDepot(this->index)); + this->sign.MarkDirty(); } /** @@ -93,6 +102,10 @@ void Depot::Disuse() { /* Mark that the depot is demolished and start the countdown. */ this->delete_ctr = 8; + + /* Update the sign, it will be visible from now. */ + this->UpdateVirtCoord(); + _viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeDepot(this->index)); } /** diff --git a/src/depot_base.h b/src/depot_base.h index ba09027befbab..137da13ee173d 100644 --- a/src/depot_base.h +++ b/src/depot_base.h @@ -11,6 +11,7 @@ #define DEPOT_BASE_H #include "depot_map.h" +#include "viewport_type.h" #include "core/pool_type.hpp" #include "timer/timer_game_calendar.h" #include "rail_type.h" @@ -41,6 +42,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. + ViewportSign sign; ///< NOSAVE: Dimensions of sign Station *station; ///< For aircraft, station associated with this hangar. union { @@ -95,6 +97,7 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> { void Reuse(TileIndex xy); void Disuse(); + void UpdateVirtCoord(); /* 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 88959bf98e93e..1364800dd82b7 100644 --- a/src/depot_cmd.cpp +++ b/src/depot_cmd.cpp @@ -17,6 +17,9 @@ #include "vehiclelist.h" #include "window_func.h" #include "depot_cmd.h" +#include "strings_func.h" +#include "landscape.h" +#include "viewport_kdtree.h" #include "timer/timer_game_tick.h" #include "table/strings.h" @@ -60,6 +63,8 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str } if (flags & DC_EXEC) { + /* _viewport_sign_kdtree does not need to be updated, only in-use depots can be renamed */ + if (reset) { d->name.clear(); MakeDefaultName(d); @@ -77,6 +82,27 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str return CommandCost(); } +/** Update the virtual coords needed to draw the depot sign. */ +void Depot::UpdateVirtCoord() +{ + Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE); + + pt.y -= 32 * ZOOM_BASE; + + SetDParam(0, this->veh_type); + SetDParam(1, this->index); + this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_DEPOT, STR_VIEWPORT_DEPOT_TINY); + + SetWindowDirty(WC_VEHICLE_DEPOT, this->index); +} + +/** Update the virtual coords needed to draw the depot sign for all depots. */ +void UpdateAllDepotVirtCoords() +{ + /* Only demolished depots have signs. */ + for (Depot *d : Depot::Iterate()) if (!d->IsInUse()) d->UpdateVirtCoord(); +} + /** * Find a demolished depot close to a tile. * @param ta Tile area to search for. diff --git a/src/depot_func.h b/src/depot_func.h index f214e6d848997..d1890c87285a3 100644 --- a/src/depot_func.h +++ b/src/depot_func.h @@ -19,6 +19,7 @@ void ShowDepotWindow(DepotID depot_id); void InitDepotWindowBlockSizes(); void DeleteDepotHighlightOfVehicle(const Vehicle *v); +void UpdateAllDepotVirtCoords(); /** * Find out if the slope of the tile is suitable to build a depot of given direction diff --git a/src/lang/english.txt b/src/lang/english.txt index b00cb375770c6..d237f8766f429 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5848,6 +5848,9 @@ STR_VIEWPORT_STATION_TINY :{TINY_FONT}{STA STR_VIEWPORT_WAYPOINT :{WAYPOINT} STR_VIEWPORT_WAYPOINT_TINY :{TINY_FONT}{WAYPOINT} +STR_VIEWPORT_DEPOT :{DEPOT} +STR_VIEWPORT_DEPOT_TINY :{TINY_FONT}{DEPOT} + # Simple strings to get specific types of data STR_COMPANY_NAME :{COMPANY} STR_COMPANY_NAME_COMPANY_NUM :{COMPANY} {COMPANY_NUM} diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index d64214f68d22a..a1a277703c728 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -11,6 +11,7 @@ #include "../void_map.h" #include "../signs_base.h" #include "../depot_base.h" +#include "../depot_func.h" #include "../fios.h" #include "../gamelog_internal.h" #include "../network/network.h" @@ -222,6 +223,7 @@ static inline RailType UpdateRailType(RailType rt, RailType min) void UpdateAllVirtCoords() { UpdateAllStationVirtCoords(); + UpdateAllDepotVirtCoords(); UpdateAllSignVirtCoords(); UpdateAllTownVirtCoords(); UpdateAllTextEffectVirtCoords(); diff --git a/src/station.cpp b/src/station.cpp index 0d4198ac6cb88..cf07e27f92e87 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -755,6 +755,9 @@ void Airport::AddHangar() */ void Airport::RemoveHangar() { + if (this->hangar == nullptr) return; + + /* TODO Check this. */ RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->hangar->index); for (Aircraft *a : Aircraft::Iterate()) { @@ -764,6 +767,7 @@ void Airport::RemoveHangar() a->current_order.MakeDummy(); } + this->hangar->Disuse(); delete this->hangar; this->hangar = nullptr; } diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 79e995079129a..33db4fc6673cb 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -44,6 +44,7 @@ #include "core/random_func.hpp" #include "core/backup_type.hpp" #include "depot_base.h" +#include "depot_func.h" #include "object_map.h" #include "object_base.h" #include "ai/ai.hpp" @@ -3005,6 +3006,8 @@ CommandCost CmdRenameTown(DoCommandFlag flags, TownID town_id, const std::string ClearAllStationCachedNames(); ClearAllIndustryCachedNames(); UpdateAllStationVirtCoords(); + UpdateAllDepotVirtCoords(); + RebuildViewportKdtree(); } return CommandCost(); } diff --git a/src/viewport.cpp b/src/viewport.cpp index 189296239831c..2bee590429a65 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -66,6 +66,8 @@ #include "viewport_func.h" #include "station_base.h" #include "waypoint_base.h" +#include "depot_base.h" +#include "depot_func.h" #include "town.h" #include "signs_base.h" #include "signs_func.h" @@ -1366,12 +1368,14 @@ static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi) bool show_waypoints = HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES) && _game_mode != GM_MENU; bool show_towns = HasBit(_display_opt, DO_SHOW_TOWN_NAMES) && _game_mode != GM_MENU; bool show_signs = HasBit(_display_opt, DO_SHOW_SIGNS) && !IsInvisibilitySet(TO_SIGNS); + bool show_depotsigns = _game_mode != GM_MENU; bool show_competitors = HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS); /* Collect all the items first and draw afterwards, to ensure layering */ std::vector stations; std::vector towns; std::vector signs; + std::vector depots; _viewport_sign_kdtree.FindContained(search_rect.left, search_rect.top, search_rect.right, search_rect.bottom, [&](const ViewportSignKdtreeItem & item) { switch (item.type) { @@ -1415,6 +1419,19 @@ static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi) break; } + case ViewportSignKdtreeItem::VKI_DEPOT: { + if (!show_depotsigns) break; + const Depot *depot = Depot::Get(item.id.depot); + + /* Only show depot name after the depot is removed. */ + if (depot->IsInUse()) break; + /* Don't draw if depot is owned by another company and competitor signs are hidden. */ + if (!show_competitors && _local_company != depot->owner) break; + + depots.push_back(depot); + break; + } + default: NOT_REACHED(); } @@ -1441,6 +1458,12 @@ static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi) (si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? INVALID_COLOUR : _company_colours[si->owner])); } + for (const auto *d : depots) { + SetDParam(0, d->veh_type); + SetDParam(1, d->index); + ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &d->sign, STR_VIEWPORT_DEPOT, STR_VIEWPORT_DEPOT_TINY, STR_NULL, COLOUR_GREY); + } + for (const auto *st : stations) { SetDParam(0, st->index); SetDParam(1, st->facilities); @@ -2237,6 +2260,7 @@ static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y) BaseStation *st = nullptr, *last_st = nullptr; Town *t = nullptr, *last_t = nullptr; Sign *si = nullptr, *last_si = nullptr; + Depot *dep = nullptr, *last_dep = nullptr; /* See ViewportAddKdtreeSigns() for details on the search logic */ _viewport_sign_kdtree.FindContained(search_rect.left, search_rect.top, search_rect.right, search_rect.bottom, [&](const ViewportSignKdtreeItem & item) { @@ -2268,6 +2292,12 @@ static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y) if (CheckClickOnViewportSign(vp, x, y, &si->sign)) last_si = si; break; + case ViewportSignKdtreeItem::VKI_DEPOT: + dep = Depot::Get(item.id.depot); + if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break; + if (CheckClickOnViewportSign(vp, x, y, &dep->sign)) last_dep = dep; + break; + default: NOT_REACHED(); } @@ -2287,6 +2317,9 @@ static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y) } else if (last_si != nullptr) { HandleClickOnSign(last_si); return true; + } else if (last_dep != nullptr) { + ShowDepotWindow(last_dep->index); + return true; } else { return false; } @@ -2310,6 +2343,23 @@ ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeStation(StationID id) return item; } +ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeDepot(DepotID id) +{ + ViewportSignKdtreeItem item; + item.type = VKI_DEPOT; + item.id.depot = id; + + const Depot *depot = Depot::Get(id); + + item.center = depot->sign.center; + item.top = depot->sign.top; + + /* Assume the sign can be a candidate for drawing, so measure its width */ + _viewport_sign_maxwidth = std::max(_viewport_sign_maxwidth, depot->sign.width_normal); + + return item; +} + ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeWaypoint(StationID id) { ViewportSignKdtreeItem item; @@ -2385,6 +2435,11 @@ void RebuildViewportKdtree() if (sign->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeSign(sign->index)); } + for (const Depot *dep : Depot::Iterate()) { + if (dep->IsInUse()) continue; + items.push_back(ViewportSignKdtreeItem::MakeDepot(dep->index)); + } + _viewport_sign_kdtree.Build(items.begin(), items.end()); } diff --git a/src/viewport_kdtree.h b/src/viewport_kdtree.h index 3c2f49c2e4c17..333c3685fca58 100644 --- a/src/viewport_kdtree.h +++ b/src/viewport_kdtree.h @@ -22,12 +22,14 @@ struct ViewportSignKdtreeItem { VKI_WAYPOINT, VKI_TOWN, VKI_SIGN, + VKI_DEPOT, }; ItemType type; union { StationID station; TownID town; SignID sign; + DepotID depot; } id; int32_t center; int32_t top; @@ -43,6 +45,8 @@ struct ViewportSignKdtreeItem { return this->id.town == other.id.town; case VKI_SIGN: return this->id.sign == other.id.sign; + case VKI_DEPOT: + return this->id.depot == other.id.depot; default: NOT_REACHED(); } @@ -59,6 +63,8 @@ struct ViewportSignKdtreeItem { return this->id.town < other.id.town; case VKI_SIGN: return this->id.sign < other.id.sign; + case VKI_DEPOT: + return this->id.depot < other.id.depot; default: NOT_REACHED(); } @@ -68,6 +74,7 @@ struct ViewportSignKdtreeItem { static ViewportSignKdtreeItem MakeWaypoint(StationID id); static ViewportSignKdtreeItem MakeTown(TownID id); static ViewportSignKdtreeItem MakeSign(SignID id); + static ViewportSignKdtreeItem MakeDepot(DepotID id); }; inline int32_t Kdtree_ViewportSignXYFunc(const ViewportSignKdtreeItem &item, int dim) From 126ad5ab31c742a97de4267ea952f6ba5b8f26e0 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 2 Apr 2021 15:11:02 +0200 Subject: [PATCH 026/143] Add: Draw roadbits in road depots. --- src/road_cmd.cpp | 71 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index d277079070e8f..e1f85fce84765 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -1468,11 +1468,21 @@ void DrawRoadCatenary(const TileInfo *ti) RoadBits tram = ROAD_NONE; if (IsTileType(ti->tile, MP_ROAD)) { - if (IsNormalRoad(ti->tile)) { - road = GetRoadBits(ti->tile, RTT_ROAD); - tram = GetRoadBits(ti->tile, RTT_TRAM); - } else if (IsLevelCrossing(ti->tile)) { - tram = road = (GetCrossingRailAxis(ti->tile) == AXIS_Y ? ROAD_X : ROAD_Y); + switch (GetRoadTileType(ti->tile)) { + case ROAD_TILE_NORMAL: + road = GetRoadBits(ti->tile, RTT_ROAD); + tram = GetRoadBits(ti->tile, RTT_TRAM); + break; + case ROAD_TILE_CROSSING: + tram = road = (GetCrossingRailAxis(ti->tile) == AXIS_Y ? ROAD_X : ROAD_Y); + break; + case ROAD_TILE_DEPOT: { + DiagDirection dir = GetRoadDepotDirection(ti->tile); + road = DiagDirToRoadBits(dir); + tram = DiagDirToRoadBits(dir); + break; + } + default: NOT_REACHED(); } } else if (IsTileType(ti->tile, MP_STATION)) { if (IsAnyRoadStop(ti->tile)) { @@ -1835,17 +1845,28 @@ static void DrawTile_Road(TileInfo *ti) case ROAD_TILE_DEPOT: { if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); - PaletteID palette = COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)); - RoadType road_rt = GetRoadTypeRoad(ti->tile); RoadType tram_rt = GetRoadTypeTram(ti->tile); - const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt == INVALID_ROADTYPE ? tram_rt : road_rt); + assert(road_rt != INVALID_ROADTYPE || tram_rt != INVALID_ROADTYPE); + const RoadTypeInfo *road_rti = road_rt != INVALID_ROADTYPE ? GetRoadTypeInfo(road_rt) : nullptr; + const RoadTypeInfo *tram_rti = tram_rt != INVALID_ROADTYPE ? GetRoadTypeInfo(tram_rt) : nullptr; + const RoadTypeInfo *main_rti = tram_rti != nullptr ? tram_rti : road_rti; + + DiagDirection dir = GetRoadDepotDirection(ti->tile); + uint road_offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); + uint tram_offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); - int relocation = GetCustomRoadSprite(rti, ti->tile, ROTSG_DEPOT); + + PaletteID pal = PAL_NONE; + const DrawTileSprites *dts = &_road_depot[dir]; + DrawGroundSprite(dts->ground.sprite, pal); + DrawRoadOverlays(ti, pal, road_rti, tram_rti, road_offset, tram_offset); + + int relocation = GetCustomRoadSprite(main_rti, ti->tile, ROTSG_DEPOT); bool default_gfx = relocation == 0; if (default_gfx) { - if (HasBit(rti->flags, ROTF_CATENARY)) { - if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK && road_rt == INVALID_ROADTYPE && !rti->UsesOverlay()) { + if (HasBit(main_rti->flags, ROTF_CATENARY)) { + if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK && road_rt == INVALID_ROADTYPE && !main_rti->UsesOverlay()) { /* Sprites with track only work for default tram */ relocation = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_ROAD_DEPOT; default_gfx = false; @@ -1858,21 +1879,11 @@ static void DrawTile_Road(TileInfo *ti) relocation -= SPR_ROAD_DEPOT; } - DiagDirection dir = GetRoadDepotDirection(ti->tile); - const DrawTileSprites *dts = &_road_depot[dir]; - DrawGroundSprite(dts->ground.sprite, PAL_NONE); + /* Draw road, tram catenary */ + DrawRoadCatenary(ti); - if (default_gfx) { - uint offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); - if (rti->UsesOverlay()) { - SpriteID ground = GetCustomRoadSprite(rti, ti->tile, ROTSG_OVERLAY); - if (ground != 0) DrawGroundSprite(ground + offset, PAL_NONE); - } else if (road_rt == INVALID_ROADTYPE) { - DrawGroundSprite(SPR_TRAMWAY_OVERLAY + offset, PAL_NONE); - } - } + DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile))); - DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, palette); break; } } @@ -1890,7 +1901,9 @@ void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt) { PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company); + RoadTramType rtt = GetRoadTramType(rt); const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + uint road_offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); int relocation = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_DEPOT); bool default_gfx = relocation == 0; if (default_gfx) { @@ -1911,6 +1924,16 @@ void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt) const DrawTileSprites *dts = &_road_depot[dir]; DrawSprite(dts->ground.sprite, PAL_NONE, x, y); + if (rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND); + DrawSprite(ground + road_offset, PAL_NONE, x, y); + ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY); + if (ground != 0) DrawSprite(ground + road_offset, PAL_NONE, x, y); + } else if (rtt == RTT_TRAM) { + DrawSprite(SPR_TRAMWAY_TRAM + road_offset, PAL_NONE, x, y); + DrawSprite(SPR_TRAMWAY_OVERLAY + road_offset, PAL_NONE, x, y); + } + if (default_gfx) { uint offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); if (rti->UsesOverlay()) { From d8f9bc3fd0a19960516f7b705e25baa7f2ec5826 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 2 Apr 2021 16:21:34 +0200 Subject: [PATCH 027/143] Feature: Road depots may have a road and tram type at the same time. --- src/road_cmd.cpp | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index e1f85fce84765..10f96de6cfa3d 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -524,9 +524,20 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec return CommandCost(EXPENSES_CONSTRUCTION, RoadClearCost(existing_rt) * 2); } - default: - case ROAD_TILE_DEPOT: - return CMD_ERROR; + case ROAD_TILE_DEPOT: { + if (!HasRoadTypeRoad(tile) || !HasRoadTypeTram(tile)) return CMD_ERROR; + if (flags & DC_EXEC) { + Company *c = Company::GetIfValid(GetTileOwner(tile)); + c->infrastructure.road[GetRoadType(tile, rtt)] -= ROAD_DEPOT_TRACKBIT_FACTOR; + DirtyCompanyInfrastructureWindows(c->index); + SetRoadType(tile, rtt, INVALID_ROADTYPE); + Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), false); + MarkTileDirtyByTile(tile); + } + return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); + } + + default: NOT_REACHED(); } } @@ -712,7 +723,16 @@ CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, R break; case ROAD_TILE_DEPOT: - if ((GetAnyRoadBits(tile, rtt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT); + if (DiagDirToRoadBits(GetRoadDepotDirection(tile)) == pieces) { + /* Check if we can add a new road/tram type if none present. */ + if (HasTileRoadType(tile, rtt)) { + return_cmd_error(STR_ERROR_ALREADY_BUILT); + } + /* We may add a new road type. */ + cost.AddCost(_price[PR_BUILD_DEPOT_ROAD]); + break; + } + goto do_clear; default: NOT_REACHED(); @@ -886,7 +906,12 @@ do_clear:; switch (GetTileType(tile)) { case MP_ROAD: { RoadTileType rttype = GetRoadTileType(tile); - if (existing == ROAD_NONE || rttype == ROAD_TILE_CROSSING) { + if (rttype == ROAD_TILE_DEPOT) { + SetRoadType(tile, rtt, rt); + UpdateCompanyRoadInfrastructure(rt, _current_company, ROAD_DEPOT_TRACKBIT_FACTOR); + Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), true); + break; + } else if (existing == ROAD_NONE || rttype == ROAD_TILE_CROSSING) { SetRoadType(tile, rtt, rt); SetRoadOwner(tile, rtt, company); if (rtt == RTT_ROAD) SetTownIndex(tile, town_id); @@ -1227,14 +1252,19 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags) CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; + CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); + RoadType rt = GetRoadTypeRoad(tile); + RoadType tt = GetRoadTypeTram(tile); + if (rt != INVALID_ROADTYPE && tt != INVALID_ROADTYPE) cost.AddCost(_price[PR_CLEAR_DEPOT_ROAD]); + if (flags & DC_EXEC) { Depot *depot = Depot::GetByTile(tile); Company *c = Company::GetIfValid(depot->owner); if (c != nullptr) { /* A road depot has two road bits. */ RoadType rt = GetRoadTypeRoad(tile); - if (rt == INVALID_ROADTYPE) rt = GetRoadTypeTram(tile); - c->infrastructure.road[rt] -= ROAD_DEPOT_TRACKBIT_FACTOR; + if (rt != INVALID_ROADTYPE) c->infrastructure.road[rt] -= ROAD_DEPOT_TRACKBIT_FACTOR; + if (tt != INVALID_ROADTYPE) c->infrastructure.road[tt] -= ROAD_DEPOT_TRACKBIT_FACTOR; DirtyCompanyInfrastructureWindows(c->index); } @@ -1242,7 +1272,7 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags) depot->AfterAddRemove(TileArea(tile), false); } - return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); + return cost; } static CommandCost ClearTile_Road(TileIndex tile, DoCommandFlag flags) From 1fda004905dbdf8639663493776f6d3b3e018a6e Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 17:50:40 +0200 Subject: [PATCH 028/143] Add: Add settings for building new types of depots. --- src/lang/english.txt | 15 +++++++++ src/saveload/afterload.cpp | 7 +++++ src/saveload/saveload.h | 2 ++ src/settings_gui.cpp | 4 +++ src/settings_table.cpp | 5 +++ src/settings_type.h | 10 ++++++ src/table/settings/game_settings.ini | 46 ++++++++++++++++++++++++++++ 7 files changed, 89 insertions(+) diff --git a/src/lang/english.txt b/src/lang/english.txt index d237f8766f429..c1f55ad1a3975 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1622,6 +1622,19 @@ STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS :Allow to join d STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS_HELPTEXT :Allow adding parts to a depot without directly touching the existing parts. Needs Ctrl+Click while placing the new parts STR_CONFIG_SETTING_DEPOT_SPREAD :Maximum depot spread: {STRING2} STR_CONFIG_SETTING_DEPOT_SPREAD_HELPTEXT :Maximum area the parts of a single depot may be spread out on + +STR_CONFIG_SETTING_RAIL_DEPOT_TYPES :Rail depot types: {STRING2} +STR_CONFIG_SETTING_RAIL_DEPOT_TYPES_HELPTEXT :Available rail depot types for construction for human players +STR_CONFIG_SETTING_ROAD_DEPOT_TYPES :Road depot types: {STRING2} +STR_CONFIG_SETTING_ROAD_DEPOT_TYPES_HELPTEXT :Available road depot types for construction for human players +STR_CONFIG_SETTING_WATER_DEPOT_TYPES :Water depot types: {STRING2} +STR_CONFIG_SETTING_WATER_DEPOT_TYPES_HELPTEXT :Available water depot types for construction for human players +###length 3 +STR_CONFIG_SETTING_ONLY_STANDARD_DEPOT :Standard depots +STR_CONFIG_SETTING_ONLY_EXTENDED_DEPOT :Extended depots +STR_CONFIG_SETTING_BOTH_DEPOT_TYPES :Both depot types +###next-name-looks-similar + STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL :Allow replacing rail vehicles with incompatible rail types: {STRING2} STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL_HELPTEXT :Allow replacing rail vehicles even if they are not compatible by rail type STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_ROAD :Allow replacing road vehicles with incompatible road types: {STRING2} @@ -5134,6 +5147,8 @@ STR_ERROR_CAN_T_BUILD_TRAM_DEPOT :{WHITE}Can't bu STR_ERROR_CAN_T_BUILD_SHIP_DEPOT :{WHITE}Can't build ship depot here... STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING_DEPOT :{WHITE}... adjoins more than one existing depot +STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE :{WHITE}... depot type not available + STR_ERROR_CAN_T_RENAME_DEPOT :{WHITE}Can't rename depot... STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT :{WHITE}... must be stopped inside a depot diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index a1a277703c728..b91e10fad3608 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -820,6 +820,13 @@ bool AfterLoadGame() _settings_game.depot.allow_no_comp_roadtype_replacements = false; } + if (IsSavegameVersionBefore(SLV_EXTENDED_DEPOTS)) { + /* Set standard depots as the only available depots. */ + _settings_game.depot.rail_depot_types = 1; + _settings_game.depot.road_depot_types = 1; + _settings_game.depot.water_depot_types = 1; + } + /* Load the sprites */ GfxLoadSprites(); LoadStringWidthTable(); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index a6691e3120462..c67a65359a46c 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -396,6 +396,8 @@ enum SaveLoadVersion : uint16_t { SLV_KEEP_REMOVED_DEPOTS, ///< 320 PR#XXXXX Keep remove depots for a while. + SLV_EXTENDED_DEPOTS, ///< 321 PR#8480 Extended depots for rail, road and water transport. + SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 739a339a7a773..9a24e44b47a3e 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2150,6 +2150,10 @@ static SettingsContainer &GetSettingsTree() depots->Add(new SettingEntry("depot.depot_spread")); depots->Add(new SettingEntry("depot.distant_join_depots")); + depots->Add(new SettingEntry("depot.rail_depot_types")); + depots->Add(new SettingEntry("depot.road_depot_types")); + depots->Add(new SettingEntry("depot.water_depot_types")); + depots->Add(new SettingEntry("depot.allow_no_comp_railtype_replacements")); depots->Add(new SettingEntry("depot.allow_no_comp_roadtype_replacements")); } diff --git a/src/settings_table.cpp b/src/settings_table.cpp index 0f262ebfe3344..72100e8f6b820 100644 --- a/src/settings_table.cpp +++ b/src/settings_table.cpp @@ -396,6 +396,11 @@ static void InvalidateReplacementWindows(int32_t) InvalidateWindowClassesData(WC_REPLACE_VEHICLE); } +static void DepotSettingsChanged(int32_t) +{ + CloseWindowByClass(WC_BUILD_TOOLBAR); +} + /** * Update any possible saveload window and delete any newgrf dialogue as * its widget parts might change. Reinit all windows as it allows access to the diff --git a/src/settings_type.h b/src/settings_type.h index 4d1451e0ae2b4..a3c354849d0aa 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -570,11 +570,21 @@ struct StationSettings { uint8_t station_spread; ///< amount a station may spread }; +enum DepotTypes : uint8_t { + ONLY_STANDARD_DEPOT_TYPE = 1, + ONLY_EXTENDED_DEPOT_TYPE = 2, + BOTH_DEPOT_TYPES = 3, +}; + /** Settings related to depots. */ struct DepotSettings { uint8_t depot_spread; ///< amount a depot may spread bool distant_join_depots; ///< allow to join non-adjacent depots + uint8_t rail_depot_types; ///< allowed rail depot types for contruction + uint8_t road_depot_types; ///< allowed road depot types for contruction + uint8_t water_depot_types; ///< allowed water depot types for contruction + bool allow_no_comp_railtype_replacements; ///< allow replacing rail vehicles even if rail type is not compatible bool allow_no_comp_roadtype_replacements; ///< allow replacing road vehicles even if road type is not compatible }; diff --git a/src/table/settings/game_settings.ini b/src/table/settings/game_settings.ini index 269da0c70bdd2..75154462a1475 100644 --- a/src/table/settings/game_settings.ini +++ b/src/table/settings/game_settings.ini @@ -23,6 +23,7 @@ static void StationCatchmentChanged(int32_t new_value); static void MaxVehiclesChanged(int32_t new_value); static bool CheckDifferentRailRoadTypesReplacements(int32_t &new_value); static void InvalidateReplacementWindows(int32_t new_value); +static void DepotSettingsChanged(int32_t new_value); static const SettingVariant _game_settings_table[] = { [post-amble] @@ -147,6 +148,51 @@ str = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS_HELPTEXT post_cb = [](auto) { CloseWindowById(WC_SELECT_STATION, 0); } +[SDT_VAR] +var = depot.rail_depot_types +type = SLE_UINT8 +from = SLV_EXTENDED_DEPOTS +flags = SF_GUI_DROPDOWN +def = 1 +min = 1 +max = 3 +interval = 1 +str = STR_CONFIG_SETTING_RAIL_DEPOT_TYPES +strhelp = STR_CONFIG_SETTING_RAIL_DEPOT_TYPES_HELPTEXT +strval = STR_CONFIG_SETTING_ONLY_STANDARD_DEPOT +post_cb = DepotSettingsChanged +cat = SC_EXPERT + +[SDT_VAR] +var = depot.road_depot_types +type = SLE_UINT8 +from = SLV_EXTENDED_DEPOTS +flags = SF_GUI_DROPDOWN +def = 1 +min = 1 +max = 3 +interval = 1 +str = STR_CONFIG_SETTING_ROAD_DEPOT_TYPES +strhelp = STR_CONFIG_SETTING_ROAD_DEPOT_TYPES_HELPTEXT +strval = STR_CONFIG_SETTING_ONLY_STANDARD_DEPOT +post_cb = DepotSettingsChanged +cat = SC_EXPERT + +[SDT_VAR] +var = depot.water_depot_types +type = SLE_UINT8 +from = SLV_EXTENDED_DEPOTS +flags = SF_GUI_DROPDOWN +def = 1 +min = 1 +max = 3 +interval = 1 +str = STR_CONFIG_SETTING_WATER_DEPOT_TYPES +strhelp = STR_CONFIG_SETTING_WATER_DEPOT_TYPES_HELPTEXT +strval = STR_CONFIG_SETTING_ONLY_STANDARD_DEPOT +post_cb = DepotSettingsChanged +cat = SC_EXPERT + [SDT_BOOL] var = depot.distant_join_depots from = SLV_DEPOT_SPREAD From 66cb1e78a50b8fbd2c077d2e7755892adb64091b Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 27 Mar 2021 13:51:37 +0100 Subject: [PATCH 029/143] Add: Add and adapt some functions for extended depots. --- src/depot.cpp | 14 ++++- src/depot_map.h | 92 ++++++++++++++++++++++++++++++++ src/depot_type.h | 8 +++ src/lang/english.txt | 1 + src/pathfinder/pathfinder_func.h | 11 +++- src/vehicle.cpp | 16 ++++++ 6 files changed, 139 insertions(+), 3 deletions(-) diff --git a/src/depot.cpp b/src/depot.cpp index 88518039cfb86..b59609613eb8d 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -112,19 +112,29 @@ void Depot::Disuse() * Of all the depot parts a depot has, return the best destination for a vehicle. * @param v The vehicle. * @param dep The depot vehicle \a v is heading for. - * @return The closest part of depot to vehicle v. + * @return The free and closest (if none is free, just closest) part of depot to vehicle v. */ TileIndex Depot::GetBestDepotTile(Vehicle *v) const { assert(this->veh_type == v->type); TileIndex best_depot = INVALID_TILE; + DepotReservation best_found_type = DEPOT_RESERVATION_END; uint best_distance = UINT_MAX; for (const auto &tile : this->depot_tiles) { + bool check_south = v->type == VEH_ROAD; uint new_distance = DistanceManhattan(v->tile, tile); - if (new_distance < best_distance) { +again: + DepotReservation depot_reservation = GetDepotReservation(tile, check_south); + if (((best_found_type == depot_reservation) && new_distance < best_distance) || (depot_reservation < best_found_type)) { best_depot = tile; best_distance = new_distance; + best_found_type = depot_reservation; + } + if (check_south) { + /* For road vehicles, check north direction as well. */ + check_south = false; + goto again; } } diff --git a/src/depot_map.h b/src/depot_map.h index 51639a3b418a5..18d172fbeb811 100644 --- a/src/depot_map.h +++ b/src/depot_map.h @@ -83,4 +83,96 @@ inline VehicleType GetDepotVehicleType(Tile t) } } +/** Return true if a tile belongs to an extended depot. */ +static inline bool IsExtendedDepot(Tile tile) { + assert(IsValidTile(tile)); + assert(IsDepotTile(tile)); + if (IsAirportTile(tile)) return false; + return HasBit(tile.m5(), 5); +} + +/** Return true if a tile belongs to an extended depot. */ +static inline bool IsExtendedDepotTile(TileIndex tile) { + if (!IsValidTile(tile)) return false; + if (!IsDepotTile(tile)) return false; + return IsExtendedDepot(tile); +} + +/** + * Has this depot some vehicle servicing or stopped inside? + * @param tile tile of the depot. + * @param south_dir In case of road transport, return reservation facing south if true. + * @return The type of reservation on this tile (empty, servicing or occupied). + * @pre is a depot tile + */ +static inline DepotReservation GetDepotReservation(Tile t, bool south_dir = false) +{ + assert(IsDepotTile(t)); + if (!IsExtendedDepot(t)) return DEPOT_RESERVATION_EMPTY; + if (south_dir) { + assert(GetDepotVehicleType(t) == VEH_ROAD); + return (DepotReservation)GB(t.m6(), 4, 2); + } + return (DepotReservation)GB(t.m4(), 6, 2); +} + +/** + * Is this a platform/depot tile full with stopped vehicles? + * @param tile tile of the depot. + * @param south_dir In case of road transport, check reservation facing south if true. + * @return the type of reservation of the depot. + * @pre is a depot tile + */ +static inline bool IsDepotFullWithStoppedVehicles(TileIndex t, bool south_dir = false) +{ + assert(IsDepotTile(t)); + if (!IsExtendedDepot(t)) return false; + return GetDepotReservation(t, south_dir) == DEPOT_RESERVATION_FULL_STOPPED_VEH; +} + + +/** + * Has this depot tile/platform some vehicle inside? + * @param tile tile of the depot. + * @param south_dir In case of road transport, check reservation facing south if true. + * @return true iff depot tile/platform has no vehicle. + * @pre IsExtendedDepotTile + */ +static inline bool IsExtendedDepotEmpty(TileIndex t, bool south_dir = false) +{ + assert(IsExtendedDepotTile(t)); + return GetDepotReservation(t, south_dir) == DEPOT_RESERVATION_EMPTY; +} + +/** + * Mark whether this depot has a ship inside. + * @param tile of the depot. + * @param reservation type of reservation + * @param south_dir Whether to set south direction reservation. + * @pre tile is an extended ship depot. + */ +static inline void SetDepotReservation(Tile t, DepotReservation reservation, bool south_dir = false) +{ + assert(IsDepotTile(t)); + if (!IsExtendedDepot(t)) return; + switch (GetTileType(t)) { + default: NOT_REACHED(); + case MP_RAILWAY: + break; + case MP_ROAD: + if (south_dir) { + SB(t.m6(), 4, 2, reservation); + return; + } + break; + case MP_WATER: + assert(GetDepotReservation(t) == GetDepotReservation(GetOtherShipDepotTile(t))); + SB(Tile(GetOtherShipDepotTile(t)).m4(), 6, 2, reservation); + break; + case MP_STATION: return; + } + + SB(t.m4(), 6, 2, reservation); +} + #endif /* DEPOT_MAP_H */ diff --git a/src/depot_type.h b/src/depot_type.h index a03a33b4f8753..62b222289ad34 100644 --- a/src/depot_type.h +++ b/src/depot_type.h @@ -20,4 +20,12 @@ static const uint MAX_LENGTH_DEPOT_NAME_CHARS = 32; ///< The maximum length of a static const uint DEF_MAX_DEPOT_SPREAD = 12; +/** Type of reservation of extended ship depots. */ +enum DepotReservation { + DEPOT_RESERVATION_EMPTY = 0, ///< No vehicle servicing/stopped on depot tile/platform. + DEPOT_RESERVATION_IN_USE = 1, ///< At least a vehicle is in the depot, but the depot tile is not full of stopped vehicles. + DEPOT_RESERVATION_FULL_STOPPED_VEH = 2, ///< The depot tile/platform is full with stopped vehicles. + DEPOT_RESERVATION_END +}; + #endif /* DEPOT_TYPE_H */ diff --git a/src/lang/english.txt b/src/lang/english.txt index c1f55ad1a3975..abb158714445c 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -910,6 +910,7 @@ STR_NEWS_TRAIN_IS_STUCK :{WHITE}{VEHICLE STR_NEWS_VEHICLE_IS_LOST :{WHITE}{VEHICLE} is lost STR_NEWS_VEHICLE_UNPROFITABLE_YEAR :{WHITE}{VEHICLE}'s profit last year was {CURRENCY_LONG} STR_NEWS_VEHICLE_UNPROFITABLE_PERIOD :{WHITE}{VEHICLE}'s profit last period was {CURRENCY_LONG} +STR_NEWS_VEHICLE_CAN_T_FIND_FREE_DEPOT :{WHITE}{VEHICLE} can't find a free depot STR_NEWS_AIRCRAFT_DEST_TOO_FAR :{WHITE}{VEHICLE} can't get to the next destination because it is out of range STR_NEWS_ORDER_REFIT_FAILED :{WHITE}{VEHICLE} stopped because an ordered refit failed diff --git a/src/pathfinder/pathfinder_func.h b/src/pathfinder/pathfinder_func.h index 781d35645895a..410776511b5b0 100644 --- a/src/pathfinder/pathfinder_func.h +++ b/src/pathfinder/pathfinder_func.h @@ -66,13 +66,22 @@ static inline TileIndex CalcClosestDepotTile(DepotID depot_id, TileIndex tile) } TileIndex best_tile = INVALID_TILE; + DepotReservation best_found_type = dep->veh_type == VEH_SHIP ? DEPOT_RESERVATION_END : DEPOT_RESERVATION_EMPTY; uint best_distance = UINT_MAX; for (auto const &depot_tile : dep->depot_tiles) { uint new_distance = DistanceManhattan(depot_tile, tile); - if (new_distance < best_distance) { + bool check_south_direction = dep->veh_type == VEH_ROAD; +again: + DepotReservation depot_reservation = GetDepotReservation(depot_tile, check_south_direction); + if (((best_found_type == depot_reservation) && new_distance < best_distance) || (depot_reservation < best_found_type)) { best_tile = depot_tile; best_distance = new_distance; + best_found_type = depot_reservation; + } + if (check_south_direction) { + check_south_direction = false; + goto again; } } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index d10f8dcb0f332..64876f780bdae 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -790,6 +790,22 @@ void Vehicle::ShiftDates(TimerGameEconomy::Date interval) */ void Vehicle::HandlePathfindingResult(bool path_found) { + if (this->dest_tile != INVALID_TILE && IsDepotTypeTile(this->dest_tile, (TransportType)this->type) && IsDepotFullWithStoppedVehicles(this->dest_tile)) { + /* Vehicle cannot find a free depot. */ + /* Were we already lost? */ + if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return; + + /* It is first time the problem occurred, set the "lost" flag. */ + SetBit(this->vehicle_flags, VF_PATHFINDER_LOST); + /* Notify user about the event. */ + AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index)); + if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) { + SetDParam(0, this->index); + AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_CAN_T_FIND_FREE_DEPOT, this->index); + } + return; + } + if (path_found) { /* Route found, is the vehicle marked with "lost" flag? */ if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return; From 19e73d866898259dc14e4f3fa42705d973c0148c Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 17:52:41 +0200 Subject: [PATCH 030/143] Change: Add a wait counter for all vehicle types. --- src/saveload/vehicle_sl.cpp | 4 +++- src/vehicle_base.h | 25 +++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index 4e42de39622ad..f28d04b0e59c1 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -665,6 +665,7 @@ class SlVehicleCommon : public DefaultSaveLoadHandler SLE_VAR(Vehicle, progress, SLE_UINT8), SLE_VAR(Vehicle, vehstatus, SLE_UINT8), + SLE_CONDVAR(Vehicle, wait_counter, SLE_UINT16, SLV_EXTENDED_DEPOTS, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, SLV_5, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, last_loading_station, SLE_UINT16, SLV_182, SL_MAX_VERSION), @@ -793,7 +794,7 @@ class SlVehicleTrain : public DefaultSaveLoadHandler { SLE_CONDVAR(Train, flags, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SLV_100), SLE_CONDVAR(Train, flags, SLE_UINT16, SLV_100, SL_MAX_VERSION), - SLE_CONDVAR(Train, wait_counter, SLE_UINT16, SLV_136, SL_MAX_VERSION), + SLE_CONDVAR(Train, wait_counter, SLE_UINT16, SLV_136, SLV_EXTENDED_DEPOTS), SLE_CONDVAR(Train, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION), }; inline const static SaveLoadCompatTable compat_description = _vehicle_train_sl_compat; @@ -1072,6 +1073,7 @@ struct VEHSChunkHandler : ChunkHandler { default: SlErrorCorrupt("Invalid vehicle type"); } + if (IsSavegameVersionBefore(SLV_EXTENDED_DEPOTS)) assert(v->type == VEH_TRAIN || v->wait_counter == 0); SlObject(v, slt); if (_cargo_count != 0 && IsCompanyBuildableVehicleType(v) && CargoPacket::CanAllocateItem()) { diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 65fac6e1035f7..b13e6bda8ed21 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -313,23 +313,24 @@ struct Vehicle : VehiclePool::PoolItem<&_vehicle_pool>, BaseVehicle, BaseConsist * 0xff == reserved for another custom sprite */ uint8_t spritenum; - uint8_t x_extent; ///< x-extent of vehicle bounding box - uint8_t y_extent; ///< y-extent of vehicle bounding box - uint8_t z_extent; ///< z-extent of vehicle bounding box - int8_t x_bb_offs; ///< x offset of vehicle bounding box - int8_t y_bb_offs; ///< y offset of vehicle bounding box - int8_t x_offs; ///< x offset for vehicle sprite - int8_t y_offs; ///< y offset for vehicle sprite + uint8_t x_extent; ///< x-extent of vehicle bounding box + uint8_t y_extent; ///< y-extent of vehicle bounding box + uint8_t z_extent; ///< z-extent of vehicle bounding box + int8_t x_bb_offs; ///< x offset of vehicle bounding box + int8_t y_bb_offs; ///< y offset of vehicle bounding box + int8_t x_offs; ///< x offset for vehicle sprite + int8_t y_offs; ///< y offset for vehicle sprite EngineID engine_type; ///< The type of engine used for this vehicle. TextEffectID fill_percent_te_id; ///< a text-effect id to a loading indicator object UnitID unitnumber; ///< unit number, for display purposes only - uint16_t cur_speed; ///< current speed - uint8_t subspeed; ///< fractional speed - uint8_t acceleration; ///< used by train & aircraft - uint32_t motion_counter; ///< counter to occasionally play a vehicle sound. - uint8_t progress; ///< The percentage (if divided by 256) this vehicle already crossed the tile unit. + uint16_t cur_speed; ///< current speed + uint8_t subspeed; ///< fractional speed + uint8_t acceleration; ///< used by train & aircraft + uint32_t motion_counter; ///< counter to occasionally play a vehicle sound. + uint16_t wait_counter; ///< waiting ticks (servicing, waiting in front of a signal or forced proceeding) + uint8_t progress; ///< The percentage (if divided by 256) this vehicle already crossed the tile unit. uint8_t waiting_triggers; ///< Triggers to be yet matched before rerandomizing the random bits. uint16_t random_bits; ///< Bits used for randomized variational spritegroups. From b169aa56e0d9ac916c21792ea77b721f5465d894 Mon Sep 17 00:00:00 2001 From: Juanjo Date: Sat, 23 Aug 2014 18:22:18 +0200 Subject: [PATCH 031/143] Add: Add a function to check if a window has a certain widget. --- src/window_gui.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/window_gui.h b/src/window_gui.h index 1c5b7d8d5c84f..a082e141f05d9 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -336,6 +336,11 @@ struct Window : ZeroedMemoryAllocator { template inline NWID *GetWidget(WidgetID widnum); + inline bool HasWidget(WidgetID widnum) const + { + return this->GetWidget(widnum) != nullptr; + } + const Scrollbar *GetScrollbar(WidgetID widnum) const; Scrollbar *GetScrollbar(WidgetID widnum); From a686644f2041faf5e7d2c0870b91afb8418bd1ea Mon Sep 17 00:00:00 2001 From: Juanjo Date: Fri, 27 Dec 2013 18:30:36 +0000 Subject: [PATCH 032/143] Add: Add two "tracks": depot and wormhole. --- src/track_type.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/track_type.h b/src/track_type.h index e3c3f22b6792f..89a30cd5d2284 100644 --- a/src/track_type.h +++ b/src/track_type.h @@ -14,7 +14,8 @@ /** * These are used to specify a single track. - * Can be translated to a trackbit with TrackToTrackbit + * Can be translated to a trackbit with TrackToTrackbit. + * TRACK_WORMHOLE and TRACK_DEPOT do not represent single tracks but states; they cannot be translated to trackbits. */ enum Track : uint8_t { TRACK_BEGIN = 0, ///< Used for iterations @@ -24,7 +25,9 @@ enum Track : uint8_t { TRACK_LOWER = 3, ///< Track in the lower corner of the tile (south) TRACK_LEFT = 4, ///< Track in the left corner of the tile (west) TRACK_RIGHT = 5, ///< Track in the right corner of the tile (east) - TRACK_END, ///< Used for iterations + TRACK_END = 6, ///< Used for iterations + TRACK_WORMHOLE = TRACK_END, + TRACK_DEPOT = 7, INVALID_TRACK = 0xFF, ///< Flag for an invalid track }; @@ -49,8 +52,8 @@ enum TrackBits : uint8_t { TRACK_BIT_3WAY_NW = TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_LEFT, ///< "Arrow" to the north-west TRACK_BIT_ALL = TRACK_BIT_CROSS | TRACK_BIT_HORZ | TRACK_BIT_VERT, ///< All possible tracks TRACK_BIT_MASK = 0x3FU, ///< Bitmask for the first 6 bits - TRACK_BIT_WORMHOLE = 0x40U, ///< Bitflag for a wormhole (used for tunnels) - TRACK_BIT_DEPOT = 0x80U, ///< Bitflag for a depot + TRACK_BIT_WORMHOLE = 1U << TRACK_WORMHOLE, ///< Bitflag for a wormhole (used for tunnels) + TRACK_BIT_DEPOT = 1U << TRACK_DEPOT, ///< Bitflag for a depot INVALID_TRACK_BIT = 0xFF, ///< Flag for an invalid trackbits value }; DECLARE_ENUM_AS_BIT_SET(TrackBits) From 6382cd2424fb1aa230e8e6cf41d9f64eea9b3784 Mon Sep 17 00:00:00 2001 From: Juanjo Date: Fri, 27 Dec 2013 18:50:44 +0000 Subject: [PATCH 033/143] Change: Adapt IsInDepot() for ships. --- src/ship.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ship.h b/src/ship.h index c47be05436a4e..927dc171ef5fa 100644 --- a/src/ship.h +++ b/src/ship.h @@ -43,7 +43,7 @@ struct Ship final : public SpecializedVehicle { int GetDisplayMaxSpeed() const override { return this->vcache.cached_max_speed / 2; } int GetCurrentMaxSpeed() const override { return std::min(this->vcache.cached_max_speed, this->current_order.GetMaxSpeed() * 2); } Money GetRunningCost() const override; - bool IsInDepot() const override { return this->state == TRACK_BIT_DEPOT; } + bool IsInDepot() const override { return HasBit((uint8_t)this->state, TRACK_DEPOT); } bool Tick() override; void OnNewCalendarDay() override; void OnNewEconomyDay() override; From d6935e3c63479daeef97369bf9fc1e901e910c5e Mon Sep 17 00:00:00 2001 From: Juanjo Date: Fri, 27 Dec 2013 18:06:30 +0000 Subject: [PATCH 034/143] Change: Adapt GetVehicleTrackdir() for ships. --- src/ship_cmd.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 1695c4e153110..4d7ce518ad22f 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -290,11 +290,12 @@ Trackdir Ship::GetVehicleTrackdir() const if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR; if (this->IsInDepot()) { - /* We'll assume the ship is facing outwards */ - return DiagDirToDiagTrackdir(GetShipDepotDirection(this->tile)); + /* Only old depots need it. */ + /* We'll assume the ship is facing outwards. */ + if (this->state == TRACK_BIT_DEPOT) return DiagDirToDiagTrackdir(GetShipDepotDirection(this->tile)); } - if (this->state == TRACK_BIT_WORMHOLE) { + if (this->state == TRACK_BIT_WORMHOLE || this->IsInDepot()) { /* ship on aqueduct, so just use its direction and assume a diagonal track */ return DiagDirToDiagTrackdir(DirToDiagDir(this->direction)); } From 859c8867a0a0a3d0d29c41afde3904128a9bc2ab Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 17:58:37 +0200 Subject: [PATCH 035/143] Feature: Allow building extended ship depots. --- src/dock_gui.cpp | 49 +++++++++++++++++++++++++++----- src/lang/english.txt | 6 ++-- src/script/api/script_marine.cpp | 2 +- src/water_cmd.cpp | 11 +++++-- src/water_cmd.h | 2 +- src/water_map.h | 7 +++-- src/widgets/dock_widget.h | 3 +- 7 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index 2cc37266a5610..2b2a369665f12 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -113,7 +113,12 @@ struct BuildDocksToolbarWindow : Window { { if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_DT_STATION)) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); - if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_DT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + + if (_game_mode == GM_NORMAL && + ((this->HasWidget(WID_DT_DEPOT) && this->IsWidgetLowered(WID_DT_DEPOT)) || + (this->HasWidget(WID_DT_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_DT_EXTENDED_DEPOT)))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } this->Window::Close(); } @@ -130,6 +135,7 @@ struct BuildDocksToolbarWindow : Window { bool can_build = CanBuildVehicleInfrastructure(VEH_SHIP); this->SetWidgetsDisabledState(!can_build, WID_DT_DEPOT, + WID_DT_EXTENDED_DEPOT, WID_DT_STATION, WID_DT_BUOY); if (!can_build) { @@ -140,11 +146,13 @@ struct BuildDocksToolbarWindow : Window { if (_game_mode != GM_EDITOR) { if (!can_build) { /* Show in the tooltip why this button is disabled. */ - this->GetWidget(WID_DT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); + if (this->HasWidget(WID_DT_DEPOT)) this->GetWidget(WID_DT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); + if (this->HasWidget(WID_DT_EXTENDED_DEPOT)) this->GetWidget(WID_DT_EXTENDED_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); this->GetWidget(WID_DT_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); this->GetWidget(WID_DT_BUOY)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); } else { - this->GetWidget(WID_DT_DEPOT)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP); + if (this->HasWidget(WID_DT_DEPOT)) this->GetWidget(WID_DT_DEPOT)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP); + if (this->HasWidget(WID_DT_EXTENDED_DEPOT)) this->GetWidget(WID_DT_EXTENDED_DEPOT)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUILD_EXTENDED_DEPOT_TOOLTIP); this->GetWidget(WID_DT_STATION)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP); this->GetWidget(WID_DT_BUOY)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP); } @@ -167,6 +175,7 @@ struct BuildDocksToolbarWindow : Window { break; case WID_DT_DEPOT: // Build depot button + case WID_DT_EXTENDED_DEPOT: if (HandlePlacePushButton(this, widget, SPR_CURSOR_SHIP_DEPOT, HT_RECT)) { ShowBuildDocksDepotPicker(this); } @@ -209,7 +218,8 @@ struct BuildDocksToolbarWindow : Window { PlaceProc_DemolishArea(tile); break; - case WID_DT_DEPOT: { // Build depot button + case WID_DT_DEPOT: // Build depot button + case WID_DT_EXTENDED_DEPOT: { CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP); ViewportPlaceMethod vpm = _ship_depot_direction != AXIS_X ? VPM_LIMITED_X_FIXED_Y : VPM_LIMITED_Y_FIXED_X; @@ -274,8 +284,10 @@ struct BuildDocksToolbarWindow : Window { break; case DDSP_BUILD_DEPOT: { bool adjacent = _ctrl_pressed; + bool extended = this->last_clicked_widget == WID_DT_EXTENDED_DEPOT; + auto proc = [=](DepotID join_to) -> bool { - return Command::Post(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, start_tile, _ship_depot_direction, adjacent, join_to, end_tile); + return Command::Post(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, start_tile, _ship_depot_direction, adjacent, extended, join_to, end_tile); }; ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_SHIP); @@ -291,7 +303,11 @@ struct BuildDocksToolbarWindow : Window { { if (_game_mode != GM_EDITOR && this->IsWidgetLowered(WID_DT_STATION)) SetViewportCatchmentStation(nullptr, true); - if (_game_mode != GM_EDITOR && this->IsWidgetLowered(WID_DT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + if (_game_mode != GM_EDITOR && + ((this->HasWidget(WID_DT_DEPOT) && this->IsWidgetLowered(WID_DT_DEPOT)) || + (this->HasWidget(WID_DT_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_DT_EXTENDED_DEPOT)))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } this->RaiseButtons(); @@ -346,6 +362,25 @@ struct BuildDocksToolbarWindow : Window { }, DockToolbarGlobalHotkeys}; }; +/** + * Add the depot icons depending on availability of construction. + * @return Panel with water depot buttons. + */ +static std::unique_ptr MakeNWidgetWaterDepot() +{ + auto hor = std::make_unique(); + + if (HasBit(_settings_game.depot.water_depot_types, 0)) { + hor->Add(std::make_unique(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_DEPOT, SPR_IMG_SHIP_DEPOT, STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP)); + } + + if (HasBit(_settings_game.depot.water_depot_types, 1)) { + hor->Add(std::make_unique(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_EXTENDED_DEPOT, SPR_IMG_SHIP_DEPOT, STR_WATERWAYS_TOOLBAR_BUILD_EXTENDED_DEPOT_TOOLTIP)); + } + + return hor; +} + /** * Nested widget parts of docks toolbar, game version. * Position of #WID_DT_RIVER widget has changed. @@ -361,7 +396,7 @@ static constexpr NWidgetPart _nested_build_docks_toolbar_widgets[] = { NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_LOCK), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_BUILD_LOCK, STR_WATERWAYS_TOOLBAR_BUILD_LOCKS_TOOLTIP), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(5, 22), SetFill(1, 1), EndContainer(), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_DEMOLISH), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), - NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_DEPOT), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_SHIP_DEPOT, STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP), + NWidgetFunction(MakeNWidgetWaterDepot), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_STATION), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_SHIP_DOCK, STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_BUOY), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_BUOY, STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_BUILD_AQUEDUCT), SetMinimalSize(23, 22), SetFill(0, 1), SetDataTip(SPR_IMG_AQUEDUCT, STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP), diff --git a/src/lang/english.txt b/src/lang/english.txt index abb158714445c..5da8b20ba3d57 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2959,7 +2959,8 @@ STR_WATERWAYS_TOOLBAR_CAPTION :{WHITE}Waterway STR_WATERWAYS_TOOLBAR_CAPTION_SE :{WHITE}Waterways STR_WATERWAYS_TOOLBAR_BUILD_CANALS_TOOLTIP :{BLACK}Build canals. Also press Shift to show cost estimate only STR_WATERWAYS_TOOLBAR_BUILD_LOCKS_TOOLTIP :{BLACK}Build locks. Also press Shift to show cost estimate only -STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP :{BLACK}Build ship depot (for buying and servicing ships). Also press Shift to show cost estimate only +STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP :{BLACK}Build standard ship depot (for buying and servicing ships). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only +STR_WATERWAYS_TOOLBAR_BUILD_EXTENDED_DEPOT_TOOLTIP :{BLACK}Build extended ship depot (for buying and servicing ships). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Build ship dock. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Place a buoy which can be used as a waypoint. Also press Shift to show cost estimate only STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Build aqueduct. Also press Shift to show cost estimate only @@ -3202,7 +3203,8 @@ STR_LAI_WATER_DESCRIPTION_CANAL :Canal STR_LAI_WATER_DESCRIPTION_LOCK :Lock STR_LAI_WATER_DESCRIPTION_RIVER :River STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK :Coast or riverbank -STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT :Ship depot +STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT :Standard ship depot +STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT_EXTENDED :Extended ship depot # Industries come directly from their industry names diff --git a/src/script/api/script_marine.cpp b/src/script/api/script_marine.cpp index 5e6d60a6c6b37..5b30290d4be49 100644 --- a/src/script/api/script_marine.cpp +++ b/src/script/api/script_marine.cpp @@ -83,7 +83,7 @@ EnforcePrecondition(false, ::IsValidTile(front)); EnforcePrecondition(false, (::TileX(front) == ::TileX(tile)) != (::TileY(front) == ::TileY(tile))); - return ScriptObject::Command::Do(tile, ::TileX(front) == ::TileX(tile) ? AXIS_Y : AXIS_X, false, INVALID_DEPOT, tile); + return ScriptObject::Command::Do(tile, ::TileX(front) == ::TileX(tile) ? AXIS_Y : AXIS_X, false, false, INVALID_DEPOT, tile); } /* static */ bool ScriptMarine::BuildDock(TileIndex tile, StationID station_id) diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index c24cf6bd24877..f75ca6d4dce01 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -97,12 +97,17 @@ static void MarkCanalsAndRiversAroundDirty(TileIndex tile) * @param tile first tile where ship depot is built * @param axis depot orientation (Axis) * @param adjacent allow adjacent depots + * @param extended whether to build an extended depot * @param join_to depot to join to * @param end_tile end tile of area to be built * @return the cost of this operation or an error */ -CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis, bool adjacent, DepotID join_to, TileIndex end_tile) +CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis, bool adjacent, bool extended, DepotID join_to, TileIndex end_tile) { + if (Company::IsValidHumanID(_current_company) && !HasBit(_settings_game.depot.water_depot_types, extended)) { + return_cmd_error(STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE); + } + if (!IsValidAxis(axis)) return CMD_ERROR; TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); @@ -149,7 +154,7 @@ CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis, bo if (flags & DC_EXEC) { DepotPart dp = northern_tiles.Contains(t) ? DEPOT_PART_NORTH : DEPOT_PART_SOUTH; - MakeShipDepot(t, _current_company, depot->index, dp, axis, wc); + MakeShipDepot(t, _current_company, depot->index, extended, dp, axis, wc); CheckForDockingTile(t); MarkTileDirtyByTile(t); } @@ -984,7 +989,7 @@ static void GetTileDesc_Water(TileIndex tile, TileDesc *td) case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break; case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break; case WATER_TILE_DEPOT: - td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT; + td->str = IsExtendedDepot(tile) ? STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT_EXTENDED : STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT; td->build_date = Depot::GetByTile(tile)->build_date; break; default: NOT_REACHED(); diff --git a/src/water_cmd.h b/src/water_cmd.h index 60e00d4a4cdf8..02efc043cea9e 100644 --- a/src/water_cmd.h +++ b/src/water_cmd.h @@ -13,7 +13,7 @@ #include "command_type.h" #include "water_map.h" -CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis, bool adjacent, DepotID depot_id, TileIndex end_tile); +CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis, bool adjacent, bool extended, DepotID depot_id, TileIndex end_tile); CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, WaterClass wc, bool diagonal); CommandCost CmdBuildLock(DoCommandFlag flags, TileIndex tile); diff --git a/src/water_map.h b/src/water_map.h index 1e062109a3823..5291a980139c8 100644 --- a/src/water_map.h +++ b/src/water_map.h @@ -24,6 +24,8 @@ enum WaterTileTypeBitLayout { WBL_TYPE_LOCK = 0x1, ///< Lock ('type' bitfield). WBL_TYPE_DEPOT = 0x2, ///< Depot ('type' bitfield). + WBL_DEPOT_EXTENDED = 5, ///< Bit for the standard/extended depot flag. + WBL_COAST_FLAG = 0, ///< Flag for coast. WBL_LOCK_ORIENT_BEGIN = 0, ///< Start of lock orientation bitfield. @@ -464,11 +466,12 @@ inline void MakeCanal(Tile t, Owner o, uint8_t random_bits) * @param t Tile to place the ship depot section. * @param o Owner of the depot. * @param did Depot ID. + * @param extended True if building an extended depot. * @param part Depot part (either #DEPOT_PART_NORTH or #DEPOT_PART_SOUTH). * @param a Axis of the depot. * @param original_water_class Original water class. */ -inline void MakeShipDepot(Tile t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) +inline void MakeShipDepot(Tile t, Owner o, DepotID did, bool extended, DepotPart part, Axis a, WaterClass original_water_class) { SetTileType(t, MP_WATER); SetTileOwner(t, o); @@ -477,7 +480,7 @@ inline void MakeShipDepot(Tile t, Owner o, DepotID did, DepotPart part, Axis a, t.m2() = did; t.m3() = 0; t.m4() = 0; - t.m5() = WBL_TYPE_DEPOT << WBL_TYPE_BEGIN | part << WBL_DEPOT_PART | a << WBL_DEPOT_AXIS; + t.m5() = WBL_TYPE_DEPOT << WBL_TYPE_BEGIN | extended << WBL_DEPOT_EXTENDED | part << WBL_DEPOT_PART | a << WBL_DEPOT_AXIS; SB(t.m6(), 2, 4, 0); t.m7() = 0; } diff --git a/src/widgets/dock_widget.h b/src/widgets/dock_widget.h index 0a1f480eb6187..84f39589c0861 100644 --- a/src/widgets/dock_widget.h +++ b/src/widgets/dock_widget.h @@ -22,7 +22,8 @@ enum DockToolbarWidgets : WidgetID { WID_DT_CANAL, ///< Build canal button. WID_DT_LOCK, ///< Build lock button. WID_DT_DEMOLISH, ///< Demolish aka dynamite button. - WID_DT_DEPOT, ///< Build depot button. + WID_DT_DEPOT, ///< Build standard depot button. + WID_DT_EXTENDED_DEPOT, ///< Build extended depot button. WID_DT_STATION, ///< Build station button. WID_DT_BUOY, ///< Build buoy button. WID_DT_RIVER, ///< Build river button (in scenario editor). From 2c520aceb8129f202f013dbb64ef4d65f4e35f43 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 24 Dec 2020 00:34:54 +0100 Subject: [PATCH 036/143] Change: Get a better depot when close to destination tile of depots. --- src/ship_cmd.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 4d7ce518ad22f..cc0e94a5e3c95 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -496,6 +496,13 @@ static void ShipArrivesAt(const Vehicle *v, Station *st) */ static Track ChooseShipTrack(Ship *v, TileIndex tile, TrackBits tracks) { + /* Before choosing a track, if close to the destination station or depot (not an oil rig)... */ + if (DistanceManhattan(v->dest_tile, tile) <= 5 && (v->current_order.IsType(OT_GOTO_DEPOT) && + (!IsShipDepotTile(v->dest_tile) || (IsExtendedDepotTile(v->dest_tile) && !IsExtendedDepotEmpty(v->dest_tile))))) { + /* Try to get a depot tile. */ + v->dest_tile = Depot::Get(v->current_order.GetDestination())->GetBestDepotTile(v); + } + bool path_found = true; Track track; From 8d748fd30b96f6020c5d64556edc7cf31fed2a1a Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 10 May 2023 22:02:27 +0200 Subject: [PATCH 037/143] Change: Ships enter depots when they are in the middle of the two depot tiles (not in the center of a tile). # Conflicts: # src/ship_cmd.cpp --- src/ship_cmd.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index cc0e94a5e3c95..cd7ff42ad9d8f 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -773,13 +773,6 @@ static void ShipController(Ship *v) UpdateVehicleTimetable(v, true); v->IncrementRealOrderIndex(); v->current_order.MakeDummy(); - } else if (v->current_order.IsType(OT_GOTO_DEPOT) && - v->dest_tile == gp.new_tile) { - /* Depot orders really need to reach the tile */ - if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) { - VehicleEnterDepot(v); - return; - } } else if (v->current_order.IsType(OT_GOTO_STATION) && IsDockingTile(gp.new_tile)) { /* Process station in the orderlist. */ Station *st = Station::Get(v->current_order.GetDestination()); @@ -800,6 +793,16 @@ static void ShipController(Ship *v) /* New tile */ if (!IsValidTile(gp.new_tile)) return ReverseShip(v); + if (v->current_order.IsType(OT_GOTO_DEPOT) && + IsShipDepotTile(gp.new_tile) && + GetOtherShipDepotTile(gp.new_tile) == gp.old_tile && + v->current_order.GetDestination() == GetDepotIndex(gp.new_tile)) { + HandleShipEnterDepot(v); + v->UpdatePosition(); + v->UpdateViewport(true, true); + return; + } + const DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile); assert(diagdir != INVALID_DIAGDIR); const TrackBits tracks = GetAvailShipTracks(gp.new_tile, diagdir); From 862671ba2ca685e41bf250d2a0afa405809cb99e Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 24 Dec 2020 00:38:26 +0100 Subject: [PATCH 038/143] Feature: Show servicing vehicles in extended depots on viewport and on vehicle window. --- src/lang/english.txt | 3 + src/script/api/script_order.cpp | 3 +- src/script/api/script_vehiclelist.cpp | 2 +- src/ship_cmd.cpp | 19 +++++ src/vehicle.cpp | 118 ++++++++++++++++++++++++-- src/vehicle_base.h | 7 ++ src/vehicle_cmd.cpp | 82 ++++++++++++++++++ src/vehicle_func.h | 1 + src/vehicle_gui.cpp | 2 + 9 files changed, 227 insertions(+), 10 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 5da8b20ba3d57..b64434f9ba533 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -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}% diff --git a/src/script/api/script_order.cpp b/src/script/api/script_order.cpp index 5dcd39ac4eb60..b11f19d03b724 100644 --- a/src/script/api/script_order.cpp +++ b/src/script/api/script_order.cpp @@ -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; } diff --git a/src/script/api/script_vehiclelist.cpp b/src/script/api/script_vehiclelist.cpp index 73bb4f4d30ad9..267dac241a72e 100644 --- a/src/script/api/script_vehiclelist.cpp +++ b/src/script/api/script_vehiclelist.cpp @@ -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: diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index cd7ff42ad9d8f..df9b6d4b0747b 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -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; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 64876f780bdae..f38c5650d760e 100644 --- a/src/vehicle.cpp +++ b/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; +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 cur_company(_current_company, v->owner); + CommandCost cost = std::get<0>(Command::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; -static AutoreplaceMap _vehicles_to_autoreplace; - void InitializeVehicles() { _vehicles_to_autoreplace.clear(); diff --git a/src/vehicle_base.h b/src/vehicle_base.h index b13e6bda8ed21..8b1afb4978fa8 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -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 @@ struct Vehicle : VehiclePool::PoolItem<&_vehicle_pool>, BaseVehicle, BaseConsist 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; diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index 16ec09955111c..7147b5c84adf5 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -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); +} diff --git a/src/vehicle_func.h b/src/vehicle_func.h index 8047a1952edbf..eedd4c4a6d928 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -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); diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index bb277bab34de0..ad00066fb772c 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -3111,6 +3111,8 @@ struct VehicleViewWindow : Window { } 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)) { From c857602f0343310fa028fa2ec002badcd3672825 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 24 Dec 2020 00:45:07 +0100 Subject: [PATCH 039/143] Change: Adapt how ships enter and leave extended depots. --- src/ship_cmd.cpp | 82 ++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index df9b6d4b0747b..cb7db048a9852 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -374,6 +374,12 @@ static bool CheckReverseShip(const Ship *v, Trackdir *trackdir = nullptr) return YapfShipCheckReverse(v, trackdir); } +static bool CheckPlaceShipOnDepot(TileIndex tile) +{ + assert(IsShipDepotTile(tile)); + return !IsExtendedDepot(tile) || IsExtendedDepotEmpty(tile); +} + void HandleShipEnterDepot(Ship *v) { assert(IsShipDepotTile(v->tile)); @@ -403,45 +409,55 @@ static bool CheckShipLeaveDepot(Ship *v) /* We are leaving a depot, but have to go to the exact same one; re-enter */ if (v->current_order.IsType(OT_GOTO_DEPOT) && IsShipDepotTile(v->tile) && GetDepotIndex(v->tile) == v->current_order.GetDestination()) { - VehicleEnterDepot(v); + HandleShipEnterDepot(v); return true; } /* Don't leave depot if no destination set */ if (v->dest_tile == 0) return true; - /* Don't leave depot if another vehicle is already entering/leaving */ - /* This helps avoid CPU load if many ships are set to start at the same time */ - if (HasVehicleOnPos(v->tile, nullptr, &EnsureNoMovingShipProc)) return true; + if (!IsExtendedDepot(v->tile)) { + /* Don't leave depot if another vehicle is already entering/leaving */ + /* This helps avoid CPU load if many ships are set to start at the same time */ + if (HasVehicleOnPos(v->tile, nullptr, &EnsureNoMovingShipProc)) return true; - TileIndex tile = v->tile; - Axis axis = GetShipDepotAxis(tile); + TileIndex tile = v->tile; + Axis axis = GetShipDepotAxis(tile); + bool reverse = false; - DiagDirection north_dir = ReverseDiagDir(AxisToDiagDir(axis)); - TileIndex north_neighbour = TileAdd(tile, TileOffsByDiagDir(north_dir)); - DiagDirection south_dir = AxisToDiagDir(axis); - TileIndex south_neighbour = TileAdd(tile, 2 * TileOffsByDiagDir(south_dir)); + DiagDirection north_dir = ReverseDiagDir(AxisToDiagDir(axis)); + TileIndex north_neighbour = TileAdd(tile, TileOffsByDiagDir(north_dir)); + DiagDirection south_dir = AxisToDiagDir(axis); + TileIndex south_neighbour = TileAdd(tile, 2 * TileOffsByDiagDir(south_dir)); - TrackBits north_tracks = DiagdirReachesTracks(north_dir) & GetTileShipTrackStatus(north_neighbour); - TrackBits south_tracks = DiagdirReachesTracks(south_dir) & GetTileShipTrackStatus(south_neighbour); - if (north_tracks && south_tracks) { - if (CheckReverseShip(v)) north_tracks = TRACK_BIT_NONE; - } + TrackBits north_tracks = DiagdirReachesTracks(north_dir) & GetTileShipTrackStatus(north_neighbour); + TrackBits south_tracks = DiagdirReachesTracks(south_dir) & GetTileShipTrackStatus(south_neighbour); + if (north_tracks && south_tracks) { + if (CheckReverseShip(v)) north_tracks = TRACK_BIT_NONE; + } - if (north_tracks) { - /* Leave towards north */ - v->rotation = v->direction = DiagDirToDir(north_dir); - } else if (south_tracks) { - /* Leave towards south */ - v->rotation = v->direction = DiagDirToDir(south_dir); - } else { - /* Both ways blocked */ - return false; - } + if (north_tracks) { + /* Leave towards north */ + v->rotation = v->direction = DiagDirToDir(north_dir); + } else if (south_tracks) { + /* Leave towards south */ + v->rotation = v->direction = DiagDirToDir(south_dir); + } else { + /* Both ways blocked */ + return false; + } - v->state = AxisToTrackBits(axis); - v->vehstatus &= ~VS_HIDDEN; + v->state = AxisToTrackBits(axis); + v->vehstatus &= ~VS_HIDDEN; + /* Leave towards south if reverse. */ + v->rotation = v->direction = DiagDirToDir(reverse ? south_dir : north_dir); + + v->state = AxisToTrackBits(axis); + v->vehstatus &= ~VS_HIDDEN; + } + + v->state &= ~TRACK_BIT_DEPOT; v->cur_speed = 0; v->UpdateViewport(true, true); DepotID depot_id = GetDepotIndex(v->tile); @@ -733,6 +749,8 @@ static void ShipController(Ship *v) if (v->vehstatus & VS_STOPPED) return; + if (v->ContinueServicing()) return; + if (ProcessOrders(v) && CheckReverseShip(v)) return ReverseShip(v); v->HandleLoading(); @@ -816,10 +834,12 @@ static void ShipController(Ship *v) IsShipDepotTile(gp.new_tile) && GetOtherShipDepotTile(gp.new_tile) == gp.old_tile && v->current_order.GetDestination() == GetDepotIndex(gp.new_tile)) { - HandleShipEnterDepot(v); - v->UpdatePosition(); - v->UpdateViewport(true, true); - return; + if (CheckPlaceShipOnDepot(v->tile)) { + HandleShipEnterDepot(v); + v->UpdatePosition(); + v->UpdateViewport(true, true); + return; + } } const DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile); From 117f7fa786db6388642a108a64e29cc75e058c08 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 9 Jan 2021 17:22:46 +0100 Subject: [PATCH 040/143] Codechange: Reorder code in autoreplace vehicles. --- src/autoreplace_cmd.cpp | 53 +++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 52f4588fbc513..38b88b85d189f 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -28,6 +28,7 @@ #include "order_cmd.h" #include "train_cmd.h" #include "vehicle_cmd.h" +#include "depot_map.h" #include "table/strings.h" @@ -777,45 +778,45 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) any_replacements |= (e != INVALID_ENGINE); w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : nullptr); } + if (!any_replacements) return_cmd_error(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO); CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0); bool nothing_to_do = true; + bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0); - if (any_replacements) { - bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0); + /* Stop the vehicle */ + if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, true)); + if (cost.Failed()) return cost; - /* Stop the vehicle */ - if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, true)); - if (cost.Failed()) return cost; + assert(free_wagon || v->IsStoppedInDepot()); - assert(free_wagon || v->IsStoppedInDepot()); + /* We have to construct the new vehicle chain to test whether it is valid. + * Vehicle construction needs random bits, so we have to save the random seeds + * to prevent desyncs and to replay newgrf callbacks during DC_EXEC */ + SavedRandomSeeds saved_seeds; + SaveRandomSeeds(&saved_seeds); + if (free_wagon) { + cost.AddCost(ReplaceFreeUnit(&v, flags & ~DC_EXEC, ¬hing_to_do)); + } else { + cost.AddCost(ReplaceChain(&v, flags & ~DC_EXEC, wagon_removal, ¬hing_to_do)); + } + RestoreRandomSeeds(saved_seeds); - /* We have to construct the new vehicle chain to test whether it is valid. - * Vehicle construction needs random bits, so we have to save the random seeds - * to prevent desyncs and to replay newgrf callbacks during DC_EXEC */ - SavedRandomSeeds saved_seeds; - SaveRandomSeeds(&saved_seeds); + if (cost.Succeeded() && (flags & DC_EXEC) != 0) { if (free_wagon) { - cost.AddCost(ReplaceFreeUnit(&v, flags & ~DC_EXEC, ¬hing_to_do)); + ret = ReplaceFreeUnit(&v, flags, ¬hing_to_do); } else { - cost.AddCost(ReplaceChain(&v, flags & ~DC_EXEC, wagon_removal, ¬hing_to_do)); + ret = ReplaceChain(&v, flags, wagon_removal, ¬hing_to_do); } - RestoreRandomSeeds(saved_seeds); - if (cost.Succeeded() && (flags & DC_EXEC) != 0) { - if (free_wagon) { - ret = ReplaceFreeUnit(&v, flags, ¬hing_to_do); - } else { - ret = ReplaceChain(&v, flags, wagon_removal, ¬hing_to_do); - } - assert(ret.Succeeded() && ret.GetCost() == cost.GetCost()); - } - - /* Restart the vehicle */ - if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false)); + assert(ret.Succeeded()); + assert(ret.GetCost() == cost.GetCost()); } - if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO); + /* Restart the vehicle */ + if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false)); + + assert(cost.Failed() || !nothing_to_do); return cost; } From 36159c01aa418802088b355464c50167139d58d1 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Mon, 29 Mar 2021 21:03:40 +0200 Subject: [PATCH 041/143] Add: Set reservation of extended ship depots and prepare for setting it for road and train depots. --- src/autoreplace_cmd.cpp | 4 ++++ src/depot.cpp | 32 ++++++++++++++++++++++++++++++++ src/depot_map.h | 2 ++ src/ship.h | 2 +- src/ship_cmd.cpp | 14 +++++++++++++- src/vehicle.cpp | 6 +++++- 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 38b88b85d189f..084ca8c8385fa 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -790,6 +790,8 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) assert(free_wagon || v->IsStoppedInDepot()); + if (IsExtendedDepotTile(v->tile)) UpdateExtendedDepotReservation(v, false); + /* We have to construct the new vehicle chain to test whether it is valid. * Vehicle construction needs random bits, so we have to save the random seeds * to prevent desyncs and to replay newgrf callbacks during DC_EXEC */ @@ -813,6 +815,8 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) assert(ret.GetCost() == cost.GetCost()); } + if (IsExtendedDepotTile(v->tile)) UpdateExtendedDepotReservation(v, true); + /* Restart the vehicle */ if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false)); diff --git a/src/depot.cpp b/src/depot.cpp index b59609613eb8d..9373182e8894f 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -244,3 +244,35 @@ void Depot::RescanDepotTiles() InvalidateWindowData(WC_BUILD_VEHICLE, this->index, 0, true); } } + +/** + * Fix tile reservations and vehicle on extended depots. + * @param v Vehicle to be checked. + * @param reserve Whether to reserve or free the position v is occupying. + */ +void UpdateExtendedDepotReservation(Vehicle *v, bool reserve) +{ + assert(v != nullptr); + assert(IsExtendedDepotTile(v->tile)); + DepotReservation res_type = DEPOT_RESERVATION_EMPTY; + + res_type = (v->vehstatus & VS_STOPPED) ? + DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + + switch (v->type) { + case VEH_ROAD: + break; + + case VEH_SHIP: + SetDepotReservation(v->tile, res_type); + break; + + case VEH_TRAIN: + break; + + case VEH_AIRCRAFT: + break; + + default: NOT_REACHED(); + } +} diff --git a/src/depot_map.h b/src/depot_map.h index 18d172fbeb811..da3e1804c957a 100644 --- a/src/depot_map.h +++ b/src/depot_map.h @@ -175,4 +175,6 @@ static inline void SetDepotReservation(Tile t, DepotReservation reservation, boo SB(t.m4(), 6, 2, reservation); } +void UpdateExtendedDepotReservation(Vehicle *v, bool state); + #endif /* DEPOT_MAP_H */ diff --git a/src/ship.h b/src/ship.h index 927dc171ef5fa..42d9392e88d19 100644 --- a/src/ship.h +++ b/src/ship.h @@ -31,7 +31,7 @@ struct Ship final : public SpecializedVehicle { /** We don't want GCC to zero our struct! It already is zeroed and has an index! */ Ship() : SpecializedVehicleBase() {} /** We want to 'destruct' the right class. */ - virtual ~Ship() { this->PreDestructor(); } + virtual ~Ship(); void MarkDirty() override; void UpdateDeltaXY() override; diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index cb7db048a9852..0f06765b4bd9b 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -303,6 +303,15 @@ Trackdir Ship::GetVehicleTrackdir() const return TrackDirectionToTrackdir(FindFirstTrack(this->state), this->direction); } +Ship::~Ship() +{ + if (CleaningPool()) return; + + if (this->IsInDepot()) SetDepotReservation(this->tile, DEPOT_RESERVATION_EMPTY); + + this->PreDestructor(); +} + void Ship::MarkDirty() { this->colourmap = PAL_NONE; @@ -385,6 +394,7 @@ void HandleShipEnterDepot(Ship *v) assert(IsShipDepotTile(v->tile)); if (IsExtendedDepot(v->tile)) { + SetDepotReservation(v->tile, DEPOT_RESERVATION_IN_USE); v->state |= TRACK_BIT_DEPOT; v->cur_speed = 0; v->UpdateCache(); @@ -416,7 +426,9 @@ static bool CheckShipLeaveDepot(Ship *v) /* Don't leave depot if no destination set */ if (v->dest_tile == 0) return true; - if (!IsExtendedDepot(v->tile)) { + if (IsExtendedDepot(v->tile)) { + SetDepotReservation(v->tile, DEPOT_RESERVATION_EMPTY); + } else { /* Don't leave depot if another vehicle is already entering/leaving */ /* This helps avoid CPU load if many ships are set to start at the same time */ if (HasVehicleOnPos(v->tile, nullptr, &EnsureNoMovingShipProc)) return true; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index f38c5650d760e..a787f89b259ea 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1198,7 +1198,11 @@ void CallVehicleTicks() /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick() * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that * they are already leaving the depot again before being replaced. */ - if (it.second) v->vehstatus &= ~VS_STOPPED; + if (it.second) { + v->vehstatus &= ~VS_STOPPED; + } else if (IsExtendedDepotTile(v->tile)){ + UpdateExtendedDepotReservation(v, true); + } /* Store the position of the effect as the vehicle pointer will become invalid later */ int x = v->x_pos; From ce16d0f4ccb2fc55926e7a527acce2ef56281aff Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 24 Dec 2020 01:02:59 +0100 Subject: [PATCH 042/143] Change: Adapt commands for buying and replacing ships in extended depots. --- src/autoreplace_cmd.cpp | 25 ++++++++++++++++++++++++ src/lang/english.txt | 1 + src/ship_cmd.cpp | 43 ++++++++++++++++++++++++++++++----------- 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 084ca8c8385fa..ffcaf46710c88 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -20,6 +20,7 @@ #include "core/random_func.hpp" #include "vehiclelist.h" #include "road.h" +#include "ship.h" #include "ai/ai.hpp" #include "news_func.h" #include "strings_func.h" @@ -515,6 +516,25 @@ struct ReplaceChainItem { Vehicle *GetVehicle() const { return new_veh == nullptr ? old_veh : new_veh; } }; +/** + * When replacing a ship in an extended depot, copy the direction as well. + * @param old_ship The ship being replaced. + * @param new_ship The new ship that will replace the old one. + */ +void CopyShipStatusInExtendedDepot(const Ship *old_ship, Ship *new_ship) +{ + assert(IsExtendedDepotTile(old_ship->tile)); + assert(old_ship->tile == new_ship->tile); + + new_ship->x_pos = old_ship->x_pos; + new_ship->y_pos = old_ship->y_pos; + new_ship->z_pos = old_ship->z_pos; + new_ship->state = old_ship->state; + new_ship->direction = old_ship->direction; + new_ship->rotation = old_ship->rotation; + new_ship->GetImage(new_ship->direction, EIT_ON_MAP, &new_ship->sprite_cache.sprite_seq); +} + /** * Replace a whole vehicle chain * @param chain vehicle chain to let autoreplace/renew operator on @@ -712,6 +732,11 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags)); if (cost.Succeeded()) { + /* Copy position and direction for ships in extended depots. */ + if (old_head->type == VEH_SHIP && IsExtendedDepotTile(old_head->tile)) { + CopyShipStatusInExtendedDepot(Ship::From(old_head), Ship::From(new_head)); + } + /* The new vehicle is constructed, now take over cargo */ if ((flags & DC_EXEC) != 0) { TransferCargo(old_head, new_head, true); diff --git a/src/lang/english.txt b/src/lang/english.txt index b64434f9ba533..12b4e430f0223 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5341,6 +5341,7 @@ STR_ERROR_TOO_MANY_VEHICLES_IN_GAME :{WHITE}Too many STR_ERROR_CAN_T_CHANGE_SERVICING :{WHITE}Can't change servicing interval... STR_ERROR_VEHICLE_IS_DESTROYED :{WHITE}... vehicle is destroyed +STR_ERROR_NO_FREE_DEPOT :{WHITE}... there is no free depot STR_ERROR_CAN_T_CLONE_VEHICLE_LIST :{WHITE}... not all vehicles are identical diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 0f06765b4bd9b..40c49152cfc8c 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -36,6 +36,7 @@ #include "industry.h" #include "industry_map.h" #include "ship_cmd.h" +#include "command_func.h" #include "table/strings.h" @@ -957,10 +958,22 @@ void Ship::SetDestTile(TileIndex tile) */ CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret) { - tile = GetShipDepotNorthTile(tile); + assert(IsShipDepotTile(tile)); + if (!(flags & DC_AUTOREPLACE)) { + std::vector *depot_tiles = &(Depot::GetByTile(tile)->depot_tiles); + tile = INVALID_TILE; + for (std::vector::iterator it = depot_tiles->begin(); it != depot_tiles->end(); ++it) { + if (CheckPlaceShipOnDepot(*it)) { + tile = *it; + break; + } + } + if (tile == INVALID_TILE) return_cmd_error(STR_ERROR_NO_FREE_DEPOT); + } + if (flags & DC_EXEC) { - int x; - int y; + bool is_extended_depot = IsExtendedDepot(tile); + TileIndexDiffC offset = TileIndexDiffCByDiagDir(ReverseDiagDir(GetShipDepotDirection(tile))); const ShipVehicleInfo *svi = &e->u.ship; @@ -969,14 +982,22 @@ CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, V v->owner = _current_company; v->tile = tile; - x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2; - y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2; - v->x_pos = x; - v->y_pos = y; - v->z_pos = GetSlopePixelZ(x, y); + v->x_pos = TileX(tile) * TILE_SIZE + TILE_SIZE / 2 + offset.x * (TILE_SIZE / 2 - 1); + v->y_pos = TileY(tile) * TILE_SIZE + TILE_SIZE / 2 + offset.y * (TILE_SIZE / 2 - 1); + v->z_pos = GetSlopePixelZ(v->x_pos, v->y_pos); + v->state = TRACK_BIT_DEPOT; + if (is_extended_depot) { + v->state |= AxisToTrackBits(GetShipDepotAxis(tile)); + v->direction = AxisToDirection(GetShipDepotAxis(v->tile)); + SetDepotReservation(v->tile, DEPOT_RESERVATION_FULL_STOPPED_VEH); + } else { + v->vehstatus |= VS_HIDDEN; + } + + v->rotation = v->direction; v->UpdateDeltaXY(); - v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; + v->vehstatus |= VS_STOPPED | VS_DEFPAL; v->spritenum = svi->image_index; v->cargo_type = e->GetDefaultCargoType(); @@ -992,8 +1013,6 @@ CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, V v->reliability_spd_dec = e->reliability_spd_dec; v->max_age = e->GetLifeLengthInDays(); - v->state = TRACK_BIT_DEPOT; - v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_ships); v->date_of_last_service = TimerGameEconomy::date; v->date_of_last_service_newgrf = TimerGameCalendar::date; @@ -1014,6 +1033,8 @@ CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, V v->InvalidateNewGRFCacheOfChain(); v->UpdatePosition(); + + if (is_extended_depot) v->MarkDirty(); } return CommandCost(); From ce5a004a117b6418c0bb083dfcbd3e14240444e4 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 25 Dec 2020 15:06:18 +0100 Subject: [PATCH 043/143] Add: Do not try to cross an extended depot that has a ship inside. --- src/pathfinder/yapf/yapf_ship.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pathfinder/yapf/yapf_ship.cpp b/src/pathfinder/yapf/yapf_ship.cpp index 57d5e9d87742d..b2b998ba8b4e0 100644 --- a/src/pathfinder/yapf/yapf_ship.cpp +++ b/src/pathfinder/yapf/yapf_ship.cpp @@ -382,6 +382,10 @@ class CYapfCostShipT c += count * 3 * YAPF_TILE_LENGTH; } + if (IsShipDepotTile(n.GetTile())) { + if (IsExtendedDepot(n.GetTile()) && IsDepotFullWithStoppedVehicles(n.GetTile())) c += YAPF_INFINITE_PENALTY; + } + /* Skipped tile cost for aqueducts. */ c += YAPF_TILE_LENGTH * tf->m_tiles_skipped; From d5f9960d797392babe7cf54bb7ed22402fb48c33 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 18:01:01 +0200 Subject: [PATCH 044/143] Add: Add the widget for rail depots. --- src/lang/english.txt | 3 ++- src/rail_gui.cpp | 45 ++++++++++++++++++++++++++++++++------- src/widgets/rail_widget.h | 31 ++++++++++++++------------- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 12b4e430f0223..c5760f53792cb 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2806,7 +2806,8 @@ STR_RAIL_TOOLBAR_MAGLEV_CONSTRUCTION_CAPTION :Maglev Construc STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}Build railway track. Ctrl+Click to remove railway track. Also press Shift to show cost estimate only STR_RAIL_TOOLBAR_TOOLTIP_BUILD_AUTORAIL :{BLACK}Build railway track using the Autorail mode. Ctrl+Click to remove railway track. Also press Shift to show cost estimate only -STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}Build train depot (for buying and servicing trains). Also press Shift to show cost estimate only +STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT :{BLACK}Build standard train depot (for buying and servicing trains). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only +STR_RAIL_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAIN_DEPOT :{BLACK}Build extended train depot (for buying and servicing trains). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT :{BLACK}Build waypoint on railway. Ctrl+Click to select another waypoint to join. Also press Shift to show cost estimate only STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_STATION :{BLACK}Build railway station. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_SIGNALS :{BLACK}Build signal on railway. Ctrl+Click to build the alternate signal style{}Click+Drag to fill the selected section of rail with signals at the chosen spacing. Ctrl+Click+Drag to fill signals up to the next junction, station, or signal. Also press Shift to show cost estimate only diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 226691f8ecc51..6001a5de033ce 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -337,9 +337,8 @@ static bool RailToolbar_CtrlChanged(Window *w) if (w->IsWidgetDisabled(WID_RAT_REMOVE)) return false; /* allow ctrl to switch remove mode only for these widgets */ - for (WidgetID i = WID_RAT_BUILD_NS; i <= WID_RAT_BUILD_STATION; i++) { - if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_DEPOT) && w->IsWidgetLowered(i)) { + if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_DEPOT) && w->HasWidget(i) && w->IsWidgetLowered(i)) { ToggleRailButton_Remove(w); return true; } @@ -459,7 +458,11 @@ struct BuildRailToolbarWindow : Window { if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); CloseWindowById(WC_SELECT_STATION, 0); - if (this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + if ((this->HasWidget(WID_RAT_BUILD_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) || + (this->HasWidget(WID_RAT_BUILD_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_EXTENDED_DEPOT))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } + this->Window::Close(); } @@ -511,7 +514,8 @@ struct BuildRailToolbarWindow : Window { this->GetWidget(WID_RAT_BUILD_EW)->widget_data = rti->gui_sprites.build_ew_rail; this->GetWidget(WID_RAT_BUILD_Y)->widget_data = rti->gui_sprites.build_y_rail; this->GetWidget(WID_RAT_AUTORAIL)->widget_data = rti->gui_sprites.auto_rail; - this->GetWidget(WID_RAT_BUILD_DEPOT)->widget_data = rti->gui_sprites.build_depot; + if (this->HasWidget(WID_RAT_BUILD_DEPOT)) this->GetWidget(WID_RAT_BUILD_DEPOT)->widget_data = rti->gui_sprites.build_depot; + if (this->HasWidget(WID_RAT_BUILD_EXTENDED_DEPOT)) this->GetWidget(WID_RAT_BUILD_EXTENDED_DEPOT)->widget_data = rti->gui_sprites.build_depot; this->GetWidget(WID_RAT_CONVERT_RAIL)->widget_data = rti->gui_sprites.convert_rail; this->GetWidget(WID_RAT_BUILD_TUNNEL)->widget_data = rti->gui_sprites.build_tunnel; } @@ -540,6 +544,7 @@ struct BuildRailToolbarWindow : Window { case WID_RAT_BUILD_Y: case WID_RAT_AUTORAIL: case WID_RAT_BUILD_DEPOT: + case WID_RAT_BUILD_EXTENDED_DEPOT: case WID_RAT_BUILD_WAYPOINT: case WID_RAT_BUILD_STATION: case WID_RAT_BUILD_SIGNALS: @@ -821,7 +826,10 @@ struct BuildRailToolbarWindow : Window { if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) SetViewportCatchmentStation(nullptr, true); if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true); - if (this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + if ((this->HasWidget(WID_RAT_BUILD_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) || + (this->HasWidget(WID_RAT_BUILD_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_EXTENDED_DEPOT))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } this->RaiseButtons(); this->DisableWidget(WID_RAT_REMOVE); @@ -847,7 +855,8 @@ struct BuildRailToolbarWindow : Window { /* do not toggle Remove button by Ctrl when placing station or depot */ if (!this->IsWidgetLowered(WID_RAT_BUILD_STATION) && !this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT) && - !this->IsWidgetLowered(WID_RAT_BUILD_DEPOT) && + !(this->HasWidget(WID_RAT_BUILD_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) && + !(this->HasWidget(WID_RAT_BUILD_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_EXTENDED_DEPOT)) && RailToolbar_CtrlChanged(this)) return ES_HANDLED; return ES_NOT_HANDLED; } @@ -889,6 +898,27 @@ struct BuildRailToolbarWindow : Window { }, RailToolbarGlobalHotkeys}; }; +/** + * Add the depot icons depending on availability of construction. + * @return Panel with rail depot buttons. + */ +static std::unique_ptr MakeNWidgetRailDepot() +{ + auto hor = std::make_unique(); + + if (HasBit(_settings_game.depot.rail_depot_types, 0)) { + /* Add the widget for building standard rail depot. */ + hor->Add(std::make_unique(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_DEPOT, SPR_IMG_DEPOT_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT)); + } + + if (HasBit(_settings_game.depot.rail_depot_types, 1)) { + /* Add the widget for building extended rail depot. */ + hor->Add(std::make_unique(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_EXTENDED_DEPOT, SPR_IMG_DEPOT_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAIN_DEPOT)); + } + + return hor; +} + static constexpr NWidgetPart _nested_build_rail_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), @@ -911,8 +941,7 @@ static constexpr NWidgetPart _nested_build_rail_widgets[] = { NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), - NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_DEPOT), - SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DEPOT_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING), + NWidgetFunction(MakeNWidgetRailDepot), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_WAYPOINT), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_STATION), diff --git a/src/widgets/rail_widget.h b/src/widgets/rail_widget.h index f90280c52be87..b7358c515273e 100644 --- a/src/widgets/rail_widget.h +++ b/src/widgets/rail_widget.h @@ -13,21 +13,22 @@ /** Widgets of the #BuildRailToolbarWindow class. */ enum RailToolbarWidgets : WidgetID { /* Name starts with RA instead of R, because of collision with RoadToolbarWidgets */ - WID_RAT_CAPTION, ///< Caption of the window. - WID_RAT_BUILD_NS, ///< Build rail along the game view Y axis. - WID_RAT_BUILD_X, ///< Build rail along the game grid X axis. - WID_RAT_BUILD_EW, ///< Build rail along the game view X axis. - WID_RAT_BUILD_Y, ///< Build rail along the game grid Y axis. - WID_RAT_AUTORAIL, ///< Autorail tool. - WID_RAT_DEMOLISH, ///< Destroy something with dynamite! - WID_RAT_BUILD_DEPOT, ///< Build a depot. - WID_RAT_BUILD_WAYPOINT, ///< Build a waypoint. - WID_RAT_BUILD_STATION, ///< Build a station. - WID_RAT_BUILD_SIGNALS, ///< Build signals. - WID_RAT_BUILD_BRIDGE, ///< Build a bridge. - WID_RAT_BUILD_TUNNEL, ///< Build a tunnel. - WID_RAT_REMOVE, ///< Bulldozer to remove rail. - WID_RAT_CONVERT_RAIL, ///< Convert other rail to this type. + WID_RAT_CAPTION, ///< Caption of the window. + WID_RAT_BUILD_NS, ///< Build rail along the game view Y axis. + WID_RAT_BUILD_X, ///< Build rail along the game grid X axis. + WID_RAT_BUILD_EW, ///< Build rail along the game view X axis. + WID_RAT_BUILD_Y, ///< Build rail along the game grid Y axis. + WID_RAT_AUTORAIL, ///< Autorail tool. + WID_RAT_DEMOLISH, ///< Destroy something with dynamite! + WID_RAT_BUILD_DEPOT, ///< Build a depot. + WID_RAT_BUILD_EXTENDED_DEPOT, ///< Build an extended depot. + WID_RAT_BUILD_WAYPOINT, ///< Build a waypoint. + WID_RAT_BUILD_STATION, ///< Build a station. + WID_RAT_BUILD_SIGNALS, ///< Build signals. + WID_RAT_BUILD_BRIDGE, ///< Build a bridge. + WID_RAT_BUILD_TUNNEL, ///< Build a tunnel. + WID_RAT_REMOVE, ///< Bulldozer to remove rail. + WID_RAT_CONVERT_RAIL, ///< Convert other rail to this type. INVALID_WID_RAT = -1, }; From b77e1e3409da06d8dbf1726c1e86b2eb91027623 Mon Sep 17 00:00:00 2001 From: Juanjo Date: Tue, 23 Sep 2014 18:22:18 +0200 Subject: [PATCH 045/143] Add: Add IsStandard and IsExtendedRailDepotTile map functions. --- src/pathfinder/follow_track.hpp | 2 +- src/pathfinder/yapf/yapf_costrail.hpp | 5 ++- src/pbs.cpp | 8 ++--- src/rail_cmd.cpp | 1 + src/rail_map.h | 44 +++++++++++++++++++++++++++ src/train_cmd.cpp | 16 +++++++--- 6 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index d384568b62f74..9a16bcba93e6e 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -311,7 +311,7 @@ struct CFollowTrackT return false; } } - if (IsRailTT() && IsDepotTypeTile(m_new_tile, TT())) { + if (IsRailTT() && IsStandardRailDepotTile(m_new_tile)) { DiagDirection exitdir = GetRailDepotDirection(m_new_tile); if (ReverseDiagDir(exitdir) != m_exitdir) { m_err = EC_NO_WAY; diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index f6217d2b245ee..d50bb24377ff6 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -389,13 +389,12 @@ class CYapfCostRailT : public CYapfCostBase { /* Tests for 'potential target' reasons to close the segment. */ if (cur.tile == prev.tile) { /* Penalty for reversing in a depot. */ - assert(IsRailDepot(cur.tile)); + assert(IsStandardRailDepot(cur.tile)); segment_cost += Yapf().PfGetSettings().rail_depot_reverse_penalty; - } else if (IsRailDepotTile(cur.tile)) { + } else if (IsStandardRailDepotTile(cur.tile)) { /* We will end in this pass (depot is possible target) */ end_segment_reason |= ESRB_DEPOT; - } else if (cur.tile_type == MP_STATION && IsRailWaypoint(cur.tile)) { if (v->current_order.IsType(OT_GOTO_WAYPOINT) && GetStationIndex(cur.tile) == v->current_order.GetDestination() && diff --git a/src/pbs.cpp b/src/pbs.cpp index 363404330c5fc..03b5311f894f4 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -240,7 +240,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra if (tile == start_tile && trackdir == start_trackdir) break; } /* Depot tile? Can't continue. */ - if (IsRailDepotTile(tile)) break; + if (IsStandardRailDepotTile(tile)) break; /* Non-pbs signal? Reservation can't continue. */ if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) break; } @@ -292,7 +292,7 @@ PBSTileInfo FollowTrainReservation(const Train *v, Vehicle **train_on_res) TileIndex tile = v->tile; Trackdir trackdir = v->GetVehicleTrackdir(); - if (IsRailDepotTile(tile) && !GetDepotReservationTrackBits(tile)) return PBSTileInfo(tile, trackdir, false); + if (IsStandardRailDepotTile(tile) && !GetDepotReservationTrackBits(tile)) return PBSTileInfo(tile, trackdir, false); FindTrainOnTrackInfo ftoti; ftoti.res = FollowReservation(v->owner, GetRailTypeInfo(v->railtype)->compatible_railtypes, tile, trackdir); @@ -379,7 +379,7 @@ Train *GetTrainForReservation(TileIndex tile, Track track) */ bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg) { - if (IsRailDepotTile(tile)) return true; + if (IsStandardRailDepotTile(tile)) return true; if (IsTileType(tile, MP_RAILWAY)) { /* For non-pbs signals, stop on the signal tile. */ @@ -432,7 +432,7 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo if (TrackOverlapsTracks(reserved, track)) return false; /* Not reserved and depot or not a pbs signal -> free. */ - if (IsRailDepotTile(tile)) return true; + if (IsStandardRailDepotTile(tile)) return true; if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, track))) return true; /* Check the next tile, if it's a PBS signal, it has to be free as well. */ diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 2e02d8eb24c61..b2840174a4280 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -2960,6 +2960,7 @@ static const int8_t _deltacoord_leaveoffset[8] = { */ int TicksToLeaveDepot(const Train *v) { + assert(IsStandardRailDepotTile(v->tile)); DiagDirection dir = GetRailDepotDirection(v->tile); int length = v->CalcNextVehicleOffset(); diff --git a/src/rail_map.h b/src/rail_map.h index 03bca7e6e3243..a715fc67fe2f6 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -107,6 +107,50 @@ debug_inline static bool IsRailDepotTile(Tile t) return IsTileType(t, MP_RAILWAY) && IsRailDepot(t); } +/** + * Is this rail depot tile an extended depot? + * @param t the tile to get the information from + * @pre IsRailDepotTile(t) + * @return true if and only if the tile is an extended rail depot + */ +static inline bool IsExtendedRailDepot(Tile t) +{ + assert(IsRailDepotTile(t)); + return HasBit(t.m5(), 5); +} + +/** + * Is this rail tile a standard rail depot? + * @param t the tile to get the information from + * @pre IsTileType(t, MP_RAILWAY) + * @return true if and only if the tile is a standard rail depot + */ +static inline bool IsStandardRailDepot(Tile t) +{ + assert(IsTileType(t, MP_RAILWAY)); + return IsRailDepot(t) && !IsExtendedRailDepot(t); +} + +/** + * Is this tile a standard rail depot? + * @param t the tile to get the information from + * @return true if and only if the tile is a standard rail depot + */ +static inline bool IsStandardRailDepotTile(TileIndex t) +{ + return IsTileType(t, MP_RAILWAY) && IsStandardRailDepot(t); +} + +/** + * Is this tile rail tile and an extended rail depot? + * @param t the tile to get the information from + * @return true if and only if the tile is an extended rail depot + */ +static inline bool IsExtendedRailDepotTile(TileIndex t) +{ + return IsTileType(t, MP_RAILWAY) && IsRailDepotTile(t) && IsExtendedRailDepot(t); +} + /** * Gets the rail type of the given tile * @param t the tile to get the rail type from diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 52afef664f785..823dd379e28dc 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2182,7 +2182,7 @@ void ReverseTrainDirection(Train *v) !IsPbsSignal(GetSignalType(v->tile, FindFirstTrack(v->track)))); /* If we are on a depot tile facing outwards, do not treat the current tile as safe. */ - if (IsRailDepotTile(v->tile) && TrackdirToExitdir(v->GetVehicleTrackdir()) == GetRailDepotDirection(v->tile)) first_tile_okay = false; + if (IsStandardRailDepotTile(v->tile) && TrackdirToExitdir(v->GetVehicleTrackdir()) == GetRailDepotDirection(v->tile)) first_tile_okay = false; if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true); if (TryPathReserve(v, false, first_tile_okay)) { @@ -2550,7 +2550,7 @@ void FreeTrainTrackReservation(const Train *v) StationID station_id = IsRailStationTile(v->tile) ? GetStationIndex(v->tile) : INVALID_STATION; /* Can't be holding a reservation if we enter a depot. */ - if (IsRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return; + if (IsStandardRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return; if (v->track == TRACK_BIT_DEPOT) { /* Front engine is in a depot. We enter if some part is not in the depot. */ for (const Train *u = v; u != nullptr; u = u->Next()) { @@ -3006,6 +3006,7 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay) if (mark_as_stuck) MarkTrainAsStuck(v); return false; } else { + assert(IsStandardRailDepotTile(v->tile)); /* Depot not reserved, but the next tile might be. */ TileIndex next_tile = TileAddByDiagDir(v->tile, GetRailDepotDirection(v->tile)); if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(v->tile)))) return false; @@ -3824,7 +3825,7 @@ static void DeleteLastWagon(Train *v) } /* Update signals */ - if (IsTileType(tile, MP_TUNNELBRIDGE) || IsRailDepotTile(tile)) { + if (IsTileType(tile, MP_TUNNELBRIDGE) || IsStandardRailDepotTile(tile)) { UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner); } else { SetSignalsOnBothDir(tile, track, owner); @@ -3975,8 +3976,13 @@ static bool TrainCanLeaveTile(const Train *v) /* entering a depot? */ if (IsRailDepotTile(tile)) { - DiagDirection dir = ReverseDiagDir(GetRailDepotDirection(tile)); - if (DiagDirToDir(dir) == v->direction) return false; + if (IsExtendedRailDepot(tile)) { + Direction dir = DiagDirToDir(GetRailDepotDirection(tile)); + if (dir == v->direction || ReverseDir(dir) == v->direction) return false; + } else { + DiagDirection dir = ReverseDiagDir(GetRailDepotDirection(tile)); + if (DiagDirToDir(dir) == v->direction) return false; + } } return true; From 7fe99ba429dcc7165c75e59713767cd4cba5ba13 Mon Sep 17 00:00:00 2001 From: Juanjo Date: Sat, 4 Jan 2014 16:21:34 +0100 Subject: [PATCH 046/143] Change: Transparent depots show all rail track. --- src/direction_func.h | 10 +++ src/rail_cmd.cpp | 149 ++++++++++++----------------------------- src/table/track_land.h | 12 ++-- 3 files changed, 60 insertions(+), 111 deletions(-) diff --git a/src/direction_func.h b/src/direction_func.h index c554873a0d6f1..6ea858264d3f1 100644 --- a/src/direction_func.h +++ b/src/direction_func.h @@ -276,4 +276,14 @@ inline bool IsDiagonalDirection(Direction dir) return (dir & 1) != 0; } +/** + * Checks if a given DiagDirection is facing south. + * @param diag_dir Diagonal direction to check + * @return true iff the diagonal direction is facing south. + */ +static inline bool IsDiagDirFacingSouth(DiagDirection diag_dir) +{ + return diag_dir == DIAGDIR_SE || diag_dir == DIAGDIR_SW; +} + #endif /* DIRECTION_FUNC_H */ diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index b2840174a4280..cb07c49d1c1ac 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -2117,7 +2117,12 @@ static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInf static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeInfo *rti) { RailGroundType rgt = GetRailGroundType(ti->tile); - Foundation f = GetRailFoundation(ti->tileh, track); + Foundation f = FOUNDATION_NONE; + if (IsRailDepot(ti->tile)) { + if (ti->tileh != SLOPE_FLAT) f = FOUNDATION_LEVELED; + } else { + f = GetRailFoundation(ti->tileh, track); + } Corner halftile_corner = CORNER_INVALID; if (IsNonContinuousFoundation(f)) { @@ -2157,7 +2162,18 @@ static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeIn bool no_combine = ti->tileh == SLOPE_FLAT && HasBit(rti->flags, RTF_NO_SPRITE_COMBINE); SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY); SpriteID ground = GetCustomRailSprite(rti, ti->tile, no_combine ? RTSG_GROUND_COMPLETE : RTSG_GROUND); - TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TRACK_BIT_NONE; + + TrackBits pbs = TRACK_BIT_NONE; + if (_settings_client.gui.show_track_reservation) { + if (IsPlainRail(ti->tile)) { + pbs = GetRailReservationTrackBits(ti->tile); + } else { + assert(IsRailDepot(ti->tile)); + if (HasDepotReservation(ti->tile)) { + pbs = track; + } + } + } if (track == TRACK_BIT_NONE) { /* Half-tile foundation, no track here? */ @@ -2387,7 +2403,14 @@ static void DrawTrackBits(TileInfo *ti, TrackBits track) /* PBS debugging, draw reserved tracks darker */ if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) { /* Get reservation, but mask track on halftile slope */ - TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track; + TrackBits pbs = TRACK_BIT_NONE; + if (IsPlainRail(ti->tile)) { + pbs = GetRailReservationTrackBits(ti->tile) & track; + } else { + assert(IsRailDepot(ti->tile)); + if (HasDepotReservation(ti->tile)) pbs = track; + } + if (pbs & TRACK_BIT_X) { if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) { DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH); @@ -2470,124 +2493,40 @@ static void DrawTile_Track(TileInfo *ti) _drawtile_track_palette = COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)); + TrackBits rails = TRACK_BIT_NONE; if (IsPlainRail(ti->tile)) { - TrackBits rails = GetTrackBits(ti->tile); - - DrawTrackBits(ti, rails); - - if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti); - - if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti); - - if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti); + rails = GetTrackBits(ti->tile); } else { - /* draw depot */ - const DrawTileSprites *dts; - PaletteID pal = PAL_NONE; - SpriteID relocation; - - if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); - - if (IsInvisibilitySet(TO_BUILDINGS)) { - /* Draw rail instead of depot */ - dts = &_depot_invisible_gfx_table[GetRailDepotDirection(ti->tile)]; - } else { - dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)]; - } - - SpriteID image; - if (rti->UsesOverlay()) { - image = SPR_FLAT_GRASS_TILE; - } else { - image = dts->ground.sprite; - if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset(); - } - - /* Adjust ground tile for desert and snow. */ - if (IsSnowRailGround(ti->tile)) { - if (image != SPR_FLAT_GRASS_TILE) { - image += rti->snow_offset; // tile with tracks - } else { - image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground - } + assert(IsRailDepot(ti->tile)); + DiagDirection dir = GetRailDepotDirection(ti->tile); + if (IsDiagDirFacingSouth(dir) || IsTransparencySet(TO_BUILDINGS)) { + rails = TrackToTrackBits(GetRailDepotTrack(ti->tile)); } + } - DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, _drawtile_track_palette)); + DrawTrackBits(ti, rails); - if (rti->UsesOverlay()) { - SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND); + if (IsPlainRail(ti->tile) && HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti); - switch (GetRailDepotDirection(ti->tile)) { - case DIAGDIR_NE: - if (!IsInvisibilitySet(TO_BUILDINGS)) break; - [[fallthrough]]; - case DIAGDIR_SW: - DrawGroundSprite(ground + RTO_X, PAL_NONE); - break; - case DIAGDIR_NW: - if (!IsInvisibilitySet(TO_BUILDINGS)) break; - [[fallthrough]]; - case DIAGDIR_SE: - DrawGroundSprite(ground + RTO_Y, PAL_NONE); - break; - default: - break; - } + if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti); - if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) { - SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY); - - switch (GetRailDepotDirection(ti->tile)) { - case DIAGDIR_NE: - if (!IsInvisibilitySet(TO_BUILDINGS)) break; - [[fallthrough]]; - case DIAGDIR_SW: - DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH); - break; - case DIAGDIR_NW: - if (!IsInvisibilitySet(TO_BUILDINGS)) break; - [[fallthrough]]; - case DIAGDIR_SE: - DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH); - break; - default: - break; - } - } - } else { - /* PBS debugging, draw reserved tracks darker */ - if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) { - switch (GetRailDepotDirection(ti->tile)) { - case DIAGDIR_NE: - if (!IsInvisibilitySet(TO_BUILDINGS)) break; - [[fallthrough]]; - case DIAGDIR_SW: - DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH); - break; - case DIAGDIR_NW: - if (!IsInvisibilitySet(TO_BUILDINGS)) break; - [[fallthrough]]; - case DIAGDIR_SE: - DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH); - break; - default: - break; - } - } - } + if (IsRailDepot(ti->tile) && !IsInvisibilitySet(TO_BUILDINGS)) { + /* draw depot */ + const DrawTileSprites *dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)]; int depot_sprite = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT); - relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset(); - - if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti); + SpriteID relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset(); DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, _drawtile_track_palette); } + + if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti); + DrawBridgeMiddle(ti); } void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype) { - const DrawTileSprites *dts = &_depot_gfx_table[dir]; + const DrawTileSprites *dts = &_depot_gfx_gui_table[dir]; const RailTypeInfo *rti = GetRailTypeInfo(railtype); SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite; uint32_t offset = rti->GetRailtypeSpriteOffset(); diff --git a/src/table/track_land.h b/src/table/track_land.h index 70ea770bfed4e..d8275743ed2cf 100644 --- a/src/table/track_land.h +++ b/src/table/track_land.h @@ -33,18 +33,18 @@ static const DrawTileSeqStruct _depot_gfx_NW[] = { TILE_SEQ_END() }; -static const DrawTileSprites _depot_gfx_table[] = { +static const DrawTileSprites _depot_gfx_gui_table[] = { { {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_NE }, { {SPR_RAIL_TRACK_Y, PAL_NONE}, _depot_gfx_SE }, { {SPR_RAIL_TRACK_X, PAL_NONE}, _depot_gfx_SW }, { {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_NW } }; -static const DrawTileSprites _depot_invisible_gfx_table[] = { - { {SPR_RAIL_TRACK_X, PAL_NONE}, _depot_gfx_NE }, - { {SPR_RAIL_TRACK_Y, PAL_NONE}, _depot_gfx_SE }, - { {SPR_RAIL_TRACK_X, PAL_NONE}, _depot_gfx_SW }, - { {SPR_RAIL_TRACK_Y, PAL_NONE}, _depot_gfx_NW } +static const DrawTileSprites _depot_gfx_table[] = { + { {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_NE }, + { {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_SE }, + { {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_SW }, + { {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_NW } }; #undef TILE_SEQ_LINE From bdc153a63754cfc07da82299a085297935453f41 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 18:18:07 +0200 Subject: [PATCH 047/143] Feature: Allow building extended train depots and fix their sprites. --- src/lang/english.txt | 5 +- src/pathfinder/yapf/yapf_rail.cpp | 2 + src/pbs.cpp | 23 +++ src/rail_cmd.cpp | 235 +++++++++++++++++++++++++----- src/rail_cmd.h | 4 +- src/rail_gui.cpp | 18 ++- src/script/api/script_rail.cpp | 2 +- 7 files changed, 244 insertions(+), 45 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index c5760f53792cb..433629922125b 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3174,7 +3174,8 @@ STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS :Railway track w STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS :Railway track with combo- and path signals STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS :Railway track with combo- and one-way path signals STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS :Railway track with path and one-way path signals -STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT :Railway train depot +STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT :Standard railway train depot +STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT_EXTENDED :Extended railway train depot STR_LAI_ROAD_DESCRIPTION_ROAD :Road STR_LAI_ROAD_DESCRIPTION_ROAD_WITH_STREETLIGHTS :Road with street lights @@ -5155,6 +5156,8 @@ STR_ERROR_CAN_T_BUILD_SHIP_DEPOT :{WHITE}Can't bu STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING_DEPOT :{WHITE}... adjoins more than one existing depot STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE :{WHITE}... depot type not available +STR_ERROR_DEPOT_EXTENDING_PLATFORMS :{WHITE}Extending already reserved depot platforms +STR_ERROR_DEPOT_EXTENDED_RAIL_DEPOT_IS_NOT_FREE :{WHITE}Extended rail depot has a reserved tile and can't be converted STR_ERROR_CAN_T_RENAME_DEPOT :{WHITE}Can't rename depot... diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp index 95800867991dc..979cfb060b0ea 100644 --- a/src/pathfinder/yapf/yapf_rail.cpp +++ b/src/pathfinder/yapf/yapf_rail.cpp @@ -646,7 +646,9 @@ bool YapfTrainFindNearestSafeTile(const Train *v, TileIndex tile, Trackdir td, b /** if any track changes, this counter is incremented - that will invalidate segment cost cache */ int CSegmentCostCacheBase::s_rail_change_counter = 0; +extern void FixBigRailDepotSprites(Tile tile); void YapfNotifyTrackLayoutChange(TileIndex tile, Track track) { + FixBigRailDepotSprites(tile); CSegmentCostCacheBase::NotifyTrackLayoutChange(tile, track); } diff --git a/src/pbs.cpp b/src/pbs.cpp index 03b5311f894f4..209f2c0acf0fd 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -446,3 +446,26 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo return !HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits)); } + +/** + * Fix the sprites of depots to show it opened or closed depending on its neighbours. + * @param t Tile that has changed. + */ +void FixBigRailDepotSprites(Tile t) +{ + if (t == INVALID_TILE) return; + + /* Expand tile area to check. */ + TileArea ta = TileArea(t).Expand(1); + + for (Tile tile : ta) { + if (!IsExtendedRailDepotTile(tile)) continue; + CFollowTrackRail ft(GetTileOwner(tile), GetRailTypeInfo(GetTileRailType(tile))->compatible_railtypes); + Track track = GetRailDepotTrack(tile); + Trackdir trackdir = TrackToTrackdir(track); + if (track == TRACK_X) trackdir = ReverseTrackdir(trackdir); + bool opened = ft.Follow(tile, trackdir); + if (track == TRACK_Y) opened = !opened; + SB(tile.m5(), 1, 1, opened); + } +} diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index cb07c49d1c1ac..2868d174b539b 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -956,6 +956,7 @@ CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, Tile * @param railtype rail type * @param dir entrance direction * @param adjacent allow adjacent depots + * @param extended build extended depots * @param join_to depot to join to * @param end_tile end tile of the area to be built * @return the cost of this operation or an error @@ -963,11 +964,13 @@ CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, Tile * @todo When checking for the tile slope, * distinguish between "Flat land required" and "land sloped in wrong direction" */ -CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile) +CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, bool extended, DepotID join_to, TileIndex end_tile) { /* check railtype and valid direction for depot (0 through 3), 4 in total */ if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR; + if (Company::IsValidHumanID(_current_company) && !HasBit(_settings_game.depot.rail_depot_types, extended)) return_cmd_error(STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE); + CommandCost cost(EXPENSES_CONSTRUCTION); TileArea ta(tile, end_tile); Depot *depot = nullptr; @@ -976,14 +979,42 @@ CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType rai CommandCost ret = FindJoiningDepot(ta, VEH_TRAIN, join_to, depot, adjacent, flags); if (ret.Failed()) return ret; + Axis axis = DiagDirToAxis(dir); + /* Do not allow extending already occupied platforms. */ + if (extended && join_to != NEW_DEPOT) { + TileArea ta_ext = TileArea(ta.tile, ta.w, ta.h).Expand(1); + + uint max_coord; + uint min_coord; + if (axis == AXIS_X) { + min_coord = TileY(ta.tile); + max_coord = min_coord + ta.h; + } else { + min_coord = TileX(ta.tile); + max_coord = min_coord + ta.w; + } + + for (Tile t : ta_ext) { + if (!IsExtendedRailDepotTile(t)) continue; + if (GetDepotIndex(t) != depot->index) continue; + if (GetRailType(t) != railtype) continue; + if (!HasDepotReservation(t)) continue; + if (DiagDirToAxis(GetRailDepotDirection(t)) != axis) continue; + uint current = (axis == AXIS_X) ? TileY(t) : TileX(t); + if (!IsInsideMM(current, min_coord, max_coord)) continue; + return_cmd_error(STR_ERROR_DEPOT_EXTENDING_PLATFORMS); + } + } + uint8_t num_new_depot_tiles = 0; - uint8_t num_rotated_depot_tiles = 0; + uint8_t num_overbuilt_depot_tiles = 0; /* Prohibit construction if * The tile is non-flat AND * 1) build-on-slopes is disabled * 2) the tile is steep i.e. spans two height levels * 3) the exit points in the wrong direction + * 4) the tile is not an already built depot (or it is a compatible single rail tile for building extended depots) */ for (Tile t : ta) { if (IsBridgeAbove(t)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); @@ -994,42 +1025,82 @@ CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType rai !CanBuildDepotByTileh(dir, tileh)) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } + if (extended && !CanBuildDepotByTileh(ReverseDiagDir(dir), tileh)) { + return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + } cost.AddCost(_price[PR_BUILD_FOUNDATION]); } - /* Check whether a depot tile exists and it needs to be rotated. */ - if (IsRailDepotTile(t) && GetDepotIndex(t) == join_to && railtype == GetRailType(t)) { - if (dir == GetRailDepotDirection(t)) continue; - - ret = EnsureNoVehicleOnGround(t); - if (ret.Failed()) return ret; + if (extended) { + if (IsPlainRailTile(t) && !HasSignals(t) && GetRailType(t) == railtype) { + /* Allow overbuilding if the tile: + * - has rail, but no signals + * - it has exactly one track + * - the track is in line with the depot + * - the current rail type is the same as the to-be-built + */ + TrackBits tracks = GetTrackBits(t); + Track track = RemoveFirstTrack(&tracks); + uint invalid_dirs = 5 << DiagDirToAxis(dir); + Track expected_track = HasBit(invalid_dirs, DIAGDIR_NE) ? TRACK_X : TRACK_Y; + + if (tracks == TRACK_BIT_NONE && track == expected_track) { + cost.AddCost(Command::Do(flags, t, track).GetCost()); + /* With flags & ~DC_EXEC CmdLandscapeClear would fail since the rail still exists */ + if (cost.Failed()) return cost; + goto new_depot_tile; + } + } - num_rotated_depot_tiles++; - if (flags & DC_EXEC) { - SetRailDepotExitDirection(t, dir); + /* Skip already existing and compatible extended depots. */ + if (IsRailDepotTile(t) && IsExtendedRailDepotTile(t) && + GetDepotIndex(t) == join_to && railtype == GetRailType(t)) { + if (axis == DiagDirToAxis(GetRailDepotDirection(t))) continue; } } else { - cost.AddCost(Command::Do(flags, t)); - if (cost.Failed()) return cost; + /* Check whether this is a standard depot tile and it needs to be rotated. */ + if (IsRailDepotTile(t) && IsStandardRailDepotTile(t) && + GetDepotIndex(t) == join_to && railtype == GetRailType(t)) { + if (dir == GetRailDepotDirection(t)) continue; - num_new_depot_tiles++; - if (flags & DC_EXEC) { - MakeRailDepot(t, _current_company, depot->index, dir, railtype); - MarkTileDirtyByTile(t); + ret = EnsureNoVehicleOnGround(t); + if (ret.Failed()) return ret; + + num_overbuilt_depot_tiles++; + if (flags & DC_EXEC) { + SetRailDepotExitDirection(t, dir); + AddSideToSignalBuffer(t, INVALID_DIAGDIR, _current_company); + YapfNotifyTrackLayoutChange(t, DiagDirToDiagTrack(dir)); + MarkTileDirtyByTile(t); + } + continue; } } + cost.AddCost(Command::Do(flags, t)); + if (cost.Failed()) return cost; + +new_depot_tile: + num_new_depot_tiles++; + if (flags & DC_EXEC) { - AddSideToSignalBuffer(t, INVALID_DIAGDIR, _current_company); + MakeRailDepot(t, _current_company, depot->index, dir, railtype); + SB(t.m5(), 5, 1, extended); + + if (extended) { + AddTrackToSignalBuffer(t, DiagDirToDiagTrack(dir), _current_company); + } else { + AddSideToSignalBuffer(t, INVALID_DIAGDIR, _current_company); + } YapfNotifyTrackLayoutChange(t, DiagDirToDiagTrack(dir)); MarkTileDirtyByTile(t); } } - if (num_new_depot_tiles + num_rotated_depot_tiles == 0) return CommandCost(); + if (num_new_depot_tiles + num_overbuilt_depot_tiles == 0) return CommandCost(); - cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN] * (num_new_depot_tiles + num_rotated_depot_tiles)); - cost.AddCost(RailBuildCost(railtype) * (num_new_depot_tiles + num_rotated_depot_tiles)); + cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN] * (num_new_depot_tiles + num_overbuilt_depot_tiles)); + cost.AddCost(RailBuildCost(railtype) * (num_new_depot_tiles + num_overbuilt_depot_tiles)); if (flags & DC_EXEC) { Company::Get(_current_company)->infrastructure.rail[railtype] += num_new_depot_tiles; @@ -1551,6 +1622,56 @@ static Vehicle *UpdateTrainPowerProc(Vehicle *v, void *data) return nullptr; } +/** + * Returns whether a depot has an extended depot + * tile which is reserved. + * @param Depot pointer to a depot + * @return true iff \a dep has an extended depot tile reserved. + */ +bool HasAnyExtendedDepotReservedTile(Depot *dep) +{ + assert(dep != nullptr); + for (TileIndex tile : dep->ta) { + if (!IsExtendedDepotTile(tile)) continue; + if (GetDepotIndex(tile) != dep->index) continue; + if (HasDepotReservation(tile)) return true; + } + + return false; +} + +CommandCost ConvertExtendedDepot(DoCommandFlag flags, Depot *dep, RailType rail_type) +{ + CommandCost cost(EXPENSES_CONSTRUCTION); + assert(dep->owner == _current_company); + Company *c = Company::Get(dep->owner); + + for (TileIndex tile : dep->ta) { + if (!IsDepotTile(tile)) continue; + if (GetDepotIndex(tile) != dep->index) continue; + assert(!HasDepotReservation(tile)); + assert(dep->owner == GetTileOwner(tile)); + + /* Original railtype we are converting from */ + RailType type = GetRailType(tile); + + if (type == rail_type || (_settings_game.vehicle.disable_elrails && rail_type == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue; + + cost.AddCost(RailConvertCost(type, rail_type)); + + if (flags & DC_EXEC) { + c->infrastructure.rail[type]--; + c->infrastructure.rail[rail_type]++; + SetRailType(tile, rail_type); + MarkTileDirtyByTile(tile); + YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile)); + DirtyCompanyInfrastructureWindows(c->index); + } + } + + return cost; +} + /** * Convert one rail type to the other. You can convert normal rail to * monorail/maglev easily or vice-versa. @@ -1664,11 +1785,12 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s if (flags & DC_EXEC) { /* notify YAPF about the track layout change */ YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile)); + } - if (find(affected_depots.begin(), affected_depots.end(), (tile)) == affected_depots.end()) { - affected_depots.push_back(GetDepotIndex(tile)); - } + if (find(affected_depots.begin(), affected_depots.end(), (tile)) == affected_depots.end()) { + affected_depots.push_back(GetDepotIndex(tile)); } + cost.AddCost(RailConvertCost(type, totype)); } else { // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS if (flags & DC_EXEC) { @@ -1760,14 +1882,18 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s } } - if (flags & DC_EXEC) { - /* Update affected depots. */ - for (auto &depot_tile : affected_depots) { - Depot *dep = Depot::Get(depot_tile); + /* Update affected depots. */ + for (auto &depot_tile : affected_depots) { + Depot *dep = Depot::Get(depot_tile); + if (HasAnyExtendedDepotReservedTile(dep)) cost.MakeError(STR_ERROR_DEPOT_EXTENDED_RAIL_DEPOT_IS_NOT_FREE); + + if (flags & DC_EXEC) { dep->RescanDepotTiles(); InvalidateWindowData(WC_VEHICLE_DEPOT, dep->index); } + } + if (flags & DC_EXEC) { /* Railtype changed, update trains as when entering different track */ for (Train *v : affected_trains) { v->ConsistChanged(CCF_TRACK); @@ -1777,7 +1903,7 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s return found_convertible_track ? cost : error; } -static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) +static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags, bool keep_rail) { assert(IsRailDepotTile(tile)); @@ -1789,6 +1915,15 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; + if (HasDepotReservation(tile)) return CMD_ERROR; + + CommandCost total_cost(EXPENSES_CONSTRUCTION); + + if (keep_rail) { + /* Don't refund the 'steel' of the track when we keep the rail. */ + total_cost.AddCost(-_price[PR_CLEAR_RAIL]); + } + if (flags & DC_EXEC) { Depot *depot = Depot::GetByTile(tile); Company *c = Company::GetIfValid(depot->owner); @@ -1802,12 +1937,24 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) if (v != nullptr) FreeTrainTrackReservation(v); } - c->infrastructure.rail[GetRailType(tile)]--; - DirtyCompanyInfrastructureWindows(c->index); + Track track = GetRailDepotTrack(tile); + RailType rt = GetRailType(tile); + bool is_extended_depot = IsExtendedDepot(tile); DoClearSquare(tile); - AddSideToSignalBuffer(tile, dir, c->index); + if (keep_rail) { + MakeRailNormal(tile, depot->owner, TrackToTrackBits(track), rt); + } else { + c->infrastructure.rail[GetRailType(tile)]--; + DirtyCompanyInfrastructureWindows(c->index); + } + + if (is_extended_depot) { + AddTrackToSignalBuffer(tile, DiagDirToDiagTrack(dir), c->index); + } else { + AddSideToSignalBuffer(tile, dir, c->index); + } YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir)); if (v != nullptr) TryPathReserve(v, true); @@ -1815,7 +1962,8 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) depot->AfterAddRemove(TileArea(tile), false); } - return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]); + total_cost.AddCost(_price[PR_CLEAR_DEPOT_TRAIN]); + return total_cost; } /** @@ -1834,7 +1982,7 @@ CommandCost CmdRemoveTrainDepot(DoCommandFlag flags, TileIndex start_tile, TileI TileArea ta(start_tile, end_tile); for (TileIndex t : ta) { if (!IsRailDepotTile(t)) continue; - CommandCost ret = RemoveTrainDepot(t, flags); + CommandCost ret = RemoveTrainDepot(t, flags, IsExtendedDepot(t)); if (ret.Failed()) return ret; cost.AddCost(ret); } @@ -1890,7 +2038,7 @@ static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags) } case RAIL_TILE_DEPOT: - return RemoveTrainDepot(tile, flags); + return RemoveTrainDepot(tile, flags, false); default: return CMD_ERROR; @@ -2742,6 +2890,13 @@ static TrackStatus GetTileTrackStatus_Track(TileIndex tile, TransportType mode, } case RAIL_TILE_DEPOT: { + if (IsExtendedRailDepot(tile)) { + Track track = GetRailDepotTrack(tile); + trackbits = TrackToTrackBits(track); + break; + } + + /* Small depot. */ DiagDirection dir = GetRailDepotDirection(tile); if (side != INVALID_DIAGDIR && side != dir) break; @@ -2839,7 +2994,7 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td) } case RAIL_TILE_DEPOT: - td->str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT; + td->str = IsExtendedDepot(tile) ? STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT_EXTENDED : STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT; if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) { if (td->rail_speed > 0) { td->rail_speed = std::min(td->rail_speed, 61); @@ -3065,6 +3220,14 @@ static CommandCost TerraformTile_Track(TileIndex tile, DoCommandFlag flags, int return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price[PR_CLEAR_WATER] : (Money)0); } else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() && AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) { + if (IsExtendedRailDepotTile(tile) && GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) { + DiagDirection direction = GetRailDepotDirection(tile); + if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction) || + !AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) { + return Command::Do(flags, tile); + } + } + return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]); } return Command::Do(flags, tile); diff --git a/src/rail_cmd.h b/src/rail_cmd.h index 775bfa7ed7653..bcd28fa2e3f2d 100644 --- a/src/rail_cmd.h +++ b/src/rail_cmd.h @@ -19,7 +19,7 @@ CommandCost CmdBuildRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileI CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, Track track); CommandCost CmdBuildSingleRail(DoCommandFlag flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals); CommandCost CmdRemoveSingleRail(DoCommandFlag flags, TileIndex tile, Track track); -CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, DepotID depot_id, TileIndex end_tile); +CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, bool extended, DepotID depot_id, TileIndex end_tile); CommandCost CmdRemoveTrainDepot(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile); CommandCost CmdBuildSingleSignal(DoCommandFlag flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy); CommandCost CmdRemoveSingleSignal(DoCommandFlag flags, TileIndex tile, Track track); @@ -42,6 +42,6 @@ DEF_CMD_TRAIT(CMD_REMOVE_SIGNAL_TRACK, CmdRemoveSignalTrack, CMD_AUTO, CommandCallback CcPlaySound_CONSTRUCTION_RAIL; CommandCallback CcStation; CommandCallback CcBuildRailTunnel; -void CcRailDepot(Commands cmd, const CommandCost &result, TileIndex tile, RailType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile); +void CcRailDepot(Commands cmd, const CommandCost &result, TileIndex tile, RailType rt, DiagDirection dir, bool adjacent, bool extended, DepotID join_to, TileIndex end_tile); #endif /* RAIL_CMD_H */ diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 6001a5de033ce..0e801ba782a04 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -138,9 +138,10 @@ static const DiagDirection _place_depot_extra_dir[12] = { DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_NW, DIAGDIR_NE, }; -void CcRailDepot(Commands, const CommandCost &result, TileIndex start_tile, RailType, DiagDirection dir, bool, DepotID, TileIndex end_tile) +void CcRailDepot(Commands, const CommandCost &result, TileIndex start_tile, RailType, DiagDirection dir, bool, bool extended, DepotID, TileIndex end_tile) { if (result.Failed()) return; + if (extended) return; if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, start_tile); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); @@ -613,7 +614,8 @@ struct BuildRailToolbarWindow : Window { break; case WID_RAT_BUILD_DEPOT: - if (HandlePlacePushButton(this, WID_RAT_BUILD_DEPOT, GetRailTypeInfo(_cur_railtype)->cursor.depot, HT_RECT)) { + case WID_RAT_BUILD_EXTENDED_DEPOT: + if (HandlePlacePushButton(this, widget, GetRailTypeInfo(_cur_railtype)->cursor.depot, HT_RECT)) { ShowBuildTrainDepotPicker(this); this->last_user_action = widget; } @@ -700,10 +702,14 @@ struct BuildRailToolbarWindow : Window { PlaceProc_DemolishArea(tile); break; - case WID_RAT_BUILD_DEPOT: { + case WID_RAT_BUILD_DEPOT: + case WID_RAT_BUILD_EXTENDED_DEPOT: { CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN); - ViewportPlaceMethod vpm = (DiagDirToAxis(_build_depot_direction) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED; + ViewportPlaceMethod vpm = VPM_X_AND_Y_LIMITED; + if (this->last_user_action == WID_RAT_BUILD_DEPOT) { + vpm = (DiagDirToAxis(_build_depot_direction) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED; + } VpStartPlaceSizing(tile, vpm, _remove_button_clicked ? DDSP_REMOVE_DEPOT : DDSP_BUILD_DEPOT); VpSetPlaceSizingLimit(_settings_game.depot.depot_spread); break; @@ -805,13 +811,15 @@ struct BuildRailToolbarWindow : Window { break; case DDSP_BUILD_DEPOT: + case DDSP_REMOVE_DEPOT: if (_remove_button_clicked) { Command::Post(STR_ERROR_CAN_T_REMOVE_TRAIN_DEPOT, CcPlaySound_CONSTRUCTION_RAIL, start_tile, end_tile); } else { bool adjacent = _ctrl_pressed; + bool extended = this->last_user_action == WID_RAT_BUILD_EXTENDED_DEPOT; auto proc = [=](DepotID join_to) -> bool { - return Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, start_tile, _cur_railtype, _build_depot_direction, adjacent, join_to, end_tile); + return Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, start_tile, _cur_railtype, _build_depot_direction, adjacent, extended, join_to, end_tile); }; ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_TRAIN); diff --git a/src/script/api/script_rail.cpp b/src/script/api/script_rail.cpp index f0c79fcff927b..d92010de7fab2 100644 --- a/src/script/api/script_rail.cpp +++ b/src/script/api/script_rail.cpp @@ -145,7 +145,7 @@ DiagDirection entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? DIAGDIR_SE : DIAGDIR_NW) : (::TileX(tile) < ::TileX(front) ? DIAGDIR_SW : DIAGDIR_NE); - return ScriptObject::Command::Do(tile, (::RailType)ScriptObject::GetRailType(), entrance_dir, false, INVALID_DEPOT, tile); + return ScriptObject::Command::Do(tile, (::RailType)ScriptObject::GetRailType(), entrance_dir, false, false, INVALID_DEPOT, tile); } /* static */ bool ScriptRail::RemoveRailDepot(TileIndex start_tile, TileIndex end_tile) From 81224849c6ee5917403a77e6a4ad075932dd3bc3 Mon Sep 17 00:00:00 2001 From: Juanjo Date: Sat, 4 Jan 2014 16:21:24 +0100 Subject: [PATCH 048/143] Add: New depot direction picker for extended rail depots. --- src/rail_gui.cpp | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 0e801ba782a04..94d67ab5ce330 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -73,7 +73,7 @@ static StationPickerSelection _station_gui; ///< Settings of the station picker. static void HandleStationPlacement(TileIndex start, TileIndex end); -static void ShowBuildTrainDepotPicker(Window *parent); +static void ShowBuildTrainDepotPicker(Window *parent, bool extended_depot); static void ShowBuildWaypointPicker(Window *parent); static Window *ShowStationBuilder(Window *parent); static void ShowSignalBuilder(Window *parent); @@ -616,7 +616,7 @@ struct BuildRailToolbarWindow : Window { case WID_RAT_BUILD_DEPOT: case WID_RAT_BUILD_EXTENDED_DEPOT: if (HandlePlacePushButton(this, widget, GetRailTypeInfo(_cur_railtype)->cursor.depot, HT_RECT)) { - ShowBuildTrainDepotPicker(this); + ShowBuildTrainDepotPicker(this, widget == WID_RAT_BUILD_EXTENDED_DEPOT); this->last_user_action = widget; } break; @@ -1768,9 +1768,24 @@ static void ShowSignalBuilder(Window *parent) } struct BuildRailDepotWindow : public PickerWindowBase { - BuildRailDepotWindow(WindowDesc &desc, Window *parent) : PickerWindowBase(desc, parent) + + BuildRailDepotWindow(WindowDesc &desc, Window *parent, bool extended_depot) : PickerWindowBase(desc, parent) { this->InitNested(TRANSPORT_RAIL); + + /* Fix direction for extended depots. */ + if (extended_depot) { + switch ((BuildRailDepotWidgets)_build_depot_direction) { + case WID_BRAD_DEPOT_NE: + _build_depot_direction++; + break; + case WID_BRAD_DEPOT_NW: + _build_depot_direction--; + break; + default: break; + } + } + this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction); } @@ -1852,9 +1867,30 @@ static WindowDesc _build_depot_desc( _nested_build_depot_widgets ); -static void ShowBuildTrainDepotPicker(Window *parent) +/** Nested widget definition of the build extended rail depot window */ +static const NWidgetPart _nested_build_extended_depot_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_BUILD_DEPOT_TRAIN_ORIENTATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), SetPadding(WidgetDimensions::unscaled.picker), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAD_DEPOT_SW), SetMinimalSize(66, 50), SetDataTip(0x0, STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAD_DEPOT_SE), SetMinimalSize(66, 50), SetDataTip(0x0, STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _build_extended_depot_desc( + WDP_AUTO, nullptr, 0, 0, + WC_BUILD_DEPOT, WC_BUILD_TOOLBAR, + WDF_CONSTRUCTION, + _nested_build_extended_depot_widgets +); + +static void ShowBuildTrainDepotPicker(Window *parent, bool extended_depot) { - new BuildRailDepotWindow(_build_depot_desc, parent); + new BuildRailDepotWindow(extended_depot ? _build_extended_depot_desc : _build_depot_desc, parent, extended_depot); } class WaypointPickerCallbacks : public PickerCallbacksNewGRFClass { From 3dc62db020b870f30e8064920a478dc9c4c05dc4 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 23 Dec 2020 22:32:53 +0100 Subject: [PATCH 049/143] Codechange: Move platform related code to separate files. --- src/CMakeLists.txt | 3 + src/base_station_base.h | 18 ---- src/economy.cpp | 10 +- src/newgrf_station.cpp | 1 + src/pathfinder/follow_track.hpp | 12 +-- src/pathfinder/yapf/yapf_costrail.hpp | 9 +- src/pathfinder/yapf/yapf_rail.cpp | 1 + src/pbs.cpp | 22 ---- src/platform.cpp | 138 ++++++++++++++++++++++++++ src/platform_func.h | 104 +++++++++++++++++++ src/platform_type.h | 22 ++++ src/station.cpp | 37 ------- src/station_base.h | 3 - src/station_cmd.cpp | 18 ++-- src/station_map.h | 22 ---- src/train_cmd.cpp | 13 +-- src/waypoint_base.h | 10 -- 17 files changed, 301 insertions(+), 142 deletions(-) create mode 100644 src/platform.cpp create mode 100644 src/platform_func.h create mode 100644 src/platform_type.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5f7847ff8a7bc..3886fa5f21c7f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -342,6 +342,9 @@ add_files( picker_func.h picker_gui.cpp picker_gui.h + platform_func.h + platform_type.h + platform.cpp progress.cpp progress.h querystring_gui.h diff --git a/src/base_station_base.h b/src/base_station_base.h index 0c0ad2277078c..de8688006e613 100644 --- a/src/base_station_base.h +++ b/src/base_station_base.h @@ -140,24 +140,6 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> { */ virtual void GetTileArea(TileArea *ta, StationType type) const = 0; - - /** - * Obtain the length of a platform - * @pre tile must be a rail station tile - * @param tile A tile that contains the platform in question - * @return The length of the platform - */ - virtual uint GetPlatformLength(TileIndex tile) const = 0; - - /** - * Determines the REMAINING length of a platform, starting at (and including) - * the given tile. - * @param tile the tile from which to start searching. Must be a rail station tile - * @param dir The direction in which to search. - * @return The platform length - */ - virtual uint GetPlatformLength(TileIndex tile, DiagDirection dir) const = 0; - /** * Get the base station belonging to a specific tile. * @param tile The tile to get the base station from. diff --git a/src/economy.cpp b/src/economy.cpp index 5c0d4f7bec7a2..9a9e54cfc272f 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -56,6 +56,7 @@ #include "timer/timer_game_calendar.h" #include "timer/timer_game_economy.h" #include "depot_base.h" +#include "platform_func.h" #include "table/strings.h" #include "table/pricebase.h" @@ -1621,14 +1622,13 @@ static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft, * Update the vehicle's load_unload_ticks, the time it will wait until it tries to load or unload * again. Adjust for overhang of trains and set it at least to 1. * @param front The vehicle to be updated. - * @param st The station the vehicle is loading at. * @param ticks The time it would normally wait, based on cargo loaded and unloaded. */ -static void UpdateLoadUnloadTicks(Vehicle *front, const Station *st, int ticks) +static void UpdateLoadUnloadTicks(Vehicle *front, int ticks) { if (front->type == VEH_TRAIN && _settings_game.order.station_length_loading_penalty) { /* Each platform tile is worth 2 rail vehicles. */ - int overhang = front->GetGroundVehicleCache()->cached_total_length - st->GetPlatformLength(front->tile) * TILE_SIZE; + int overhang = front->GetGroundVehicleCache()->cached_total_length - GetPlatformLength(front->tile) * TILE_SIZE; if (overhang > 0) { ticks <<= 1; ticks += (overhang * ticks) / 8; @@ -1890,9 +1890,9 @@ static void LoadUnloadVehicle(Vehicle *front) SetBit(front->vehicle_flags, VF_STOP_LOADING); } - UpdateLoadUnloadTicks(front, st, new_load_unload_ticks); + UpdateLoadUnloadTicks(front, new_load_unload_ticks); } else { - UpdateLoadUnloadTicks(front, st, 20); // We need the ticks for link refreshing. + UpdateLoadUnloadTicks(front, 20); // We need the ticks for link refreshing. bool finished_loading = true; if (front->current_order.GetLoadType() & OLFB_FULL_LOAD) { if (front->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) { diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index af67fbf648bca..86462ff4b9da0 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -24,6 +24,7 @@ #include "newgrf_animation_base.h" #include "newgrf_class_func.h" #include "timer/timer_game_calendar.h" +#include "platform_func.h" #include "safeguards.h" diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index 9a16bcba93e6e..ed9e24a240148 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -18,6 +18,7 @@ #include "../tunnelbridge_map.h" #include "../depot_map.h" #include "pathfinder_func.h" +#include "../platform_func.h" /** * Track follower helper template class (can serve pathfinders and vehicle @@ -370,12 +371,11 @@ struct CFollowTrackT /* special handling for rail stations - get to the end of platform */ if (IsRailTT() && m_is_station) { - /* entered railway station - * get platform length */ - uint length = BaseStation::GetByTile(m_new_tile)->GetPlatformLength(m_new_tile, TrackdirToExitdir(m_old_td)); - /* how big step we must do to get to the last platform tile? */ - m_tiles_skipped = length - 1; - /* move to the platform end */ + /* Entered a platform. */ + assert(HasStationTileRail(m_new_tile)); + /* How big step we must do to get to the last platform tile? */ + m_tiles_skipped = GetPlatformLength(m_new_tile, TrackdirToExitdir(m_old_td)) - 1; + /* Move to the platform end. */ TileIndexDiff diff = TileOffsByDiagDir(m_exitdir); diff *= m_tiles_skipped; m_new_tile = TileAdd(m_new_tile, diff); diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index d50bb24377ff6..2a5bc290d6c1c 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -590,12 +590,11 @@ class CYapfCostRailT : public CYapfCostBase { } } - /* Station platform-length penalty. */ + /* Platform-length penalty. */ if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) { - const BaseStation *st = BaseStation::GetByTile(n.GetLastTile()); - assert(st != nullptr); - uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir()))); - /* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */ + assert(HasStationTileRail(n.GetLastTile())); + uint platform_length = GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir()))); + /* Reduce the extra cost caused by passing-platform penalty (each platform receives it in the segment cost). */ extra_cost -= Yapf().PfGetSettings().rail_station_penalty * platform_length; /* Add penalty for the inappropriate platform length. */ extra_cost += PlatformLengthPenalty(platform_length); diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp index 979cfb060b0ea..8d7e55267e643 100644 --- a/src/pathfinder/yapf/yapf_rail.cpp +++ b/src/pathfinder/yapf/yapf_rail.cpp @@ -16,6 +16,7 @@ #include "yapf_destrail.hpp" #include "../../viewport_func.h" #include "../../newgrf_station.h" +#include "../../platform_func.h" #include "../../safeguards.h" diff --git a/src/pbs.cpp b/src/pbs.cpp index 209f2c0acf0fd..6cc6a33aa289d 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -47,28 +47,6 @@ TrackBits GetReservedTrackbits(TileIndex t) return TRACK_BIT_NONE; } -/** - * Set the reservation for a complete station platform. - * @pre IsRailStationTile(start) - * @param start starting tile of the platform - * @param dir the direction in which to follow the platform - * @param b the state the reservation should be set to - */ -void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b) -{ - TileIndex tile = start; - TileIndexDiff diff = TileOffsByDiagDir(dir); - - assert(IsRailStationTile(start)); - assert(GetRailStationAxis(start) == DiagDirToAxis(dir)); - - do { - SetRailStationReservation(tile, b); - MarkTileDirtyByTile(tile); - tile = TileAdd(tile, diff); - } while (IsCompatibleTrainStationTile(tile, start)); -} - /** * Try to reserve a specific track on a tile * @param tile the tile diff --git a/src/platform.cpp b/src/platform.cpp new file mode 100644 index 0000000000000..103d5a29f2866 --- /dev/null +++ b/src/platform.cpp @@ -0,0 +1,138 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file platform.cpp Implementation of platform functions. */ + +#include "stdafx.h" +#include "station_map.h" +#include "platform_func.h" +#include "viewport_func.h" + +/** + * Set the reservation for a complete station platform. + * @pre IsRailStationTile(start) + * @param start starting tile of the platform + * @param dir the direction in which to follow the platform + * @param b the state the reservation should be set to + */ +void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b) +{ + TileIndex tile = start; + TileIndexDiff diff = TileOffsByDiagDir(dir); + + assert(IsRailStationTile(start)); + assert(GetRailStationAxis(start) == DiagDirToAxis(dir)); + + do { + SetRailStationReservation(tile, b); + MarkTileDirtyByTile(tile); + tile = TileAdd(tile, diff); + } while (IsCompatibleTrainStationTile(tile, start)); +} + +/** + * Set the reservation for a complete platform in a given direction. + * @param start starting tile of the platform + * @param dir the direction in which to follow the platform + * @param b the state the reservation should be set to + */ +void SetPlatformReservation(TileIndex start, DiagDirection dir, bool b) +{ + switch (GetPlatformType(start)) { + case PT_RAIL_STATION: + SetRailStationPlatformReservation(start, dir, b); + return; + case PT_RAIL_WAYPOINT: + SetRailStationReservation(start, b); + return; + default: NOT_REACHED(); + } +} + +/** + * Get the length of a rail station platform. + * @pre IsRailStationTile(tile) + * @param tile Tile to check + * @return The length of the platform in tile length. + */ +uint GetRailStationPlatformLength(TileIndex tile) +{ + assert(IsRailStationTile(tile)); + + TileIndexDiff delta = (GetRailStationAxis(tile) == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + + TileIndex t = tile; + uint len = 0; + do { + t -= delta; + len++; + } while (IsCompatibleTrainStationTile(t, tile)); + + t = tile; + do { + t += delta; + len++; + } while (IsCompatibleTrainStationTile(t, tile)); + + return len - 1; +} + +/** + * Get the length of a rail station platform in a given direction. + * @pre IsRailStationTile(tile) + * @param tile Tile to check + * @param dir Direction to check + * @return The length of the platform in tile length in the given direction. + */ +uint GetRailStationPlatformLength(TileIndex tile, DiagDirection dir) +{ + TileIndex start_tile = tile; + uint length = 0; + assert(IsRailStationTile(tile)); + assert(dir < DIAGDIR_END); + + do { + length++; + tile += TileOffsByDiagDir(dir); + } while (IsCompatibleTrainStationTile(tile, start_tile)); + + return length; +} + +/** + * Get the length of a platform. + * @param tile Tile to check + * @return The length of the platform in tile length. + */ +uint GetPlatformLength(TileIndex tile) +{ + switch (GetPlatformType(tile)) { + case PT_RAIL_STATION: + return GetRailStationPlatformLength(tile); + case PT_RAIL_WAYPOINT: + return 1; + default: NOT_REACHED(); + } +} + +/** + * Get the length of a rail depot platform in a given direction. + * @pre IsRailDepotTile(tile) + * @param tile Tile to check + * @param dir Direction to check + * @return The length of the platform in tile length in the given direction. + */ +uint GetPlatformLength(TileIndex tile, DiagDirection dir) +{ + switch (GetPlatformType(tile)) { + case PT_RAIL_STATION: + return GetRailStationPlatformLength(tile, dir); + case PT_RAIL_WAYPOINT: + return 1; + default: NOT_REACHED(); + } +} diff --git a/src/platform_func.h b/src/platform_func.h new file mode 100644 index 0000000000000..5b1275d3ef148 --- /dev/null +++ b/src/platform_func.h @@ -0,0 +1,104 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file platform_func.h Functions related with platforms (tiles in a row that are connected somehow). */ + +#ifndef PLATFORM_FUNC_H +#define PLATFORM_FUNC_H + +#include "station_map.h" +#include "platform_type.h" + +/** + * Check if a tile is a valid continuation to a railstation tile. + * The tile \a test_tile is a valid continuation to \a station_tile, if all of the following are true: + * \li \a test_tile is a rail station tile + * \li the railtype of \a test_tile is compatible with the railtype of \a station_tile + * \li the tracks on \a test_tile and \a station_tile are in the same direction + * \li both tiles belong to the same station + * \li \a test_tile is not blocked (@see IsStationTileBlocked) + * @param test_tile Tile to test + * @param station_tile Station tile to compare with + * @pre IsRailStationTile(station_tile) + * @return true if the two tiles are compatible + */ +static inline bool IsCompatibleTrainStationTile(TileIndex test_tile, TileIndex station_tile) +{ + assert(IsRailStationTile(station_tile)); + return IsRailStationTile(test_tile) && !IsStationTileBlocked(test_tile) && + IsCompatibleRail(GetRailType(test_tile), GetRailType(station_tile)) && + GetRailStationAxis(test_tile) == GetRailStationAxis(station_tile) && + GetStationIndex(test_tile) == GetStationIndex(station_tile); +} + +/** + * Returns the type of platform of a given tile. + * @param tile Tile to check + * @return the type of platform (rail station, rail waypoint...) + */ +static inline PlatformType GetPlatformType(TileIndex tile) +{ + switch (GetTileType(tile)) { + case MP_STATION: + if (IsRailStation(tile)) return PT_RAIL_STATION; + if (IsRailWaypoint(tile)) return PT_RAIL_WAYPOINT; + break; + default: break; + } + + return INVALID_PLATFORM_TYPE; +} + +/** + * Check whether a tile is a known platform type. + * @param tile to check + * @return whether the tile is a known platform type. + */ +static inline bool IsPlatformTile(TileIndex tile) +{ + return GetPlatformType(tile) != INVALID_PLATFORM_TYPE; +} + +/** + * Check whether a platform tile is reserved. + * @param tile to check + * @return whether the platform tile is reserved + */ +static inline bool HasPlatformReservation(TileIndex tile) +{ + switch(GetPlatformType(tile)) { + case PT_RAIL_STATION: + case PT_RAIL_WAYPOINT: + return HasStationReservation(tile); + default: NOT_REACHED(); + } +} + +/** + * Check whether two tiles are compatible platform tiles: they must have the same + * platform type and (depending on the platform type) its railtype or other specs. + * @param test_tile the tile to check + * @param orig_tile the tile with the platform type we are interested in + * @return whether the two tiles are compatible tiles for defining a platform + */ +static inline bool IsCompatiblePlatformTile(TileIndex test_tile, TileIndex orig_tile) +{ + switch (GetPlatformType(orig_tile)) { + case PT_RAIL_STATION: + return IsCompatibleTrainStationTile(test_tile, orig_tile); + case PT_RAIL_WAYPOINT: + return test_tile == orig_tile; + default: NOT_REACHED(); + } +} + +void SetPlatformReservation(TileIndex start, DiagDirection dir, bool b); + +uint GetPlatformLength(TileIndex tile); +uint GetPlatformLength(TileIndex tile, DiagDirection dir); + +#endif /* PLATFORM_FUNC_H */ diff --git a/src/platform_type.h b/src/platform_type.h new file mode 100644 index 0000000000000..a2a48c90b3e5d --- /dev/null +++ b/src/platform_type.h @@ -0,0 +1,22 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file platform_type.h Types related to platforms. */ + +#ifndef PLATFORM_TYPE_H +#define PLATFORM_TYPE_H + +#include "core/enum_type.hpp" + +enum PlatformType { + PT_RAIL_STATION, + PT_RAIL_WAYPOINT, + PT_END, + INVALID_PLATFORM_TYPE = PT_END, +}; + +#endif /* PLATFORM_TYPE_H */ diff --git a/src/station.cpp b/src/station.cpp index cf07e27f92e87..95475eba6a657 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -269,43 +269,6 @@ void Station::MarkTilesDirty(bool cargo_change) const } } -/* virtual */ uint Station::GetPlatformLength(TileIndex tile) const -{ - assert(this->TileBelongsToRailStation(tile)); - - TileIndexDiff delta = (GetRailStationAxis(tile) == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); - - TileIndex t = tile; - uint len = 0; - do { - t -= delta; - len++; - } while (IsCompatibleTrainStationTile(t, tile)); - - t = tile; - do { - t += delta; - len++; - } while (IsCompatibleTrainStationTile(t, tile)); - - return len - 1; -} - -/* virtual */ uint Station::GetPlatformLength(TileIndex tile, DiagDirection dir) const -{ - TileIndex start_tile = tile; - uint length = 0; - assert(IsRailStationTile(tile)); - assert(dir < DIAGDIR_END); - - do { - length++; - tile += TileOffsByDiagDir(dir); - } while (IsCompatibleTrainStationTile(tile, start_tile)); - - return length; -} - /** * Get the catchment size of an individual station tile. * @param tile Station tile to get catchment size of. diff --git a/src/station_base.h b/src/station_base.h index f5db44f23b677..531339b85b1a8 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -488,9 +488,6 @@ struct Station final : SpecializedStation { void MoveSign(TileIndex new_xy) override; void AfterStationTileSetChange(bool adding, StationType type); - - uint GetPlatformLength(TileIndex tile, DiagDirection dir) const override; - uint GetPlatformLength(TileIndex tile) const override; void RecomputeCatchment(bool no_clear_nearby_lists = false); static void RecomputeCatchmentForAll(); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index a0e8f8c80514f..9d4da2f9584e1 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -68,6 +68,7 @@ #include "cheat_type.h" #include "road_func.h" #include "depot_base.h" +#include "platform_func.h" #include "widgets/station_widget.h" @@ -1240,9 +1241,10 @@ CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_ static void FreeTrainReservation(Train *v) { FreeTrainTrackReservation(v); - if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false); + if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false); v = v->Last(); - if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), false); + if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), false); + } /** @@ -1251,10 +1253,10 @@ static void FreeTrainReservation(Train *v) */ static void RestoreTrainReservation(Train *v) { - if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true); + if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true); TryPathReserve(v, true, true); v = v->Last(); - if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true); + if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true); } /** @@ -1522,21 +1524,21 @@ CommandCost CmdBuildRailStation(DoCommandFlag flags, TileIndex tile_org, RailTyp TileIndex platform_end = tile; /* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */ - for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) { + for (TileIndex next_tile = platform_begin - tile_offset; IsCompatiblePlatformTile(next_tile, platform_begin); next_tile -= tile_offset) { platform_begin = next_tile; } - for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) { + for (TileIndex next_tile = platform_end + tile_offset; IsCompatiblePlatformTile(next_tile, platform_end); next_tile += tile_offset) { platform_end = next_tile; } /* If there is at least on reservation on the platform, we reserve the whole platform. */ bool reservation = false; for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) { - reservation = HasStationReservation(t); + reservation = HasPlatformReservation(t); } if (reservation) { - SetRailStationPlatformReservation(platform_begin, dir, true); + SetPlatformReservation(platform_begin, dir, true); } } diff --git a/src/station_map.h b/src/station_map.h index 906ad6193b435..0869d49db5a1f 100644 --- a/src/station_map.h +++ b/src/station_map.h @@ -521,28 +521,6 @@ inline TrackBits GetRailStationTrackBits(Tile t) return AxisToTrackBits(GetRailStationAxis(t)); } -/** - * Check if a tile is a valid continuation to a railstation tile. - * The tile \a test_tile is a valid continuation to \a station_tile, if all of the following are true: - * \li \a test_tile is a rail station tile - * \li the railtype of \a test_tile is compatible with the railtype of \a station_tile - * \li the tracks on \a test_tile and \a station_tile are in the same direction - * \li both tiles belong to the same station - * \li \a test_tile is not blocked (@see IsStationTileBlocked) - * @param test_tile Tile to test - * @param station_tile Station tile to compare with - * @pre IsRailStationTile(station_tile) - * @return true if the two tiles are compatible - */ -inline bool IsCompatibleTrainStationTile(Tile test_tile, Tile station_tile) -{ - assert(IsRailStationTile(station_tile)); - return IsRailStationTile(test_tile) && !IsStationTileBlocked(test_tile) && - IsCompatibleRail(GetRailType(test_tile), GetRailType(station_tile)) && - GetRailStationAxis(test_tile) == GetRailStationAxis(station_tile) && - GetStationIndex(test_tile) == GetStationIndex(station_tile); -} - /** * Get the reservation state of the rail station * @pre HasStationRail(t) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 823dd379e28dc..47e276a74249e 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -40,6 +40,7 @@ #include "timer/timer_game_calendar.h" #include "timer/timer_game_economy.h" #include "depot_base.h" +#include "platform_func.h" #include "table/strings.h" #include "table/train_sprites.h" @@ -264,9 +265,9 @@ void Train::ConsistChanged(ConsistChangeFlags allowed_changes) */ int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length) { - const Station *st = Station::Get(station_id); - *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE; - *station_length = st->GetPlatformLength(tile) * TILE_SIZE; + assert(IsRailStationTile(tile)); + *station_ahead = GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE; + *station_length = GetPlatformLength(tile) * TILE_SIZE; /* Default to the middle of the station for stations stops that are not in * the order list like intermediate stations when non-stop is disabled */ @@ -2184,7 +2185,7 @@ void ReverseTrainDirection(Train *v) /* If we are on a depot tile facing outwards, do not treat the current tile as safe. */ if (IsStandardRailDepotTile(v->tile) && TrackdirToExitdir(v->GetVehicleTrackdir()) == GetRailDepotDirection(v->tile)) first_tile_okay = false; - if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true); + if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true); if (TryPathReserve(v, false, first_tile_okay)) { /* Do a look-ahead now in case our current tile was already a safe tile. */ CheckNextTrainTile(v); @@ -2527,8 +2528,8 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_ TileIndex new_tile = TileAddByDiagDir(tile, dir); /* If the new tile is not a further tile of the same station, we * clear the reservation for the whole platform. */ - if (!IsCompatibleTrainStationTile(new_tile, tile)) { - SetRailStationPlatformReservation(tile, ReverseDiagDir(dir), false); + if (!IsCompatiblePlatformTile(new_tile, tile)) { + SetPlatformReservation(tile, ReverseDiagDir(dir), false); } } else { /* Any other tile */ diff --git a/src/waypoint_base.h b/src/waypoint_base.h index cbf2e1e608899..a7017d9acbf74 100644 --- a/src/waypoint_base.h +++ b/src/waypoint_base.h @@ -45,16 +45,6 @@ struct Waypoint final : SpecializedStation { void GetTileArea(TileArea *ta, StationType type) const override; - uint GetPlatformLength(TileIndex, DiagDirection) const override - { - return 1; - } - - uint GetPlatformLength(TileIndex) const override - { - return 1; - } - /** * Is this a single tile waypoint? * @return true if it is. From 322008be71061fd436c397b01c4c59fa1e992f57 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 20 Mar 2021 19:50:19 +0100 Subject: [PATCH 050/143] Add: Code dealing with extended rail depot platforms. --- src/depot.cpp | 33 ++++- src/pathfinder/follow_track.hpp | 47 ++++-- src/pathfinder/yapf/yapf_costrail.hpp | 4 +- src/pbs.cpp | 22 +-- src/platform.cpp | 205 ++++++++++++++++++++++++++ src/platform_func.h | 35 +++++ src/platform_type.h | 1 + src/train_cmd.cpp | 20 ++- 8 files changed, 332 insertions(+), 35 deletions(-) diff --git a/src/depot.cpp b/src/depot.cpp index 9373182e8894f..6bf7c8f99d03a 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -18,6 +18,7 @@ #include "command_func.h" #include "vehicle_base.h" #include "viewport_kdtree.h" +#include "platform_func.h" #include "safeguards.h" @@ -211,6 +212,30 @@ void Depot::AfterAddRemove(TileArea ta, bool adding) InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type); } +/** + * Check whether a tile is a destination tile, such as the starting tiles of + * rail platforms (and not the middle tiles of the platforms). + * @param dep The depot being checked + * @param tile The tile being checked + * @return Whether the tile is of the given depot. + */ +bool IsDepotDestTile(Depot *dep, TileIndex tile) +{ + assert(IsDepotTile(tile)); + assert(GetDepotIndex(tile) == dep->index); + + switch (dep->veh_type) { + case VEH_TRAIN: + assert(IsRailDepotTile(tile)); + return !IsExtendedRailDepot(tile) || IsAnyStartPlatformTile(tile); + case VEH_ROAD: + case VEH_SHIP: + case VEH_AIRCRAFT: + return true; + default: NOT_REACHED(); + } +} + /** * Rescan depot_tiles. Done after AfterAddRemove and SaveLoad. * Updates the tiles of the depot and its railtypes/roadtypes... @@ -224,7 +249,7 @@ void Depot::RescanDepotTiles() for (TileIndex tile : this->ta) { if (!IsDepotTile(tile)) continue; if (GetDepotIndex(tile) != this->index) continue; - this->depot_tiles.push_back(tile); + if (IsDepotDestTile(this, tile)) this->depot_tiles.push_back(tile); switch (veh_type) { case VEH_ROAD: this->r_types.road_types |= GetPresentRoadTypes(tile); @@ -267,8 +292,12 @@ void UpdateExtendedDepotReservation(Vehicle *v, bool reserve) SetDepotReservation(v->tile, res_type); break; - case VEH_TRAIN: + case VEH_TRAIN: { + DiagDirection dir = GetRailDepotDirection(v->tile); + SetDepotReservation(GetPlatformExtremeTile(v->tile, dir), res_type); + SetDepotReservation(GetPlatformExtremeTile(v->tile, ReverseDiagDir(dir)), res_type); break; + } case VEH_AIRCRAFT: break; diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index ed9e24a240148..2bdd6dffadc70 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -47,7 +47,8 @@ struct CFollowTrackT bool m_is_tunnel; ///< last turn passed tunnel bool m_is_bridge; ///< last turn passed bridge ramp bool m_is_station; ///< last turn passed station - int m_tiles_skipped; ///< number of skipped tunnel or station tiles + bool m_is_extended_depot; ///< last turn passed depot + int m_tiles_skipped; ///< number of skipped tunnel, depot or station tiles ErrorCode m_err; RailTypes m_railtypes; @@ -81,7 +82,7 @@ struct CFollowTrackT m_new_tile = INVALID_TILE; m_new_td_bits = TRACKDIR_BIT_NONE; m_exitdir = INVALID_DIAGDIR; - m_is_station = m_is_bridge = m_is_tunnel = false; + m_is_station = m_is_bridge = m_is_tunnel = m_is_extended_depot = false; m_tiles_skipped = 0; m_err = EC_NONE; m_railtypes = railtype_override; @@ -171,11 +172,11 @@ struct CFollowTrackT { if (!DoTrackMasking()) return true; - if (m_is_station) { - /* Check skipped station tiles as well. */ + if (m_is_station || m_is_extended_depot) { + /* Check skipped station and depot tiles as well. */ TileIndexDiff diff = TileOffsByDiagDir(m_exitdir); for (TileIndex tile = m_new_tile - diff * m_tiles_skipped; tile != m_new_tile; tile += diff) { - if (HasStationReservation(tile)) { + if ((m_is_station && HasStationReservation(tile)) || (m_is_extended_depot && HasDepotReservation(tile))) { m_new_td_bits = TRACKDIR_BIT_NONE; m_err = EC_RESERVED; return false; @@ -201,7 +202,7 @@ struct CFollowTrackT /** Follow the m_exitdir from m_old_tile and fill m_new_tile and m_tiles_skipped */ inline void FollowTileExit() { - m_is_station = m_is_bridge = m_is_tunnel = false; + m_is_station = m_is_bridge = m_is_tunnel = m_is_extended_depot = false; m_tiles_skipped = 0; /* extra handling for tunnels and bridges in our direction */ @@ -225,9 +226,13 @@ struct CFollowTrackT /* normal or station tile, do one step */ m_new_tile = TileAddByDiagDir(m_old_tile, m_exitdir); - /* special handling for stations */ - if (IsRailTT() && HasStationTileRail(m_new_tile)) { - m_is_station = true; + /* special handling for stations and multi-tile depots */ + if (IsRailTT()) { + if (HasStationTileRail(m_new_tile)) { + m_is_station = true; + } else if (IsExtendedRailDepotTile(m_new_tile)) { + m_is_extended_depot = true; + } } else if (IsRoadTT() && IsStationRoadStopTile(m_new_tile)) { m_is_station = true; } @@ -369,13 +374,14 @@ struct CFollowTrackT } } - /* special handling for rail stations - get to the end of platform */ - if (IsRailTT() && m_is_station) { + /* special handling for rail platforms - get to the end of platform */ + if (IsRailTT() && (m_is_station || m_is_extended_depot)) { /* Entered a platform. */ - assert(HasStationTileRail(m_new_tile)); + assert(HasStationTileRail(m_new_tile) || IsExtendedRailDepotTile(m_new_tile)); /* How big step we must do to get to the last platform tile? */ m_tiles_skipped = GetPlatformLength(m_new_tile, TrackdirToExitdir(m_old_td)) - 1; /* Move to the platform end. */ + TileIndexDiff diff = TileOffsByDiagDir(m_exitdir); diff *= m_tiles_skipped; m_new_tile = TileAdd(m_new_tile, diff); @@ -390,14 +396,27 @@ struct CFollowTrackT { /* rail and road depots cause reversing */ if (!IsWaterTT() && IsDepotTypeTile(m_old_tile, TT())) { - DiagDirection exitdir = IsRailTT() ? GetRailDepotDirection(m_old_tile) : GetRoadDepotDirection(m_old_tile); + DiagDirection exitdir; + switch (TT()) { + case TRANSPORT_AIR: + return false; + case TRANSPORT_RAIL: + if (IsExtendedRailDepot(m_old_tile)) return false; + exitdir = GetRailDepotDirection(m_old_tile); + break; + case TRANSPORT_ROAD: + exitdir = GetRoadDepotDirection(m_old_tile); + break; + default: NOT_REACHED(); + } + if (exitdir != m_exitdir) { /* reverse */ m_new_tile = m_old_tile; m_new_td_bits = TrackdirToTrackdirBits(ReverseTrackdir(m_old_td)); m_exitdir = exitdir; m_tiles_skipped = 0; - m_is_tunnel = m_is_bridge = m_is_station = false; + m_is_tunnel = m_is_bridge = m_is_station = m_is_extended_depot = false; return true; } } diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 2a5bc290d6c1c..af7cf5de2418a 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -439,7 +439,7 @@ class CYapfCostRailT : public CYapfCostBase { /* Waypoint is also a good reason to finish. */ end_segment_reason |= ESRB_WAYPOINT; - } else if (tf->m_is_station) { + } else if (tf->m_is_station || tf->m_is_extended_depot) { /* Station penalties. */ uint platform_length = tf->m_tiles_skipped + 1; /* We don't know yet if the station is our target or not. Act like @@ -592,7 +592,7 @@ class CYapfCostRailT : public CYapfCostBase { /* Platform-length penalty. */ if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) { - assert(HasStationTileRail(n.GetLastTile())); + assert(HasStationTileRail(n.GetLastTile()) || IsExtendedRailDepotTile(n.GetLastTile())); uint platform_length = GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir()))); /* Reduce the extra cost caused by passing-platform penalty (each platform receives it in the segment cost). */ extra_cost -= Yapf().PfGetSettings().rail_station_penalty * platform_length; diff --git a/src/pbs.cpp b/src/pbs.cpp index 6cc6a33aa289d..27a366ff919e0 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -12,6 +12,8 @@ #include "vehicle_func.h" #include "newgrf_station.h" #include "pathfinder/follow_track.hpp" +#include "platform_func.h" +#include "depot_map.h" #include "safeguards.h" @@ -180,12 +182,12 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra /* No reservation --> path end found */ if (reserved == TRACKDIR_BIT_NONE) { - if (ft.m_is_station) { + if (ft.m_is_station || ft.m_is_extended_depot) { /* Check skipped station tiles as well, maybe our reservation ends inside the station. */ TileIndexDiff diff = TileOffsByDiagDir(ft.m_exitdir); while (ft.m_tiles_skipped-- > 0) { ft.m_new_tile -= diff; - if (HasStationReservation(ft.m_new_tile)) { + if ((ft.m_is_station && HasStationReservation(ft.m_new_tile)) || (ft.m_is_extended_depot && HasDepotReservation(ft.m_new_tile))) { tile = ft.m_new_tile; trackdir = DiagDirToDiagTrackdir(ft.m_exitdir); break; @@ -278,14 +280,14 @@ PBSTileInfo FollowTrainReservation(const Train *v, Vehicle **train_on_res) if (train_on_res != nullptr) { FindVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum); if (ftoti.best != nullptr) *train_on_res = ftoti.best->First(); - if (*train_on_res == nullptr && IsRailStationTile(ftoti.res.tile)) { - /* The target tile is a rail station. The track follower + if (*train_on_res == nullptr && (IsRailStationTile(ftoti.res.tile) || IsExtendedRailDepotTile(ftoti.res.tile))) { + /* The target tile is a rail station or extended depot. The track follower * has stopped on the last platform tile where we haven't * found a train. Also check all previous platform tiles * for a possible train. */ TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(ftoti.res.trackdir))); - for (TileIndex st_tile = ftoti.res.tile + diff; *train_on_res == nullptr && IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) { - FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum); + for (TileIndex pt_tile = ftoti.res.tile + diff; *train_on_res == nullptr && IsCompatiblePlatformTile(pt_tile, ftoti.res.tile); pt_tile += diff) { + FindVehicleOnPos(pt_tile, &ftoti, FindTrainOnTrackEnum); if (ftoti.best != nullptr) *train_on_res = ftoti.best->First(); } } @@ -326,11 +328,11 @@ Train *GetTrainForReservation(TileIndex tile, Track track) FindVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum); if (ftoti.best != nullptr) return ftoti.best; - /* Special case for stations: check the whole platform for a vehicle. */ - if (IsRailStationTile(ftoti.res.tile)) { + /* Special case for stations and extended depots: check the whole platform for a vehicle. */ + if (IsRailStationTile(ftoti.res.tile) || IsExtendedRailDepotTile(ftoti.res.tile)) { TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(ftoti.res.trackdir))); - for (TileIndex st_tile = ftoti.res.tile + diff; IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) { - FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum); + for (TileIndex pt_tile = ftoti.res.tile + diff; IsCompatiblePlatformTile(pt_tile, ftoti.res.tile); pt_tile += diff) { + FindVehicleOnPos(pt_tile, &ftoti, FindTrainOnTrackEnum); if (ftoti.best != nullptr) return ftoti.best; } } diff --git a/src/platform.cpp b/src/platform.cpp index 103d5a29f2866..6878ad4344f45 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -11,6 +11,9 @@ #include "station_map.h" #include "platform_func.h" #include "viewport_func.h" +#include "depot_base.h" +#include "vehicle_base.h" +#include "engine_base.h" /** * Set the reservation for a complete station platform. @@ -34,6 +37,29 @@ void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool } while (IsCompatibleTrainStationTile(tile, start)); } + +/** + * Set the reservation for a complete depot platform. + * @pre IsExtendedRailDepotTile(start) + * @param start starting tile of the platform + * @param dir the direction in which to follow the platform + * @param b the state the reservation should be set to + */ +void SetRailDepotPlatformReservation(TileIndex start, DiagDirection dir, bool b) +{ + TileIndex tile = start; + TileIndexDiff diff = TileOffsByDiagDir(dir); + + assert(IsExtendedRailDepotTile(start)); + assert(GetRailDepotTrack(start) == DiagDirToDiagTrack(dir)); + + do { + SetDepotReservation(tile, b); + MarkTileDirtyByTile(tile); + tile = TileAdd(tile, diff); + } while (IsCompatibleTrainDepotTile(tile, start)); +} + /** * Set the reservation for a complete platform in a given direction. * @param start starting tile of the platform @@ -49,6 +75,32 @@ void SetPlatformReservation(TileIndex start, DiagDirection dir, bool b) case PT_RAIL_WAYPOINT: SetRailStationReservation(start, b); return; + case PT_RAIL_DEPOT: + SetRailDepotPlatformReservation(start, dir, b); + return; + default: NOT_REACHED(); + } +} + +/** + * Set the reservation for a complete platform. + * @param start A tile of the platform + * @param b the state the reservation should be set to + */ +void SetPlatformReservation(TileIndex start, bool b) +{ + DiagDirection dir; + switch (GetPlatformType(start)) { + case PT_RAIL_STATION: + NOT_REACHED(); + case PT_RAIL_WAYPOINT: + NOT_REACHED(); + case PT_RAIL_DEPOT: + assert(IsExtendedRailDepotTile(start)); + dir = GetRailDepotDirection(start); + SetRailDepotPlatformReservation(start, dir, b); + SetRailDepotPlatformReservation(start, ReverseDiagDir(dir), b); + return; default: NOT_REACHED(); } } @@ -103,6 +155,57 @@ uint GetRailStationPlatformLength(TileIndex tile, DiagDirection dir) return length; } +/** + * Get the length of a rail depot platform. + * @pre IsDepotTypeTile(tile, TRANSPORT_RAIL) + * @param tile Tile to check + * @return The length of the platform in tile length. + */ +uint GetRailDepotPlatformLength(TileIndex tile) +{ + assert(IsExtendedRailDepotTile(tile)); + + TileIndexDiff delta = (GetRailDepotTrack(tile) == TRACK_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + + TileIndex t = tile; + uint len = 0; + do { + t -= delta; + len++; + } while (IsCompatibleTrainDepotTile(t, tile)); + + t = tile; + do { + t += delta; + len++; + } while (IsCompatibleTrainDepotTile(t, tile)); + + return len - 1; +} + + +/** + * Get the length of a rail depot platform in a given direction. + * @pre IsRailDepotTile(tile) + * @param tile Tile to check + * @param dir Direction to check + * @return The length of the platform in tile length in the given direction. + */ +uint GetRailDepotPlatformLength(TileIndex tile, DiagDirection dir) +{ + TileIndex start_tile = tile; + uint length = 0; + assert(IsExtendedRailDepotTile(tile)); + assert(dir < DIAGDIR_END); + + do { + length++; + tile += TileOffsByDiagDir(dir); + } while (IsCompatibleTrainDepotTile(tile, start_tile)); + + return length; +} + /** * Get the length of a platform. * @param tile Tile to check @@ -115,6 +218,8 @@ uint GetPlatformLength(TileIndex tile) return GetRailStationPlatformLength(tile); case PT_RAIL_WAYPOINT: return 1; + case PT_RAIL_DEPOT: + return GetRailDepotPlatformLength(tile); default: NOT_REACHED(); } } @@ -133,6 +238,106 @@ uint GetPlatformLength(TileIndex tile, DiagDirection dir) return GetRailStationPlatformLength(tile, dir); case PT_RAIL_WAYPOINT: return 1; + case PT_RAIL_DEPOT: + return GetRailDepotPlatformLength(tile, dir); default: NOT_REACHED(); } } + +/** + * Get a tile where a rail station platform begins or ends. + * @pre IsRailStationTile(tile) + * @param tile Tile to check + * @param dir The diagonal direction to check + * @return The last tile of the platform seen from tile with direction dir. + */ +TileIndex GetRailStationExtreme(TileIndex tile, DiagDirection dir) +{ + assert(IsRailStationTile(tile)); + assert(GetRailStationAxis(tile) == DiagDirToAxis(dir)); + TileIndexDiff delta = TileOffsByDiagDir(dir); + + TileIndex t = tile; + do { + t -= delta; + } while (IsCompatibleTrainStationTile(t, tile)); + + return t + delta; +} + +/** + * Get a tile where a depot platform begins or ends. + * @pre IsExtendedDepotTile(tile) + * @param tile Tile to check + * @param dir The diagonal direction to check + * @return The last tile of the platform seen from tile with direction dir. + */ +TileIndex GetRailDepotExtreme(TileIndex tile, DiagDirection dir) +{ + assert(IsExtendedDepotTile(tile)); + assert(GetRailDepotTrack(tile) == DiagDirToDiagTrack(dir)); + TileIndexDiff delta = TileOffsByDiagDir(dir); + + TileIndex t = tile; + do { + t -= delta; + } while (IsCompatibleTrainDepotTile(t, tile)); + + return t + delta; +} + +/** + * Get a tile where a platform begins or ends. + * @param tile Tile to check + * @param dir Direction to check + * @return The last tile of the platform seen from tile with direction dir. + */ +TileIndex GetPlatformExtremeTile(TileIndex tile, DiagDirection dir) +{ + switch (GetPlatformType(tile)) { + case PT_RAIL_STATION: + return GetRailStationExtreme(tile, dir); + case PT_RAIL_WAYPOINT: + return tile; + case PT_RAIL_DEPOT: + return GetRailDepotExtreme(tile, dir); + default: NOT_REACHED(); + } +} + +/** + * Get the tiles belonging to a platform. + * @param tile Tile of a platform + * @return the tile area of the platform + */ +TileArea GetPlatformTileArea(TileIndex tile) +{ + switch (GetPlatformType(tile)) { + case PT_RAIL_STATION: { + assert(IsRailStationTile(tile)); + DiagDirection dir = AxisToDiagDir(GetRailStationAxis(tile)); + return TileArea(GetRailStationExtreme(tile, dir), GetRailStationExtreme(tile, ReverseDiagDir(dir))); + } + case PT_RAIL_WAYPOINT: + return TileArea(tile); + case PT_RAIL_DEPOT: { + assert(IsExtendedRailDepotTile(tile)); + DiagDirection dir = GetRailDepotDirection(tile); + return TileArea(GetRailDepotExtreme(tile, dir), GetRailDepotExtreme(tile, ReverseDiagDir(dir))); + } + default: NOT_REACHED(); + } +} + + +/** + * Check whether this tile is an extreme of a platform. + * @param tile Tile to check + * @return Whether the tile is the extreme of a platform. + */ +bool IsAnyStartPlatformTile(TileIndex tile) +{ + assert(IsExtendedRailDepotTile(tile)); + DiagDirection dir = GetRailDepotDirection(tile); + return tile == GetPlatformExtremeTile(tile, dir) || tile == GetPlatformExtremeTile(tile, ReverseDiagDir(dir)); +} diff --git a/src/platform_func.h b/src/platform_func.h index 5b1275d3ef148..30a2dae28033d 100644 --- a/src/platform_func.h +++ b/src/platform_func.h @@ -11,6 +11,7 @@ #define PLATFORM_FUNC_H #include "station_map.h" +#include "depot_map.h" #include "platform_type.h" /** @@ -35,6 +36,27 @@ static inline bool IsCompatibleTrainStationTile(TileIndex test_tile, TileIndex s GetStationIndex(test_tile) == GetStationIndex(station_tile); } +/** + * Check if a tile is a valid continuation to an extended rail depot tile. + * The tile \a test_tile is a valid continuation to \a depot_tile, if all of the following are true: + * \li \a test_tile is an extended depot tile + * \li \a test_tile and \a depot_tile have the same rail type + * \li the tracks on \a test_tile and \a depot_tile are in the same direction + * \li both tiles belong to the same depot + * @param test_tile Tile to test + * @param depot_tile Depot tile to compare with + * @pre IsExtendedRailDepotTile(depot_tile) + * @return true if the two tiles are compatible + */ +static inline bool IsCompatibleTrainDepotTile(TileIndex test_tile, TileIndex depot_tile) +{ + assert(IsExtendedRailDepotTile(depot_tile)); + return IsExtendedRailDepotTile(test_tile) && + GetRailType(test_tile) == GetRailType(depot_tile) && + GetRailDepotTrack(test_tile) == GetRailDepotTrack(depot_tile) && + GetDepotIndex(test_tile) == GetDepotIndex(depot_tile); +} + /** * Returns the type of platform of a given tile. * @param tile Tile to check @@ -47,6 +69,9 @@ static inline PlatformType GetPlatformType(TileIndex tile) if (IsRailStation(tile)) return PT_RAIL_STATION; if (IsRailWaypoint(tile)) return PT_RAIL_WAYPOINT; break; + case MP_RAILWAY: + if (IsExtendedRailDepotTile(tile)) return PT_RAIL_DEPOT; + break; default: break; } @@ -74,6 +99,8 @@ static inline bool HasPlatformReservation(TileIndex tile) case PT_RAIL_STATION: case PT_RAIL_WAYPOINT: return HasStationReservation(tile); + case PT_RAIL_DEPOT: + return HasDepotReservation(tile); default: NOT_REACHED(); } } @@ -92,13 +119,21 @@ static inline bool IsCompatiblePlatformTile(TileIndex test_tile, TileIndex orig_ return IsCompatibleTrainStationTile(test_tile, orig_tile); case PT_RAIL_WAYPOINT: return test_tile == orig_tile; + case PT_RAIL_DEPOT: + return IsCompatibleTrainDepotTile(test_tile, orig_tile); default: NOT_REACHED(); } } void SetPlatformReservation(TileIndex start, DiagDirection dir, bool b); +void SetPlatformReservation(TileIndex start, bool b); uint GetPlatformLength(TileIndex tile); uint GetPlatformLength(TileIndex tile, DiagDirection dir); +TileIndex GetPlatformExtremeTile(TileIndex tile, DiagDirection dir); +TileArea GetPlatformTileArea(TileIndex tile); + +bool IsAnyStartPlatformTile(TileIndex tile); + #endif /* PLATFORM_FUNC_H */ diff --git a/src/platform_type.h b/src/platform_type.h index a2a48c90b3e5d..27765d92c78d1 100644 --- a/src/platform_type.h +++ b/src/platform_type.h @@ -15,6 +15,7 @@ enum PlatformType { PT_RAIL_STATION, PT_RAIL_WAYPOINT, + PT_RAIL_DEPOT, PT_END, INVALID_PLATFORM_TYPE = PT_END, }; diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 47e276a74249e..a8ab539c2b949 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -41,6 +41,7 @@ #include "timer/timer_game_economy.h" #include "depot_base.h" #include "platform_func.h" +#include "depot_map.h" #include "table/strings.h" #include "table/train_sprites.h" @@ -2420,7 +2421,7 @@ static bool CheckTrainStayInDepot(Train *v) { /* bail out if not all wagons are in the same depot or not in a depot at all */ for (const Train *u = v; u != nullptr; u = u->Next()) { - if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false; + if (!u->IsInDepot() || u->tile != v->tile) return false; } /* if the train got no power, then keep it in the depot */ @@ -2524,7 +2525,7 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_ } } } - } else if (IsRailStationTile(tile)) { + } else if (IsRailStationTile(tile) || IsExtendedRailDepotTile(tile)) { TileIndex new_tile = TileAddByDiagDir(tile, dir); /* If the new tile is not a further tile of the same station, we * clear the reservation for the whole platform. */ @@ -2547,8 +2548,9 @@ void FreeTrainTrackReservation(const Train *v) TileIndex tile = v->tile; Trackdir td = v->GetVehicleTrackdir(); - bool free_tile = !(IsRailStationTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)); + bool free_tile = !(IsRailStationTile(v->tile) || IsExtendedRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)); StationID station_id = IsRailStationTile(v->tile) ? GetStationIndex(v->tile) : INVALID_STATION; + DepotID depot_id = IsExtendedRailDepotTile(v->tile) ? GetDepotIndex(v->tile) : INVALID_DEPOT; /* Can't be holding a reservation if we enter a depot. */ if (IsStandardRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return; @@ -2591,7 +2593,7 @@ void FreeTrainTrackReservation(const Train *v) } /* Don't free first station/bridge/tunnel if we are on it. */ - if (free_tile || (!(ft.m_is_station && GetStationIndex(ft.m_new_tile) == station_id) && !ft.m_is_tunnel && !ft.m_is_bridge)) ClearPathReservation(v, tile, td); + if (free_tile || (!(ft.m_is_station && GetStationIndex(ft.m_new_tile) == station_id) && !(ft.m_is_extended_depot && GetDepotIndex(ft.m_new_tile) == depot_id) && !ft.m_is_tunnel && !ft.m_is_bridge)) ClearPathReservation(v, tile, td); free_tile = true; } @@ -2650,7 +2652,7 @@ static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, } /* Station, depot or waypoint are a possible target. */ - bool target_seen = ft.m_is_station || (IsTileType(ft.m_new_tile, MP_RAILWAY) && !IsPlainRail(ft.m_new_tile)); + bool target_seen = ft.m_is_station || ft.m_is_extended_depot || (IsTileType(ft.m_new_tile, MP_RAILWAY) && !IsPlainRail(ft.m_new_tile)); if (target_seen || KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) { /* Choice found or possible target encountered. * On finding a possible target, we need to stop and let the pathfinder handle the @@ -4359,9 +4361,13 @@ Trackdir Train::GetVehicleTrackdir() const { if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR; - if (this->track == TRACK_BIT_DEPOT) { + if (this->IsInDepot()) { /* We'll assume the train is facing outwards */ - return DiagDirToDiagTrackdir(GetRailDepotDirection(this->tile)); // Train in depot + if (this->track == TRACK_BIT_DEPOT) + return DiagDirToDiagTrackdir(DirToDiagDir(this->direction)); // Train in depot + Track track = FindFirstTrack(this->track & ~TRACK_BIT_DEPOT); + assert(IsValidTrack(track)); + return TrackDirectionToTrackdir(track, this->direction); } if (this->track == TRACK_BIT_WORMHOLE) { From a5986b3aa1b91dcaa63af8c27af42a5104d118cd Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 2 Oct 2021 18:37:11 +0200 Subject: [PATCH 051/143] Change: Rename end segment reason for general platforms. --- src/pathfinder/yapf/yapf_costrail.hpp | 4 ++-- src/pathfinder/yapf/yapf_type.hpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index af7cf5de2418a..076c26389c112 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -446,7 +446,7 @@ class CYapfCostRailT : public CYapfCostBase { * if it is pass-through station (not our destination). */ segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length; /* We will end in this pass (station is possible target) */ - end_segment_reason |= ESRB_STATION; + end_segment_reason |= ESRB_PLATFORM; } else if (TrackFollower::DoTrackMasking() && cur.tile_type == MP_RAILWAY) { /* Searching for a safe tile? */ @@ -591,7 +591,7 @@ class CYapfCostRailT : public CYapfCostBase { } /* Platform-length penalty. */ - if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) { + if ((end_segment_reason & ESRB_PLATFORM) != ESRB_NONE) { assert(HasStationTileRail(n.GetLastTile()) || IsExtendedRailDepotTile(n.GetLastTile())); uint platform_length = GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir()))); /* Reduce the extra cost caused by passing-platform penalty (each platform receives it in the segment cost). */ diff --git a/src/pathfinder/yapf/yapf_type.hpp b/src/pathfinder/yapf/yapf_type.hpp index 4f301b0fb719b..511f89be83460 100644 --- a/src/pathfinder/yapf/yapf_type.hpp +++ b/src/pathfinder/yapf/yapf_type.hpp @@ -23,7 +23,7 @@ enum EndSegmentReason { ESR_CHOICE_FOLLOWS, ///< the next tile contains a choice (the track splits to more than one segments) ESR_DEPOT, ///< stop in the depot (could be a target next time) ESR_WAYPOINT, ///< waypoint encountered (could be a target next time) - ESR_STATION, ///< station encountered (could be a target next time) + ESR_PLATFORM, ///< platform (station/extended depot) encountered (could be a target next time) ESR_SAFE_TILE, ///< safe waiting position found (could be a target) /* The following reasons are used only internally by PfCalcCost(). @@ -47,7 +47,7 @@ enum EndSegmentReasonBits { ESRB_CHOICE_FOLLOWS = 1 << ESR_CHOICE_FOLLOWS, ESRB_DEPOT = 1 << ESR_DEPOT, ESRB_WAYPOINT = 1 << ESR_WAYPOINT, - ESRB_STATION = 1 << ESR_STATION, + ESRB_PLATFORM = 1 << ESR_PLATFORM, ESRB_SAFE_TILE = 1 << ESR_SAFE_TILE, ESRB_PATH_TOO_LONG = 1 << ESR_PATH_TOO_LONG, @@ -58,10 +58,10 @@ enum EndSegmentReasonBits { /* Additional (composite) values. */ /* What reasons mean that the target can be found and needs to be detected. */ - ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE, + ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_PLATFORM | ESRB_SAFE_TILE, /* What reasons can be stored back into cached segment. */ - ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE, + ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_PLATFORM | ESRB_SAFE_TILE, /* Reasons to abort pathfinding in this direction. */ ESRB_ABORT_PF_MASK = ESRB_DEAD_END | ESRB_PATH_TOO_LONG | ESRB_INFINITE_LOOP | ESRB_FIRST_TWO_WAY_RED, From 3ad7a57fef76efd1dd60955b37a5a511b59baa1f Mon Sep 17 00:00:00 2001 From: Juanjo Date: Fri, 8 Aug 2014 19:20:28 +0200 Subject: [PATCH 052/143] Add: Add IsAnyDepotTileReserved. --- src/pathfinder/yapf/yapf_costrail.hpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 076c26389c112..67fdd07ebb934 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -150,13 +150,24 @@ class CYapfCostRailT : public CYapfCostBase { return false; } + /** Check for a reserved depot platform. */ + inline bool IsAnyDepotTileReserved(TileIndex tile, Trackdir trackdir, int skipped) + { + TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(trackdir))); + for (; skipped >= 0; skipped--, tile += diff) { + if (HasDepotReservation(tile)) return true; + } + return false; + } + /** The cost for reserved tiles, including skipped ones. */ inline int ReservationCost(Node &n, TileIndex tile, Trackdir trackdir, int skipped) { if (n.m_num_signals_passed >= m_sig_look_ahead_costs.size() / 2) return 0; if (!IsPbsSignal(n.m_last_signal_type)) return 0; - if (IsRailStationTile(tile) && IsAnyStationTileReserved(tile, trackdir, skipped)) { + if ((IsRailStationTile(tile) && IsAnyStationTileReserved(tile, trackdir, skipped)) || + (IsExtendedRailDepotTile(tile) && IsAnyDepotTileReserved(tile, trackdir, skipped))) { return Yapf().PfGetSettings().rail_pbs_station_penalty * (skipped + 1); } else if (TrackOverlapsTracks(GetReservedTrackbits(tile), TrackdirToTrack(trackdir))) { int cost = Yapf().PfGetSettings().rail_pbs_cross_penalty; From 8312018faf7add9400990adfc4fd9ed724e62f04 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 31 Dec 2020 12:30:17 +0100 Subject: [PATCH 053/143] Add: Reservation and penalties for extended depots. --- src/pathfinder/yapf/yapf_rail.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp index 8d7e55267e643..d88e88110fb14 100644 --- a/src/pathfinder/yapf/yapf_rail.cpp +++ b/src/pathfinder/yapf/yapf_rail.cpp @@ -86,6 +86,23 @@ class CYapfReserveTrack return true; } + /** Reserve a railway platform. Tile contains the failed tile on abort. */ + bool ReserveRailDepotPlatform(TileIndex &tile, DiagDirection dir) + { + assert(IsExtendedRailDepotTile(tile)); + TileIndex start = tile; + TileIndexDiff diff = TileOffsByDiagDir(dir); + + do { + if (HasDepotReservation(tile)) return false; + SetDepotReservation(tile, true); + MarkTileDirtyByTile(tile); + tile = TileAdd(tile, diff); + } while (IsCompatibleTrainDepotTile(tile, start) && tile != m_origin_tile); + + return true; + } + /** Try to reserve a single track/platform. */ bool ReserveSingleTrack(TileIndex tile, Trackdir td) { @@ -95,6 +112,12 @@ class CYapfReserveTrack m_res_fail_tile = tile; m_res_fail_td = td; } + } else if (IsExtendedRailDepotTile(tile)) { + if (!ReserveRailDepotPlatform(tile, TrackdirToExitdir(ReverseTrackdir(td)))) { + /* Platform could not be reserved, undo. */ + m_res_fail_tile = tile; + m_res_fail_td = td; + } } else { if (!TryReserveRailTrack(tile, TrackdirToTrack(td))) { /* Tile couldn't be reserved, undo. */ @@ -117,6 +140,13 @@ class CYapfReserveTrack SetRailStationReservation(tile, false); tile = TileAdd(tile, diff); } + } else if (IsExtendedRailDepotTile(tile)) { + TileIndex start = tile; + TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(td))); + while ((tile != m_res_fail_tile || td != m_res_fail_td) && IsCompatibleTrainDepotTile(tile, start)) { + SetDepotReservation(tile, false); + tile = TileAdd(tile, diff); + } } else if (tile != m_res_fail_tile || td != m_res_fail_td) { UnreserveRailTrack(tile, TrackdirToTrack(td)); } From d94b72a47e5e2840bd5158b415a9f5284466ec11 Mon Sep 17 00:00:00 2001 From: Juanjo Date: Sun, 12 Jan 2014 12:22:43 +0100 Subject: [PATCH 054/143] Add: Add vehicle entered depot platform flag. --- src/tile_cmd.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/tile_cmd.h b/src/tile_cmd.h index 88f36fef86643..486dca11e3d93 100644 --- a/src/tile_cmd.h +++ b/src/tile_cmd.h @@ -19,9 +19,10 @@ /** The returned bits of VehicleEnterTile. */ enum VehicleEnterTileStatus { - VETS_ENTERED_STATION = 1, ///< The vehicle entered a station - VETS_ENTERED_WORMHOLE = 2, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel) - VETS_CANNOT_ENTER = 3, ///< The vehicle cannot enter the tile + VETS_ENTERED_STATION = 1, ///< The vehicle entered a station. + VETS_ENTERED_WORMHOLE = 2, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel). + VETS_CANNOT_ENTER = 3, ///< The vehicle cannot enter the tile. + VETS_ENTERED_DEPOT_PLATFORM = 4, ///< The vehicle entered a depot platform. /** * Shift the VehicleEnterTileStatus this many bits @@ -32,10 +33,11 @@ enum VehicleEnterTileStatus { VETS_STATION_MASK = 0xFFFF << VETS_STATION_ID_OFFSET, /** Bit sets of the above specified bits */ - VETSB_CONTINUE = 0, ///< The vehicle can continue normally - VETSB_ENTERED_STATION = 1 << VETS_ENTERED_STATION, ///< The vehicle entered a station - VETSB_ENTERED_WORMHOLE = 1 << VETS_ENTERED_WORMHOLE, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel) - VETSB_CANNOT_ENTER = 1 << VETS_CANNOT_ENTER, ///< The vehicle cannot enter the tile + VETSB_CONTINUE = 0, ///< The vehicle can continue normally. + VETSB_ENTERED_STATION = 1 << VETS_ENTERED_STATION, ///< The vehicle entered a station. + VETSB_ENTERED_WORMHOLE = 1 << VETS_ENTERED_WORMHOLE, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel). + VETSB_CANNOT_ENTER = 1 << VETS_CANNOT_ENTER, ///< The vehicle cannot enter the tile. + VETSB_ENTERED_DEPOT_PLATFORM = 1 << VETS_ENTERED_DEPOT_PLATFORM, ///< The vehicle entered a depot platform. }; DECLARE_ENUM_AS_BIT_SET(VehicleEnterTileStatus) From 189f4c682442ecd1ff5a06784b5309586297f61c Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 24 Dec 2020 01:31:16 +0100 Subject: [PATCH 055/143] Add: Use track depot for detecting trains in depots. --- src/train.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/train.h b/src/train.h index be4307307cd3c..677ead8d1a312 100644 --- a/src/train.h +++ b/src/train.h @@ -120,7 +120,7 @@ struct Train final : public GroundVehicle { Money GetRunningCost() const override; int GetCursorImageOffset() const; int GetDisplayImageWidth(Point *offset = nullptr) const; - bool IsInDepot() const override { return this->track == TRACK_BIT_DEPOT; } + bool IsInDepot() const override { return HasBit((uint8_t)this->track, TRACK_DEPOT); } bool Tick() override; void OnNewCalendarDay() override; void OnNewEconomyDay() override; From 7f61e785571fef05c35a0bb4fe0bfb43320e1a0b Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 24 Dec 2020 01:32:26 +0100 Subject: [PATCH 056/143] Change: Modify IsChainInDepot for ground vehicles. --- src/ground_vehicle.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ground_vehicle.cpp b/src/ground_vehicle.cpp index a6ab9758c0cbd..5981d22b7715a 100644 --- a/src/ground_vehicle.cpp +++ b/src/ground_vehicle.cpp @@ -197,7 +197,10 @@ bool GroundVehicle::IsChainInDepot() const /* Check whether the rest is also already trying to enter the depot. */ for (; v != nullptr; v = v->Next()) { - if (!v->T::IsInDepot() || v->tile != this->tile) return false; + if (!v->T::IsInDepot()) return false; + assert(IsDepotTile(v->tile)); + assert(IsDepotTile(this->tile)); + assert(GetDepotIndex(this->tile) == GetDepotIndex(v->tile)); } return true; From 39cbcc297ae58e5a6b7890a76ffd7b1ba1f8a3f1 Mon Sep 17 00:00:00 2001 From: Juanjo Date: Sun, 12 Jan 2014 12:15:54 +0100 Subject: [PATCH 057/143] Add: Add a ShouldStopAtDepot function. --- src/order_base.h | 1 + src/order_cmd.cpp | 11 +++++++++++ src/train_cmd.cpp | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/order_base.h b/src/order_base.h index 8f2f10c252aa9..b223440c591fe 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -225,6 +225,7 @@ struct Order : OrderPool::PoolItem<&_order_pool> { inline void SetMaxSpeed(uint16_t speed) { this->max_speed = speed; } bool ShouldStopAtStation(const Vehicle *v, StationID station) const; + bool ShouldStopAtDepot(DepotID depot) const; bool CanLoadOrUnload() const; bool CanLeaveWithCargo(bool has_cargo) const; diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 63a5869acb08d..c9000c18bc339 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -2215,6 +2215,17 @@ bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const !(this->GetNonStopType() & (is_dest_station ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS)); } +/** + * Check whether the given vehicle should stop at the given depot. + * @param v the vehicle that might be stopping. + * @param depot the depot to stop at. + * @return true if the vehicle should stop. + */ +bool Order::ShouldStopAtDepot(DepotID depot) const +{ + return this->IsType(OT_GOTO_DEPOT) && this->dest == depot; +} + bool Order::CanLoadOrUnload() const { return (this->IsType(OT_GOTO_STATION) || this->IsType(OT_IMPLICIT)) && diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index a8ab539c2b949..d8fec2ad80344 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2366,7 +2366,7 @@ static void CheckNextTrainTile(Train *v) switch (v->current_order.GetType()) { /* Exit if we reached our destination depot. */ case OT_GOTO_DEPOT: - if (v->tile == v->dest_tile) return; + if (IsRailDepotTile(v->tile) && v->current_order.ShouldStopAtDepot(GetDepotIndex(v->tile))) return; break; case OT_GOTO_WAYPOINT: From ec8e5c38e9f947433aacbb0b80d6c8d384334d2d Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 22 Apr 2023 21:18:59 +0200 Subject: [PATCH 058/143] Change: Adapt VehicleEnter_Track and handle trains entering extended depots. # Conflicts: # src/vehicle.cpp --- src/rail_cmd.cpp | 37 +++++++++++++++++++++++--- src/train.h | 1 + src/train_cmd.cpp | 68 ++++++++++++++++++++++++++++++++++++++++------- src/vehicle.cpp | 3 +-- 4 files changed, 94 insertions(+), 15 deletions(-) diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 2868d174b539b..a759ee8236813 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -33,6 +33,7 @@ #include "object_map.h" #include "rail_cmd.h" #include "landscape_cmd.h" +#include "platform_func.h" #include "table/strings.h" #include "table/railtypes.h" @@ -3076,6 +3077,38 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int /* This routine applies only to trains in depot tiles. */ if (u->type != VEH_TRAIN || !IsRailDepotTile(tile)) return VETSB_CONTINUE; + Train *v = Train::From(u); + + if (IsExtendedRailDepot(tile)) { + DepotID depot_id = GetDepotIndex(tile); + if (!v->current_order.ShouldStopAtDepot(depot_id)) return VETSB_CONTINUE; + + /* Stop position on platform is half the front vehicle length of the train. */ + int stop_pos = v->gcache.cached_veh_length / 2; + + int depot_ahead = (GetPlatformLength(tile, DirToDiagDir(v->direction)) - 1) * TILE_SIZE; + if (depot_ahead > stop_pos) return VETSB_CONTINUE; + + DiagDirection dir = DirToDiagDir(v->direction); + + x &= 0xF; + y &= 0xF; + + if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y); + if (y == TILE_SIZE / 2) { + if (dir == DIAGDIR_SE || dir == DIAGDIR_SW) x = TILE_SIZE - 1 - x; + + if (stop_pos == x) { + return VETSB_ENTERED_DEPOT_PLATFORM; + } else if (stop_pos < x) { + v->vehstatus |= VS_TRAIN_SLOWING; + uint16_t spd = std::max(0, stop_pos * 20 - 15); + if (spd < v->cur_speed) v->cur_speed = spd; + } + } + return VETSB_CONTINUE; + } + /* Depot direction. */ DiagDirection dir = GetRailDepotDirection(tile); @@ -3084,8 +3117,6 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int /* Make sure a train is not entering the tile from behind. */ if (_fractcoords_behind[dir] == fract_coord) return VETSB_CANNOT_ENTER; - Train *v = Train::From(u); - /* Leaving depot? */ if (v->direction == DiagDirToDir(dir)) { /* Calculate the point where the following wagon should be activated. */ @@ -3110,7 +3141,7 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int v->track = TRACK_BIT_DEPOT, v->vehstatus |= VS_HIDDEN; v->direction = ReverseDir(v->direction); - if (v->Next() == nullptr) VehicleEnterDepot(v->First()); + if (v->Next() == nullptr) HandleTrainEnterDepot(v->First()); v->tile = tile; InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); diff --git a/src/train.h b/src/train.h index 677ead8d1a312..65fd1503c1039 100644 --- a/src/train.h +++ b/src/train.h @@ -354,5 +354,6 @@ struct Train final : public GroundVehicle { }; bool HasCompatibleDepotTile(TileIndex tile, const Train *t); +bool HandleTrainEnterDepot(Train *v); #endif /* TRAIN_H */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index d8fec2ad80344..e16fc3147500d 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2412,6 +2412,31 @@ static void CheckNextTrainTile(Train *v) } } +bool HandleTrainEnterDepot(Train *v) +{ + assert(IsRailDepotTile(v->tile)); + + if (IsExtendedRailDepot(v->tile)) { + v->cur_speed = 0; + Train *t = Train::From(v); + for (Train *u = t; u != nullptr; u = u->Next()) u->track |= TRACK_BIT_DEPOT; + t->force_proceed = TFP_NONE; + ClrBit(t->flags, VRF_TOGGLE_REVERSE); + v->UpdateViewport(true, true); + SetWindowClassesDirty(WC_TRAINS_LIST); + SetWindowDirty(WC_VEHICLE_VIEW, v->index); + + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); + v->StartService(); + } else { + /* Clear path reservation */ + SetDepotReservation(v->tile, false); + VehicleEnterDepot(v); + } + + return true; +} + /** * Will the train stay in the depot the next tick? * @param v %Train to check. @@ -2420,15 +2445,30 @@ static void CheckNextTrainTile(Train *v) static bool CheckTrainStayInDepot(Train *v) { /* bail out if not all wagons are in the same depot or not in a depot at all */ - for (const Train *u = v; u != nullptr; u = u->Next()) { - if (!u->IsInDepot() || u->tile != v->tile) return false; - } + if (!v->IsInDepot()) return false; + assert(IsRailDepotTile(v->tile)); - /* if the train got no power, then keep it in the depot */ - if (v->gcache.cached_power == 0) { - v->vehstatus |= VS_STOPPED; - SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); - return true; + DepotID depot_id = GetDepotIndex(v->tile); + if (IsExtendedRailDepot(v->tile)) { + for (Train *u = v; u != nullptr; u = u->Next()) u->track &= ~TRACK_BIT_DEPOT; + v->cur_speed = 0; + + v->UpdatePosition(); + v->UpdateViewport(true, true); + v->UpdateAcceleration(); + InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); + return false; + } else { + for (const Train *u = v; u != nullptr; u = u->Next()) { + if (!u->IsInDepot() || u->tile != v->tile) return false; + } + + /* if the train got no power, then keep it in the depot */ + if (v->gcache.cached_power == 0) { + v->vehstatus |= VS_STOPPED; + SetWindowDirty(WC_VEHICLE_DEPOT, depot_id); + return true; + } } /* Check if we should wait here for unbunching. */ @@ -2460,7 +2500,7 @@ static bool CheckTrainStayInDepot(Train *v) IsRailDepotTile(v->tile) && v->current_order.GetDestination() == GetDepotIndex(v->tile)) { /* Service when depot has no reservation. */ - if (!HasDepotReservation(v->tile)) VehicleEnterDepot(v); + if (!HasDepotReservation(v->tile)) HandleTrainEnterDepot(v); return true; } @@ -2490,7 +2530,7 @@ static bool CheckTrainStayInDepot(Train *v) v->UpdatePosition(); UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner); v->UpdateAcceleration(); - InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); + InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); return false; } @@ -3443,6 +3483,12 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) if (HasBit(r, VETS_ENTERED_STATION)) { /* The new position is the end of the platform */ TrainEnterStation(v, r >> VETS_STATION_ID_OFFSET); + } else if (HasBit(r, VETS_ENTERED_DEPOT_PLATFORM)) { + if (HandleTrainEnterDepot(first)) { + v->UpdatePosition(); + v->UpdateViewport(true, true); + return false; + } } } } else { @@ -4099,6 +4145,8 @@ static bool TrainLocoHandler(Train *v, bool mode) /* exit if train is stopped */ if ((v->vehstatus & VS_STOPPED) && v->cur_speed == 0) return true; + if (v->ContinueServicing()) return true; + bool valid_order = !v->current_order.IsType(OT_NOTHING) && v->current_order.GetType() != OT_CONDITIONAL; if (ProcessOrders(v) && CheckReverseTrain(v)) { v->wait_counter = 0; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index a787f89b259ea..83518b4b911eb 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1201,6 +1201,7 @@ void CallVehicleTicks() if (it.second) { v->vehstatus &= ~VS_STOPPED; } else if (IsExtendedDepotTile(v->tile)){ + if (v->type == VEH_TRAIN) FreeTrainTrackReservation(Train::From(v)); UpdateExtendedDepotReservation(v, true); } @@ -1683,8 +1684,6 @@ void VehicleEnterDepot(Vehicle *v) case VEH_TRAIN: { Train *t = Train::From(v); SetWindowClassesDirty(WC_TRAINS_LIST); - /* Clear path reservation */ - SetDepotReservation(t->tile, false); if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile); UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner); From 72d63440b3d358ca25e5a475f6ee0338be529d33 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sun, 27 Dec 2020 22:29:23 +0100 Subject: [PATCH 059/143] Add: Check length of platforms for servicing trains. --- src/lang/english.txt | 1 + src/train_cmd.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/lang/english.txt b/src/lang/english.txt index 433629922125b..31a1c030ce701 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -911,6 +911,7 @@ STR_NEWS_VEHICLE_IS_LOST :{WHITE}{VEHICLE STR_NEWS_VEHICLE_UNPROFITABLE_YEAR :{WHITE}{VEHICLE}'s profit last year was {CURRENCY_LONG} STR_NEWS_VEHICLE_UNPROFITABLE_PERIOD :{WHITE}{VEHICLE}'s profit last period was {CURRENCY_LONG} STR_NEWS_VEHICLE_CAN_T_FIND_FREE_DEPOT :{WHITE}{VEHICLE} can't find a free depot +STR_NEWS_VEHICLE_TOO_LONG_FOR_SERVICING :{WHITE}{VEHICLE} couldn't service in short platform STR_NEWS_AIRCRAFT_DEST_TOO_FAR :{WHITE}{VEHICLE} can't get to the next destination because it is out of range STR_NEWS_ORDER_REFIT_FAILED :{WHITE}{VEHICLE} stopped because an ordered refit failed diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index e16fc3147500d..0c4c7670df8cc 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2419,6 +2419,14 @@ bool HandleTrainEnterDepot(Train *v) if (IsExtendedRailDepot(v->tile)) { v->cur_speed = 0; Train *t = Train::From(v); + for (Train *u = t; u != nullptr; u = u->Next()) { + if (!IsCompatibleTrainDepotTile(u->tile, t->tile)) { + SetDParam(0, v->index); + AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_TOO_LONG_FOR_SERVICING, v->index); + return false; + } + } + for (Train *u = t; u != nullptr; u = u->Next()) u->track |= TRACK_BIT_DEPOT; t->force_proceed = TFP_NONE; ClrBit(t->flags, VRF_TOGGLE_REVERSE); From e6deb8c5a0dd1528ad00e71a7f79800945bdb6c6 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 31 Dec 2020 12:11:14 +0100 Subject: [PATCH 060/143] Change: Update signals for extended rail depots. --- src/signal.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/signal.cpp b/src/signal.cpp index d060c58d2248b..e7ef7f7b5b5a0 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -278,6 +278,13 @@ static SigFlags ExploreSegment(Owner owner) if (GetTileOwner(tile) != owner) continue; // do not propagate signals on others' tiles (remove for tracksharing) if (IsRailDepot(tile)) { + if (IsExtendedRailDepot(tile)) { + assert(enterdir != INVALID_DIAGDIR); + if (DiagDirToDiagTrack(enterdir) != GetRailDepotTrack(tile)) continue; // different axis + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN; + tile += TileOffsByDiagDir(exitdir); + break; + } if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN; exitdir = GetRailDepotDirection(tile); @@ -496,8 +503,14 @@ static SigSegState UpdateSignalsInBuffer(Owner owner) case MP_RAILWAY: if (IsRailDepot(tile)) { /* 'optimization assert' do not try to update signals in other cases */ - assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile)); - _tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside + if (IsExtendedRailDepot(tile)) { + dir = GetRailDepotDirection(tile); + _tbdset.Add(tile, dir); + _tbdset.Add(tile + TileOffsByDiagDir(dir), ReverseDiagDir(dir)); + } else { + assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile)); + _tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside + } break; } [[fallthrough]]; From 89c0a884424f29df5586f7f9647a9dd9aaf783d3 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Thu, 24 Dec 2020 01:38:04 +0100 Subject: [PATCH 061/143] Change: Changes related with crashed trains in extended depots. --- src/train_cmd.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/vehicle.cpp | 18 ++++++++++++++++++ src/vehicle_func.h | 1 + 3 files changed, 58 insertions(+) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 0c4c7670df8cc..f5f011d204fcc 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -3271,6 +3271,7 @@ static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir) void Train::ReserveTrackUnderConsist() const { for (const Train *u = this; u != nullptr; u = u->Next()) { + if (u->vehstatus & VS_HIDDEN) continue; switch (u->track) { case TRACK_BIT_WORMHOLE: TryReserveRailTrack(u->tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(u->tile))); @@ -3299,6 +3300,23 @@ uint Train::Crash(bool flooded) /* Remove the reserved path in front of the train if it is not stuck. * Also clear all reserved tracks the train is currently on. */ if (!HasBit(this->flags, VRF_TRAIN_STUCK)) FreeTrainTrackReservation(this); + + if (IsExtendedRailDepotTile(this->tile)) { + if (this->track & ~TRACK_BIT_DEPOT) { + for (Train *v = this; v != nullptr; v = v->Next()) { + v->track &= ~TRACK_BIT_DEPOT; + } + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile)); + } + /* Remove reserved tracks of platform ahead. */ + TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(this->GetVehicleTrackdir())); + for (TileIndex pt_tile = this->tile + diff; IsCompatiblePlatformTile(pt_tile, this->tile) && HasDepotReservation(pt_tile); pt_tile += diff) { + if (EnsureNoVisibleVehicleOnGround(pt_tile).Failed()) break; + SetDepotReservation(pt_tile, false); + MarkTileDirtyByTile(pt_tile); + } + } + for (const Train *v = this; v != nullptr; v = v->Next()) { ClearPathReservation(v, v->tile, v->GetVehicleTrackdir()); if (IsTileType(v->tile, MP_TUNNELBRIDGE)) { @@ -3306,6 +3324,23 @@ uint Train::Crash(bool flooded) * if the train has just entered the wormhole. */ SetTunnelBridgeReservation(GetOtherTunnelBridgeEnd(v->tile), false); } + + if (v->Next() == nullptr && (IsRailStationTile(v->tile) || IsExtendedRailDepotTile(v->tile))) { + /* Remove reserved tracks of platform tiles behind the train. */ + TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir()))); + for (TileIndex pt_tile = v->tile + diff; IsCompatiblePlatformTile(pt_tile, v->tile); pt_tile += diff) { + if (IsExtendedRailDepotTile(pt_tile)) { + if (!HasDepotReservation(pt_tile)) break; + if (EnsureNoVisibleVehicleOnGround(pt_tile).Failed()) break; + SetDepotReservation(pt_tile, false); + } else { + if (!HasStationReservation(pt_tile)) break; + if (EnsureNoVisibleVehicleOnGround(pt_tile).Failed()) break; + SetRailStationReservation(pt_tile, false); + } + MarkTileDirtyByTile(pt_tile); + } + } } /* we may need to update crossing we were approaching, @@ -3318,6 +3353,7 @@ uint Train::Crash(bool flooded) } victims += this->GroundVehicleBase::Crash(flooded); + this->ReserveTrackUnderConsist(); this->crash_anim_pos = flooded ? 4000 : 1; // max 4440, disappear pretty fast when flooded return victims; @@ -3396,6 +3432,9 @@ static Vehicle *FindTrainCollideEnum(Vehicle *v, void *data) tcc->num += TrainCrashed(tcc->v); tcc->num += TrainCrashed(coll); + /* The crashing of the coll train frees reservation of train v: Reserve again for train v. */ + tcc->v->ReserveTrackUnderConsist(); + return nullptr; // continue searching } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 83518b4b911eb..76dc88afd0d8d 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -667,6 +667,24 @@ CommandCost EnsureNoVehicleOnGround(TileIndex tile) return CommandCost(); } +/** + * Ensure there is no visible vehicle at the ground at the given position. + * @param tile Position to examine. + * @return Succeeded command (ground is free) or failed command (a visible vehicle is found). + */ +CommandCost EnsureNoVisibleVehicleOnGround(TileIndex tile) +{ + int z = GetTileMaxPixelZ(tile); + + /* Value v is not safe in MP games, however, it is used to generate a local + * error message only (which may be different for different machines). + * Such a message does not affect MP synchronisation. + */ + Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true); + if (v != nullptr && (v->vehstatus & VS_HIDDEN) == 0) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type); + return CommandCost(); +} + /** Procedure called for every vehicle found in tunnel/bridge in the hash map */ static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data) { diff --git a/src/vehicle_func.h b/src/vehicle_func.h index eedd4c4a6d928..d92b6b1703bfd 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -165,6 +165,7 @@ inline StringID GetCmdSendToDepotMsg(const BaseVehicle *v) } CommandCost EnsureNoVehicleOnGround(TileIndex tile); +CommandCost EnsureNoVisibleVehicleOnGround(TileIndex tile); CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits); bool CanVehicleUseStation(EngineID engine_type, const struct Station *st); From 5b0e8d5331062f39d61e68422e0c1fdd69575249 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 16 Jan 2021 20:44:46 +0100 Subject: [PATCH 062/143] Add: Allow trains to reverse when leaving an extended depot. --- src/train_cmd.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index f5f011d204fcc..8f6ea09dd973b 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2445,6 +2445,19 @@ bool HandleTrainEnterDepot(Train *v) return true; } +bool CheckReverseTrain(const Train *v) +{ + if (_settings_game.difficulty.line_reverse_mode != 0 || + v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE || + !(v->direction & 1)) { + return false; + } + + assert(v->track != TRACK_BIT_NONE); + + return YapfTrainCheckReverse(v); +} + /** * Will the train stay in the depot the next tick? * @param v %Train to check. @@ -2464,6 +2477,8 @@ static bool CheckTrainStayInDepot(Train *v) v->UpdatePosition(); v->UpdateViewport(true, true); v->UpdateAcceleration(); + ProcessOrders(v); + if (CheckReverseTrain(v)) ReverseTrainDirection(v); InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); return false; } else { @@ -3113,19 +3128,6 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay) } -static bool CheckReverseTrain(const Train *v) -{ - if (_settings_game.difficulty.line_reverse_mode != 0 || - v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE || - !(v->direction & 1)) { - return false; - } - - assert(v->track != TRACK_BIT_NONE); - - return YapfTrainCheckReverse(v); -} - /** * Get the location of the next station to visit. * @param station Next station to visit. From ec1a98cf843d89906025c0ba630c7286d83a6edc Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Mon, 29 Mar 2021 17:44:44 +0200 Subject: [PATCH 063/143] Add: Base files for placing trains in extended depots. --- src/CMakeLists.txt | 2 + src/lang/english.txt | 9 ++ src/train.h | 4 + src/train_cmd.cpp | 4 +- src/train_placement.cpp | 312 ++++++++++++++++++++++++++++++++++++++++ src/train_placement.h | 58 ++++++++ 6 files changed, 386 insertions(+), 3 deletions(-) create mode 100644 src/train_placement.cpp create mode 100644 src/train_placement.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3886fa5f21c7f..ad909c65f568f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -504,6 +504,8 @@ add_files( train.h train_cmd.cpp train_cmd.h + train_placement.cpp + train_placement.h train_gui.cpp transparency.h transparency_gui.cpp diff --git a/src/lang/english.txt b/src/lang/english.txt index 31a1c030ce701..19a01d35f23e5 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5182,6 +5182,15 @@ STR_ERROR_UNABLE_TO_FIND_APPROPRIATE_DEPOT_TILE :{WHITE}Unable t STR_ERROR_DEPOT_TOO_SPREAD_OUT :{WHITE}... depot too spread out STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Wrong depot type +STR_ERROR_CAN_T_START_PLATFORM_TYPE :{WHITE}{VEHICLE} can't be started because there is no compatible platform in the depot for this type of train +STR_ERROR_CAN_T_START_PLATFORM_LONG :{WHITE}{VEHICLE} can't be started because compatible platforms are not long enough +STR_ERROR_DEPOT_FULL_DEPOT :There is no free depot compatible with this type of vehicle +###length 5 +STR_ADVICE_PLATFORM_TYPE :{WHITE}{VEHICLE} can't leave depot because there is no compatible platform for this type of train +STR_ADVICE_PLATFORM_LONG :{WHITE}{VEHICLE} can't leave depot because compatible platforms are not long enough +STR_ADVICE_VEHICLE_HAS_NO_POWER :{WHITE}{VEHICLE} can't leave depot because it has no power in any tile of the depot +STR_ADVICE_PLATFORM_FREE_PLATFORM :{WHITE}{VEHICLE} can't leave depot because compatible platforms are occupied +STR_ADVICE_PLATFORM_SIGNALS :{WHITE}{VEHICLE} can't leave depot because the segments of the compatible free platforms are occupied. Check the signaling of those segments. # Depot unbunching related errors STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED :{WHITE}... can only have one unbunching order diff --git a/src/train.h b/src/train.h index 65fd1503c1039..7edc4f5832a26 100644 --- a/src/train.h +++ b/src/train.h @@ -21,6 +21,9 @@ struct Train; +static const uint8_t _vehicle_initial_x_fract[4] = {10, 8, 4, 8}; +static const uint8_t _vehicle_initial_y_fract[4] = { 8, 4, 8, 10}; + /** Rail vehicle flags. */ enum VehicleRailFlags { VRF_REVERSING = 0, @@ -355,5 +358,6 @@ struct Train final : public GroundVehicle { bool HasCompatibleDepotTile(TileIndex tile, const Train *t); bool HandleTrainEnterDepot(Train *v); +bool CheckReverseTrain(const Train *v); #endif /* TRAIN_H */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 8f6ea09dd973b..4bdae6c8fcf08 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -42,6 +42,7 @@ #include "depot_base.h" #include "platform_func.h" #include "depot_map.h" +#include "train_placement.h" #include "table/strings.h" #include "table/train_sprites.h" @@ -55,9 +56,6 @@ static TileIndex TrainApproachingCrossingTile(const Train *v); static void CheckIfTrainNeedsService(Train *v); static void CheckNextTrainTile(Train *v); -static const uint8_t _vehicle_initial_x_fract[4] = {10, 8, 4, 8}; -static const uint8_t _vehicle_initial_y_fract[4] = { 8, 4, 8, 10}; - template <> bool IsValidImageIndex(uint8_t image_index) { diff --git a/src/train_placement.cpp b/src/train_placement.cpp new file mode 100644 index 0000000000000..5b5be9f9d7080 --- /dev/null +++ b/src/train_placement.cpp @@ -0,0 +1,312 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file train_placement.cpp Handling of trains in depot platforms. */ + +#include "stdafx.h" +#include "error.h" +#include "news_func.h" +#include "company_func.h" +#include "strings_func.h" +#include "platform_func.h" +#include "depot_base.h" +#include "depot_map.h" +#include "train_placement.h" +#include "train.h" + +#include "table/strings.h" + +#include "safeguards.h" + + +/** + * Check if a train can be placed in a given tile. + * @param train The train. + * @param check_tile The tile where we want to check whether it is possible to place the train. + * @param executing False if testing and true if the call is being executed. + * @return whether it found a platform to place the train. + */ +bool TrainPlacement::CheckPlacement(const Train *train, TileIndex check_tile, bool executing) +{ + assert(train != nullptr); + assert(IsRailDepotTile(check_tile)); + + RailType rt = GetRailType(check_tile); + PlacementInfo error_info = PI_FAILED_FREE_WAGGON; + bool is_extended_depot = IsExtendedRailDepot(check_tile); + bool succeeded = !train->IsFreeWagon(); + + if (succeeded) { + error_info = PI_FAILED_PLATFORM_TYPE; + for (const Train *t = train; t != nullptr && succeeded; t = t->Next()) { + RailType rail_type = Engine::Get(t->engine_type)->u.rail.railtype; + if (!IsCompatibleRail(rail_type, rt)) succeeded = false; + } + } + + if (succeeded && is_extended_depot) { + error_info = PI_FAILED_LENGTH; + if (train->gcache.cached_total_length > GetPlatformLength(check_tile) * TILE_SIZE) succeeded = false; + } + + if (succeeded) { + error_info = PI_FAILED_POWER; + bool has_power = false; + for (const Train *t = train; t != nullptr && !has_power; t = t->Next()) { + if (HasPowerOnRail(train->railtype, rt)) has_power = true; + } + if (!has_power) succeeded = false; + } + + if (succeeded && is_extended_depot) { + error_info = PI_FAILED_RESERVED; + + /* Check whether any tile of the platform is reserved. Don't assume all platform + * is reserved as a whole: sections of the platform may be reserved by crashed trains. */ + for (TileIndex tile : GetPlatformTileArea(check_tile)) { + if (HasDepotReservation(tile)) { + succeeded = false; + break; + } + } + } + + if (succeeded && executing) { + /* Do not check for signals if really not executing and action. */ + error_info = PI_FAILED_SIGNALS; + SigSegState seg_state = UpdateSignalsOnSegment(check_tile, INVALID_DIAGDIR, train->owner); + if (train->force_proceed == TFP_NONE && seg_state == SIGSEG_FULL) succeeded = false; + } + + if (succeeded) error_info = PI_SUCCESS; + + if (error_info > this->info) { + this->best_tile = check_tile; + this->info = error_info; + + /* A direction for the train must be choosen: the one that allows the longest train in platform. */ + DiagDirection dir = GetRailDepotDirection(check_tile); + if (is_extended_depot && GetPlatformLength(check_tile, dir) > GetPlatformLength(check_tile, ReverseDiagDir(dir))) { + dir = ReverseDiagDir(dir); + } + this->best_dir = DiagDirToDir(dir); + } + + return succeeded; +} + +/** + * Before placing a train in the rails of a depot, a valid platform must + * be found. This function finds a tile for placing the train (and also gets the direction and track). + * If there is no valid tile, it will be returned as best_tile == INVALID_TILE or info == PI_FAILED_PLATFORM_TYPE. + * @param t The train we want to place in rails. + * @param executing False if testing and true if the call is being executed. + * @pre The train must be inside the rail depot as if it where in a standard depot. + * (i.e. the track is TRACK_BIT_DEPOT, vehicles are hidden...). + */ +void TrainPlacement::LookForPlaceInDepot(const Train *train, bool executing) +{ + assert(train != nullptr); + assert(IsRailDepotTile(train->tile)); + + /* Initialitzation. */ + bool is_extended_depot = IsExtendedRailDepot(train->tile); + this->best_tile = (this->placed || !is_extended_depot) ? train->tile : GetPlatformExtremeTile(train->tile, DirToDiagDir(train->direction)); + assert(IsStandardRailDepot(this->best_tile) || IsAnyStartPlatformTile(this->best_tile)); + this->best_dir = train->direction; + this->info = PI_BEGIN; + + /* First candidate is the original position of the train. */ + if (CheckPlacement(train, this->best_tile, executing)) return; + + /* Check all platforms. */ + Depot *depot = Depot::GetByTile(train->tile); + for (auto &depot_tile : depot->depot_tiles) { + if (CheckPlacement(train, depot_tile, executing)) return; + } +} + +/** + * Check if a train can leave now or when other trains + * move away. It returns whether there is a platform long + * enough and with the appropriate rail type. + * @param train The train. + * @param executing False if testing and true if the call is being executed. + * @return true iff there is a compatible platform long enough. + */ +bool TrainPlacement::CanFindAppropriatePlatform(const Train *train, bool executing) +{ + this->LookForPlaceInDepot(train, executing); + return this->info >= PI_WONT_LEAVE; +} + + +/** + * Lift a train in a depot: keep the positions of the elements of the chain if needed, + * and keep also the original tile, direction and track. + * @param train The train we want to lift. + * @pre The train must be inside a rail depot. + * (i.e. the track is 'valid track | TRACK_BIT_DEPOT' or just 'TRACK_BIT_DEPOT'). + */ +void TrainPlacement::LiftTrain(Train *train, DoCommandFlag flags) +{ + assert(train == nullptr || train->IsInDepot()); + assert(train == nullptr || IsRailDepotTile(train->tile)); + assert(this->placed == false); + + /* Lift the train only if we have a train in an extended depot. */ + if (train == nullptr || !IsExtendedRailDepot(train->tile)) return; + + /* Do not lift in recursive commands of autoreplace. */ + if (flags & DC_AUTOREPLACE) return; + + /* If train is not placed... return, because train is already lifted. */ + if ((train->track & ~TRACK_BIT_DEPOT) == 0) return; + + /* Train is placed in rails: lift it. */ + this->placed = true; + if (flags & DC_EXEC) FreeTrainTrackReservation(train); + + for (Train *t = train; t != nullptr; t = t->Next()) { + // Lift. + t->track = TRACK_BIT_DEPOT; + t->tile = train->tile; + t->x_pos = train->x_pos; + t->y_pos = train->y_pos; + t->UpdatePosition(); + t->UpdateViewport(true, true); + + } + + if ((flags & DC_EXEC) == 0) return; + + SetPlatformReservation(train->tile, false); + + UpdateSignalsOnSegment(train->tile, INVALID_DIAGDIR, train->owner); +} + +/** + * When a train is lifted inside a depot, before starting its way again, + * must be placed in rails if in an extended rail depot; this function does all necessary things to do so. + * In general, it's the opposite of #LiftTrain + * @param train The train we want to place in rails. + * @param flags Associated command flags + * @pre The train must be inside the extended rail depot as if in a standard depot. + * (i.e. the track is TRACK_BIT_DEPOT, vehicles are hidden...). + */ +void TrainPlacement::PlaceTrain(Train *train, DoCommandFlag flags) +{ + if (train == nullptr) return; + if (train != train->First()) return; + if (!IsRailDepotTile(train->tile)) return; + if (flags & DC_AUTOREPLACE) return; + + bool executing = (flags & DC_EXEC) != 0; + + /* Look for an appropriate platform. */ + this->LookForPlaceInDepot(train, executing); + assert(!IsExtendedRailDepot(this->best_tile) || IsAnyStartPlatformTile(this->best_tile)); + + if (this->info < PI_FAILED_END || !executing) { + if (!executing) { + /* Restore the train. */ + this->best_tile = train->tile; + this->best_dir = train->direction; + this->info = PI_SUCCESS; + } + + if (!this->placed || (this->info < PI_FAILED_END && executing)) { + for (Train *t = train; t != nullptr; t = t->Next()) { + t->tile = this->best_tile; + t->vehstatus |= VS_HIDDEN; + t->track = TRACK_BIT_DEPOT; + } + if (!executing) return; + train->PowerChanged(); + } + + if (this->info < PI_FAILED_END && executing) { + /* Train cannot leave until changing the depot. Stop the train and send a message. */ + if (info < PI_WONT_LEAVE) { + train->vehstatus |= VS_STOPPED; + /* If vehicle is not stopped and user is the local company, send a message if needed. */ + if ((train->vehstatus & VS_STOPPED) == 0 && train->owner == _local_company && train->IsFrontEngine()) { + SetDParam(0, train->index); + AddVehicleAdviceNewsItem(STR_ADVICE_PLATFORM_TYPE + info - PI_ERROR_BEGIN, train->index); + } + } + return; + } + } + + assert(this->best_tile != INVALID_TILE); + assert(this->best_dir != INVALID_DIR); + assert(IsRailDepotTile(this->best_tile)); + + if (executing) { + train->tile = this->best_tile; + train->track = TrackToTrackBits(GetRailDepotTrack(this->best_tile)); + train->direction = this->best_dir; + train->PowerChanged(); + } + + if (IsStandardRailDepot(this->best_tile)) { + int x = TileX(this->best_tile) * TILE_SIZE + _vehicle_initial_x_fract[DirToDiagDir(this->best_dir)]; + int y = TileY(this->best_tile) * TILE_SIZE + _vehicle_initial_y_fract[DirToDiagDir(this->best_dir)]; + for (Train *t = train; t != nullptr; t = t->Next()) { + t->tile = this->best_tile; + t->direction = this->best_dir; + t->vehstatus |= VS_HIDDEN; + t->track = TRACK_BIT_DEPOT; + t->x_pos = x; + t->y_pos = y; + t->z_pos = GetSlopePixelZ(x, y); + t->UpdatePosition(); + t->UpdateViewport(true, true); + } + return; + } + + DiagDirection placing_dir = ReverseDiagDir(DirToDiagDir(this->best_dir)); + + static const uint8_t _plat_initial_x_fract[4] = {15, 8, 0, 8}; + static const uint8_t _plat_initial_y_fract[4] = { 8, 0, 8, 15}; + + int x = TileX(this->best_tile) * TILE_SIZE | _plat_initial_x_fract[placing_dir]; + int y = TileY(this->best_tile) * TILE_SIZE | _plat_initial_y_fract[placing_dir]; + + /* Add the offset for the first vehicle. */ + x += TileIndexDiffCByDiagDir(placing_dir).x * (train->gcache.cached_veh_length + 1) / 2; + y += TileIndexDiffCByDiagDir(placing_dir).y * (train->gcache.cached_veh_length + 1) / 2; + + /* Proceed placing the train in the given tile. + * At this point, the first vehicle contains the direction, tile and track. + * We must update positions of all the chain. */ + for (Train *t = train; t != nullptr; t = t->Next()) { + t->vehstatus &= ~VS_HIDDEN; + t->direction = this->best_dir; + t->track = DiagDirToDiagTrackBits(placing_dir) | TRACK_BIT_DEPOT; + t->x_pos = x; + t->y_pos = y; + t->z_pos = GetSlopePixelZ(t->x_pos, t->y_pos); + t->tile = TileVirtXY(t->x_pos,t->y_pos); + + assert(t->z_pos == train->z_pos); + assert(IsExtendedRailDepotTile(t->tile)); + + t->UpdatePosition(); + t->UpdateViewport(true, true); + + int advance = t->CalcNextVehicleOffset(); + x += TileIndexDiffCByDiagDir(placing_dir).x * advance; + y += TileIndexDiffCByDiagDir(placing_dir).y * advance; + } + + SetPlatformReservation(train->tile, true); + + UpdateSignalsOnSegment(train->tile, INVALID_DIAGDIR, train->owner); +} diff --git a/src/train_placement.h b/src/train_placement.h new file mode 100644 index 0000000000000..dfa11458941d8 --- /dev/null +++ b/src/train_placement.h @@ -0,0 +1,58 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file train_placement.h Handling of trains in depot platforms. */ + +#ifndef TRAIN_PLA_H +#define TRAIN_PLA_H + +#include "core/enum_type.hpp" +#include "train.h" + + +/* Flags of failure and success when placing a train. */ +enum PlacementInfo { + PI_BEGIN = 0, + PI_FAILED_FREE_WAGGON = PI_BEGIN, // Free waggon: not to be placed. + PI_ERROR_BEGIN, + PI_FAILED_PLATFORM_TYPE = PI_ERROR_BEGIN, // No compatible platforms with train type. + PI_FAILED_LENGTH, // There are compatible platforms but not long enough. + PI_FAILED_POWER, // No engine gets power in the platform. + PI_WONT_LEAVE, + PI_FAILED_RESERVED = PI_WONT_LEAVE, // There are compatible platforms but reserved right now. + PI_FAILED_SIGNALS, // There are compatible platforms not reserved, but signals don't allow placing it now. + PI_FAILED_END, + PI_SUCCESS = PI_FAILED_END, // There is an appropriate platform. + PI_END, +}; + +/* Store position of a train and lift it when necessary. */ +struct TrainPlacement { + bool placed; // True if train is placed in rails. + TileIndex best_tile; // Best tile for the train. + Direction best_dir; // Best direction for the train. + PlacementInfo info; // Info of possible problems in best platform. + + TrainPlacement() : placed(false), + best_tile(INVALID_TILE), + best_dir(INVALID_DIR), + info(PI_FAILED_PLATFORM_TYPE) {} + + bool CheckPlacement(const Train *train, TileIndex tile, bool executing); + void LookForPlaceInDepot(const Train *train, bool executing); + bool CanFindAppropriatePlatform(const Train *train, bool executing); + + void LiftTrain(Train *train, DoCommandFlag flags); + void PlaceTrain(Train *train, DoCommandFlag flags); +}; + +static inline bool CheckIfTrainNeedsPlacement(const Train *train) +{ + return IsExtendedRailDepot(train->tile) && (train->track & ~TRACK_BIT_DEPOT) == 0; +} + +#endif /* TRAIN_PLA_H */ From d7dcc7360695ef16e37f22bba77b9dd4d086502a Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 22 Apr 2023 21:29:44 +0200 Subject: [PATCH 064/143] Change: Check whether to stay in an extended rail depot or place the train and reverse if appropriate. --- src/depot_gui.cpp | 2 +- src/lang/english.txt | 1 + src/train_cmd.cpp | 190 ++++++++++++++++++++++--------------------- src/vehicle_cmd.cpp | 39 ++++++++- 4 files changed, 137 insertions(+), 95 deletions(-) diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 85d99eeaf1530..16f5b85ba0d03 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -817,7 +817,7 @@ struct DepotWindow : Window { case WID_D_STOP_ALL: case WID_D_START_ALL: { VehicleListIdentifier vli(VL_DEPOT_LIST, this->type, this->owner); - Command::Post(tile, widget == WID_D_START_ALL, false, vli); + Command::Post(STR_ERROR_CAN_T_START_STOP_VEHICLES, tile, widget == WID_D_START_ALL, false, vli); break; } diff --git a/src/lang/english.txt b/src/lang/english.txt index 19a01d35f23e5..4aa70378dd94d 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5160,6 +5160,7 @@ STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE :{WHITE}... depo STR_ERROR_DEPOT_EXTENDING_PLATFORMS :{WHITE}Extending already reserved depot platforms STR_ERROR_DEPOT_EXTENDED_RAIL_DEPOT_IS_NOT_FREE :{WHITE}Extended rail depot has a reserved tile and can't be converted +STR_ERROR_CAN_T_START_STOP_VEHICLES :{WHITE}Can't start at least one vehicle in the depot... STR_ERROR_CAN_T_RENAME_DEPOT :{WHITE}Can't rename depot... STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT :{WHITE}... must be stopped inside a depot diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 4bdae6c8fcf08..ef3b1a2469b74 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -883,6 +883,10 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin UpdateTrainGroupID(v); CheckConsistencyOfArticulatedVehicle(v); + + TrainPlacement train_placement; + train_placement.LiftTrain(v, flags); + train_placement.PlaceTrain(v, flags); } return CommandCost(); @@ -1051,82 +1055,6 @@ static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *origina return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME); } -/** - * Check if a train can be placed in a depot tile. - * @param train The train. - * @param tile The tile to check whether it is possible to place the train. - * @return whether it found a depot tile in which to place the train. - */ -bool CheckPlacement(const Train *train, TileIndex tile) -{ - assert(train != nullptr); - assert(IsRailDepotTile(tile)); - - RailType rt = GetRailType(tile); - for (const Train *t = train; t != nullptr; t = t->Next()) { - RailType rail_type = Engine::Get(t->engine_type)->u.rail.railtype; - if (!IsCompatibleRail(rail_type, rt)) return false; - } - - return true; -} - -/** - * Find a valid tile before placing a train in the depot. - * @param t The train to place in a rail depot tile. - * @return a compatible tile, if any, preferabily the one the first vehicle is or INVALID_TILE if none found. - */ -TileIndex LookForTileInDepot(const Train *train) -{ - assert(train != nullptr); - assert(IsRailDepotTile(train->tile)); - TileIndex best_tile = INVALID_TILE; - - /* First candidate is the original position of the train. */ - if (CheckPlacement(train, train->tile)) { - if (HasPowerOnRail(train->railtype, GetRailType(train->tile))) return train->tile; - best_tile = train->tile; - } - - /* Check all depot tiles. */ - Depot *depot = Depot::GetByTile(train->tile); - for (std::vector::iterator it = depot->depot_tiles.begin(); it != depot->depot_tiles.end(); ++it) { - if (CheckPlacement(train, *it)) { - if (HasPowerOnRail(train->railtype, GetRailType(*it))) return *it; - if (best_tile == INVALID_TILE) best_tile = *it; - } - } - - return best_tile; -} - -/** - * Find an appropriate depot tile for a train and place - * all the vehicle chain in the same depot tile. - * @param train The train to place. - */ -void PlaceOnRailDepot(Train *train) -{ - assert(train->First() == train); - - TileIndex depot_tile = LookForTileInDepot(train); - assert(depot_tile != INVALID_TILE); - - DiagDirection diag_dir = GetRailDepotDirection(depot_tile); - int x = TileX(depot_tile) * TILE_SIZE + _vehicle_initial_x_fract[diag_dir]; - int y = TileY(depot_tile) * TILE_SIZE + _vehicle_initial_y_fract[diag_dir]; - for (Train *t = train; t != nullptr; t = t->Next()) { - t->tile = depot_tile; - t->direction = DiagDirToDir(diag_dir); - t->vehstatus |= VS_HIDDEN; - t->track = TRACK_BIT_DEPOT; - t->x_pos = x; - t->y_pos = y; - t->z_pos = GetSlopePixelZ(x, y); - t->UpdatePosition(); - } -} - /** * Check whether the train parts can be attached. * @param t the train to check @@ -1137,7 +1065,9 @@ static CommandCost CheckTrainAttachment(Train *t) /* No multi-part train, no need to check. */ if (t == nullptr || t->Next() == nullptr) return CommandCost(); - if (LookForTileInDepot(t) == INVALID_TILE) return_cmd_error(STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT); + TrainPlacement tp; + tp.LookForPlaceInDepot(t, false); + if (tp.info == PI_FAILED_PLATFORM_TYPE) return_cmd_error(STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT); /* The maximum length for a train. For each part we decrease this by one * and if the result is negative the train is simply too long. */ @@ -1415,6 +1345,13 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID bool original_src_head_front_engine = original_src_head->IsFrontEngine(); bool original_dst_head_front_engine = original_dst_head != nullptr && original_dst_head->IsFrontEngine(); + TrainPlacement train_placement_src; + TrainPlacement train_placement_dst; + train_placement_src.LiftTrain(src_head, flags); + train_placement_dst.LiftTrain(dst_head, flags); + + assert(src_head != nullptr); + /* (Re)arrange the trains in the wanted arrangement. */ ArrangeTrains(&dst_head, dst, &src_head, src, move_chain); @@ -1427,6 +1364,8 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID /* Restore the train we had. */ RestoreTrainBackup(original_src); RestoreTrainBackup(original_dst); + train_placement_src.PlaceTrain(original_src_head, flags & ~DC_EXEC); + if (src_head != dst_head) train_placement_dst.PlaceTrain(original_dst_head, flags & ~DC_EXEC); return ret; } } @@ -1504,15 +1443,14 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID CheckCargoCapacity(dst_head); } - if (src_head != nullptr) { - PlaceOnRailDepot(src_head->First()); - src_head->First()->MarkDirty(); - } + if (src_head != nullptr) src_head->First()->MarkDirty(); + if (dst_head != nullptr) dst_head->First()->MarkDirty(); - if (dst_head != nullptr) { - PlaceOnRailDepot(dst_head->First()); - dst_head->First()->MarkDirty(); - } + bool reverse_emplacement_order = !train_placement_src.placed && train_placement_dst.placed; + + if (!reverse_emplacement_order) train_placement_src.PlaceTrain(src_head, flags); + if (src_head != dst_head) train_placement_dst.PlaceTrain(dst_head, flags); + if (reverse_emplacement_order) train_placement_src.PlaceTrain(src_head, flags); /* We are undoubtedly changing something in the depot and train list. */ InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(src->tile)); @@ -1521,6 +1459,8 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID /* We don't want to execute what we're just tried. */ RestoreTrainBackup(original_src); RestoreTrainBackup(original_dst); + train_placement_src.PlaceTrain(original_src_head, flags); + if (src_head != dst_head) train_placement_dst.PlaceTrain(original_dst_head, flags); } return CommandCost(); @@ -1545,6 +1485,9 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b if (v->IsRearDualheaded()) return_cmd_error(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT); + TrainPlacement train_placement; + train_placement.LiftTrain(first, flags); + /* First make a backup of the order of the train. That way we can do * whatever we want with the order and later on easily revert. */ TrainList original; @@ -1562,12 +1505,14 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b if (ret.Failed()) { /* Restore the train we had. */ RestoreTrainBackup(original); + train_placement.PlaceTrain(first, flags); return ret; } if (first->orders == nullptr && !OrderList::CanAllocateItem()) { /* Restore the train we had. */ RestoreTrainBackup(original); + train_placement.PlaceTrain(first, flags); return_cmd_error(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS); } @@ -1597,6 +1542,7 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b /* We need to update the information about the train. */ NormaliseTrainHead(new_head); + train_placement.PlaceTrain(new_head, flags); /* We are undoubtedly changing something in the depot and train list. */ InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); @@ -1605,8 +1551,9 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b /* Actually delete the sold 'goods' */ delete sell_head; } else { - /* We don't want to execute what we're just tried. */ + /* We don't want to execute what we have just tried. */ RestoreTrainBackup(original); + train_placement.PlaceTrain(first, flags); } return cost; @@ -2117,7 +2064,11 @@ static bool IsWholeTrainInsideDepot(const Train *v) void ReverseTrainDirection(Train *v) { if (IsRailDepotTile(v->tile)) { - if (IsWholeTrainInsideDepot(v)) return; + if (IsExtendedDepot(v->tile)) { + if ((v->track & TRACK_BIT_DEPOT) != 0 && (v->track & ~TRACK_BIT_DEPOT) != 0) return; + } else { + if (IsWholeTrainInsideDepot(v)) return; + } InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); } @@ -2231,7 +2182,14 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool ToggleBit(v->flags, VRF_REVERSE_DIRECTION); front->ConsistChanged(CCF_ARRANGE); - if (IsRailDepotTile(front->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile)); + if (IsRailDepotTile(front->tile)) { + if (IsExtendedDepot(front->tile)) { + TrainPlacement tp; + tp.LiftTrain(front, flags); + tp.PlaceTrain(front, flags & ~DC_EXEC); + } + SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile)); + } SetWindowDirty(WC_VEHICLE_DETAILS, front->index); SetWindowDirty(WC_VEHICLE_VIEW, front->index); SetWindowClassesDirty(WC_TRAINS_LIST); @@ -2241,6 +2199,9 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool if (!v->IsPrimaryVehicle()) return CMD_ERROR; if ((v->vehstatus & VS_CRASHED) || v->breakdown_ctr != 0) return CMD_ERROR; + /* Do not reverse while servicing. */ + if (IsExtendedRailDepotTile(v->tile) && (v->track & TRACK_BIT_DEPOT) != 0 && (v->track & ~TRACK_BIT_DEPOT) != 0) return CMD_ERROR; + if (flags & DC_EXEC) { /* Properly leave the station if we are loading and won't be loading anymore */ if (v->current_order.IsType(OT_LOADING)) { @@ -2469,15 +2430,62 @@ static bool CheckTrainStayInDepot(Train *v) DepotID depot_id = GetDepotIndex(v->tile); if (IsExtendedRailDepot(v->tile)) { + /* If not placed, try it. If not possible, exit. */ + if (CheckIfTrainNeedsPlacement(v)) { + /* If stuck, wait a little bit, so we can avoid + * trying placing it as much as we can. */ + bool already_stuck = false; + bool send_message = false; + if (HasBit(v->flags, VRF_TRAIN_STUCK)) { + already_stuck = true; + v->wait_counter++; + if (v->wait_counter % (1 << 7) != 0) { + return true; + } else if (v->wait_counter % (1 << 11) == 0) { + send_message = true; + } + ClrBit(v->flags, VRF_TRAIN_STUCK); + } + + TrainPlacement train_placement; + train_placement.LiftTrain(v, DC_EXEC); + train_placement.LookForPlaceInDepot(v, true); + if (train_placement.info < PI_FAILED_END) { + if (send_message) { + ClrBit(v->flags, VRF_TRAIN_STUCK); + /* Show message to player. */ + if (_settings_client.gui.lost_vehicle_warn && v->owner == _local_company) { + SetDParam(0, v->index); + AddVehicleAdviceNewsItem(STR_ADVICE_PLATFORM_TYPE + train_placement.info - PI_ERROR_BEGIN, v->index); + } + } + if (already_stuck) { + SetBit(v->flags, VRF_TRAIN_STUCK); + } else { + MarkTrainAsStuck(v); + } + return true; + } else { + VehicleServiceInExtendedDepot(v); + train_placement.PlaceTrain(v, DC_EXEC); + if (!IsExtendedDepot(v->tile)) return true; + } + } else { + VehicleServiceInExtendedDepot(v); + } + for (Train *u = v; u != nullptr; u = u->Next()) u->track &= ~TRACK_BIT_DEPOT; - v->cur_speed = 0; - v->UpdatePosition(); - v->UpdateViewport(true, true); + v->cur_speed = 0; v->UpdateAcceleration(); ProcessOrders(v); if (CheckReverseTrain(v)) ReverseTrainDirection(v); InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); + + /* Check whether it is safe to exit the depot. */ + if (UpdateSignalsOnSegment(v->tile, VehicleExitDir(v->direction, v->track), v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) { + if (!TryPathReserve(v, true, true)) return true; + } return false; } else { for (const Train *u = v; u != nullptr; u = u->Next()) { diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index 7147b5c84adf5..1a23c07a23934 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -38,6 +38,8 @@ #include "train_cmd.h" #include "ship_cmd.h" #include "depot_base.h" +#include "train_placement.h" +#include "strings_func.h" #include #include @@ -523,6 +525,11 @@ std::tuple CmdRefitVehicle(DoCommandFla /* For ships and aircraft there is always only one. */ only_this |= front->type == VEH_SHIP || front->type == VEH_AIRCRAFT; + /* If it is a train on a depot, lift it. New length of the vehicle won't be checked. */ + Train *train = (v->type == VEH_TRAIN && IsDepotTile(front->tile)) ? Train::From(front) : nullptr; + TrainPlacement train_placement; + train_placement.LiftTrain(train, flags); + auto [cost, refit_capacity, mail_capacity, cargo_capacities] = RefitVehicle(v, only_this, num_vehicles, new_cid, new_subtype, flags, auto_refit); if (flags & DC_EXEC) { @@ -560,6 +567,9 @@ std::tuple CmdRefitVehicle(DoCommandFla v->InvalidateNewGRFCacheOfChain(); } + /* If it is a train on an extended depot, try placing it. */ + train_placement.PlaceTrain(train, flags); + return { cost, refit_capacity, mail_capacity, cargo_capacities }; } @@ -584,9 +594,20 @@ CommandCost CmdStartStopVehicle(DoCommandFlag flags, VehicleID veh_id, bool eval if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED); switch (v->type) { - case VEH_TRAIN: - if ((v->vehstatus & VS_STOPPED) && Train::From(v)->gcache.cached_power == 0) return_cmd_error(STR_ERROR_TRAIN_START_NO_POWER); + case VEH_TRAIN: { + Train *t = Train::From(v); + if ((v->vehstatus & VS_STOPPED) && t->gcache.cached_power == 0) return_cmd_error(STR_ERROR_TRAIN_START_NO_POWER); + + /* Train cannot leave until changing the depot. Stop the train and send a message. */ + if (!(flags & DC_AUTOREPLACE) && v->IsStoppedInDepot() && CheckIfTrainNeedsPlacement(t)) { + TrainPlacement train_placement; + if (!train_placement.CanFindAppropriatePlatform(t, (flags & DC_EXEC) != 0)) { + SetDParam(0, v->index); + return_cmd_error(STR_ERROR_CAN_T_START_PLATFORM_TYPE + train_placement.info - PI_FAILED_PLATFORM_TYPE); + } + } break; + } case VEH_SHIP: case VEH_ROAD: @@ -638,6 +659,11 @@ CommandCost CmdStartStopVehicle(DoCommandFlag flags, VehicleID veh_id, bool eval /* Unbunching data is no longer valid. */ v->ResetDepotUnbunching(); + if (v->type == VEH_TRAIN && v->IsInDepot() && IsExtendedDepotTile(v->tile)) { + if ((v->vehstatus & VS_STOPPED) != 0) FreeTrainTrackReservation(Train::From(v)); + UpdateExtendedDepotReservation(v, true); + } + v->MarkDirty(); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); if (IsDepotTile(v->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); @@ -677,7 +703,14 @@ CommandCost CmdMassStartStopVehicle(DoCommandFlag flags, TileIndex tile, bool do if (!vehicle_list_window && !v->IsChainInDepot()) continue; /* Just try and don't care if some vehicle's can't be stopped. */ - Command::Do(flags, v->index, false); + CommandCost ret = Command::Do(flags, v->index, false); + if (ret.Failed()) { + if (ret.GetErrorMessage() == STR_ERROR_CAN_T_START_PLATFORM_TYPE || + ret.GetErrorMessage() == STR_ERROR_CAN_T_START_PLATFORM_LONG) { + SetDParam(0, v->index); + return ret; + } + } } return CommandCost(); From ce5ed5a78ef5f1b0a67d36c00525b0a74edc17a6 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 19:43:12 +0200 Subject: [PATCH 065/143] Change: Add the DC_AUTOREPLACE flag in missing places. --- src/autoreplace_cmd.cpp | 23 ++++++++++++++--------- src/train.h | 2 +- src/train_cmd.cpp | 8 +++++--- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index ffcaf46710c88..eaa1e1f5cd016 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -373,13 +373,13 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic if (refit_cargo != CARGO_NO_REFIT) { uint8_t subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo); - cost.AddCost(std::get<0>(Command::Do(DC_EXEC, new_veh->index, refit_cargo, subtype, false, false, 0))); + cost.AddCost(std::get<0>(Command::Do(DC_EXEC | DC_AUTOREPLACE, new_veh->index, refit_cargo, subtype, false, false, 0))); assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace() } /* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */ if (new_veh->type == VEH_TRAIN && HasBit(Train::From(old_veh)->flags, VRF_REVERSE_DIRECTION)) { - Command::Do(DC_EXEC, new_veh->index, true); + Command::Do(DC_EXEC | DC_AUTOREPLACE, new_veh->index, true); } return cost; @@ -474,7 +474,7 @@ static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, b if ((flags & DC_EXEC) != 0) { /* Move the new vehicle behind the old */ - CmdMoveVehicle(new_v, old_v, DC_EXEC, false); + CmdMoveVehicle(new_v, old_v, DC_EXEC | DC_AUTOREPLACE, false); /* Take over cargo * Note: We do only transfer cargo from the old to the new vehicle. @@ -494,7 +494,7 @@ static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, b /* If we are not in DC_EXEC undo everything */ if ((flags & DC_EXEC) == 0) { - Command::Do(DC_EXEC, new_v->index, false, false, INVALID_CLIENT_ID); + Command::Do(DC_EXEC | DC_AUTOREPLACE, new_v->index, false, false, INVALID_CLIENT_ID); } } @@ -545,6 +545,8 @@ void CopyShipStatusInExtendedDepot(const Ship *old_ship, Ship *new_ship) */ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon_removal, bool *nothing_to_do) { + assert(flags & DC_AUTOREPLACE); + Vehicle *old_head = *chain; assert(old_head->IsPrimaryVehicle()); TileIndex tile = old_head->tile; @@ -598,7 +600,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon } if (last_engine == nullptr) last_engine = append; - cost.AddCost(CmdMoveVehicle(append, new_head, DC_EXEC, false)); + cost.AddCost(CmdMoveVehicle(append, new_head, DC_EXEC | DC_AUTOREPLACE, false)); if (cost.Failed()) break; } if (last_engine == nullptr) last_engine = new_head; @@ -617,7 +619,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) { /* Insert wagon after 'last_engine' */ - CommandCost res = CmdMoveVehicle(append, last_engine, DC_EXEC, false); + CommandCost res = CmdMoveVehicle(append, last_engine, DC_EXEC | DC_AUTOREPLACE, false); /* When we allow removal of wagons, either the move failing due * to the train becoming too long, or the train becoming longer @@ -648,7 +650,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon assert(RailVehInfo(wagon->engine_type)->railveh_type == RAILVEH_WAGON); /* Sell wagon */ - [[maybe_unused]] CommandCost ret = Command::Do(DC_EXEC, wagon->index, false, false, INVALID_CLIENT_ID); + [[maybe_unused]] CommandCost ret = Command::Do(DC_EXEC | DC_AUTOREPLACE, wagon->index, false, false, INVALID_CLIENT_ID); assert(ret.Succeeded()); it->new_veh = nullptr; @@ -714,7 +716,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon if ((flags & DC_EXEC) == 0) { for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) { if (it->new_veh != nullptr) { - Command::Do(DC_EXEC, it->new_veh->index, false, false, INVALID_CLIENT_ID); + Command::Do(DC_EXEC | DC_AUTOREPLACE, it->new_veh->index, false, false, INVALID_CLIENT_ID); it->new_veh = nullptr; } } @@ -751,7 +753,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon /* If we are not in DC_EXEC undo everything */ if ((flags & DC_EXEC) == 0) { - Command::Do(DC_EXEC, new_head->index, false, false, INVALID_CLIENT_ID); + Command::Do(DC_EXEC | DC_AUTOREPLACE, new_head->index, false, false, INVALID_CLIENT_ID); } } } @@ -817,6 +819,9 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) if (IsExtendedDepotTile(v->tile)) UpdateExtendedDepotReservation(v, false); + /* Start autoreplacing the vehicle. */ + flags |= DC_AUTOREPLACE; + /* We have to construct the new vehicle chain to test whether it is valid. * Vehicle construction needs random bits, so we have to save the random seeds * to prevent desyncs and to replay newgrf callbacks during DC_EXEC */ diff --git a/src/train.h b/src/train.h index 7edc4f5832a26..9fcd925c82fce 100644 --- a/src/train.h +++ b/src/train.h @@ -69,7 +69,7 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, i void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type); bool TrainOnCrossing(TileIndex tile); -void NormalizeTrainVehInDepot(const Train *u); +void NormalizeTrainVehInDepot(const Train *u, DoCommandFlag flags = DC_EXEC); /** Variables that are cached to improve performance and such */ struct TrainCache { diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index ef3b1a2469b74..6e28db1aba69f 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -743,7 +743,7 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const w->engine_type == e->index && ///< Same type w->First() != v && ///< Don't connect to ourself !(w->vehstatus & VS_CRASHED)) { ///< Not crashed/flooded - if (Command::Do(DC_EXEC, v->index, w->Last()->index, true).Succeeded()) { + if (Command::Do(flags | DC_EXEC, v->index, w->Last()->index, true).Succeeded()) { break; } } @@ -754,14 +754,16 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const } /** Move all free vehicles in the depot to the train */ -void NormalizeTrainVehInDepot(const Train *u) +void NormalizeTrainVehInDepot(const Train *u, DoCommandFlag flags) { assert(u->IsEngine()); + assert(flags & DC_EXEC); + DepotID dep_id = GetDepotIndex(u->tile); for (const Train *v : Train::Iterate()) { if (v->IsFreeWagon() && v->IsInDepot() && GetDepotIndex(v->tile) == dep_id) { - if (Command::Do(DC_EXEC, v->index, u->index, true).Failed()) { + if (Command::Do(flags, v->index, u->index, true).Failed()) { break; } } From 9b17aea6fc0345369bbd4fa3d96c38873d8d889e Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 22 Apr 2023 21:40:20 +0200 Subject: [PATCH 066/143] Change: Deal with autoreplacements in extended rail depots. As the autoreplace flag is set, only lift and tryplacing in the original command for autoreplacing and not in any recursive calls to move, buy, refit and sell commands. # Conflicts: # src/autoreplace_cmd.cpp --- src/autoreplace_cmd.cpp | 34 +++++++++++++++++++++++++++++++--- src/depot_gui.cpp | 2 +- src/lang/english.txt | 1 + 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index eaa1e1f5cd016..d94249e188084 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -30,6 +30,8 @@ #include "train_cmd.h" #include "vehicle_cmd.h" #include "depot_map.h" +#include "train_placement.h" +#include "news_func.h" #include "table/strings.h" @@ -816,8 +818,14 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) if (cost.Failed()) return cost; assert(free_wagon || v->IsStoppedInDepot()); + if (flags & DC_EXEC) v->StopServicing(); - if (IsExtendedDepotTile(v->tile)) UpdateExtendedDepotReservation(v, false); + TrainPlacement train_placement; + if (v->type == VEH_TRAIN) { + train_placement.LiftTrain(Train::From(v), flags); + } else if (IsExtendedDepotTile(v->tile)) { + UpdateExtendedDepotReservation(v, false); + } /* Start autoreplacing the vehicle. */ flags |= DC_AUTOREPLACE; @@ -845,10 +853,30 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) assert(ret.GetCost() == cost.GetCost()); } - if (IsExtendedDepotTile(v->tile)) UpdateExtendedDepotReservation(v, true); + /* Check whether the train can be placed on tracks. */ + bool platform_error = false; + + /* Autoreplacing is done. */ + flags &= ~DC_AUTOREPLACE; + + if (v->type == VEH_TRAIN) { + if (cost.Succeeded() && (flags & DC_EXEC) != 0) { + train_placement.LookForPlaceInDepot(Train::From(v), false); + if (train_placement.info < PI_WONT_LEAVE) { + platform_error = true; + if (v->owner == _local_company && v->IsFrontEngine()) { + SetDParam(0, v->index); + AddVehicleAdviceNewsItem(STR_ADVICE_PLATFORM_TYPE + train_placement.info - PI_ERROR_BEGIN, v->index); + } + } + } + train_placement.PlaceTrain(Train::From(v), flags); + } else if (IsExtendedDepotTile(v->tile)) { + UpdateExtendedDepotReservation(v, true); + } /* Restart the vehicle */ - if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false)); + if (!platform_error && !was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false)); assert(cost.Failed() || !nothing_to_do); return cost; diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 16f5b85ba0d03..f7fd753bab568 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -840,7 +840,7 @@ struct DepotWindow : Window { break; case WID_D_AUTOREPLACE: - Command::Post(tile, this->type); + Command::Post(STR_ERROR_CAN_T_REPLACE_VEHICLES, tile, this->type); break; } diff --git a/src/lang/english.txt b/src/lang/english.txt index 4aa70378dd94d..c78501d545cf5 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5161,6 +5161,7 @@ STR_ERROR_DEPOT_EXTENDING_PLATFORMS :{WHITE}Extendin STR_ERROR_DEPOT_EXTENDED_RAIL_DEPOT_IS_NOT_FREE :{WHITE}Extended rail depot has a reserved tile and can't be converted STR_ERROR_CAN_T_START_STOP_VEHICLES :{WHITE}Can't start at least one vehicle in the depot... +STR_ERROR_CAN_T_REPLACE_VEHICLES :{WHITE}Can't replace vehicles in the depot... STR_ERROR_CAN_T_RENAME_DEPOT :{WHITE}Can't rename depot... STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT :{WHITE}... must be stopped inside a depot From 4ad42e8b17c03c995db5b1386635bf05680af608 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sun, 21 Feb 2021 22:17:13 +0100 Subject: [PATCH 067/143] Change: Flood trains inside a train depot platform. --- src/water_cmd.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index f75ca6d4dce01..1bf4ccbf75e23 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -40,6 +40,9 @@ #include "water_cmd.h" #include "landscape_cmd.h" #include "pathfinder/water_regions.h" +#include "train.h" +#include "platform_func.h" +#include "pbs.h" #include "table/strings.h" @@ -1073,16 +1076,25 @@ static void FloodVehicles(TileIndex tile) return; } - if (!IsBridgeTile(tile)) { + if (IsBridgeTile(tile)) { + TileIndex end = GetOtherBridgeEnd(tile); + z = GetBridgePixelHeight(tile); + + FindVehicleOnPos(tile, &z, &FloodVehicleProc); + FindVehicleOnPos(end, &z, &FloodVehicleProc); + } else if (IsExtendedRailDepotTile(tile)) { + /* Free reserved path. */ + if (HasDepotReservation(tile)) { + Train *v = GetTrainForReservation(tile, GetRailDepotTrack(tile)); + if (v != nullptr) FreeTrainTrackReservation(v); + } + /* Crash trains on platform. */ + for (TileIndex t : GetPlatformTileArea(tile)) { + FindVehicleOnPos(t, &z, &FloodVehicleProc); + } + } else { FindVehicleOnPos(tile, &z, &FloodVehicleProc); - return; } - - TileIndex end = GetOtherBridgeEnd(tile); - z = GetBridgePixelHeight(tile); - - FindVehicleOnPos(tile, &z, &FloodVehicleProc); - FindVehicleOnPos(end, &z, &FloodVehicleProc); } /** From 7ea7f03e4c348640aeecd3909f31dbb9a2348d98 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 2 Oct 2021 17:39:31 +0200 Subject: [PATCH 068/143] Add: Set reservation and penalties to improve pathfinding in extended depots. --- src/pathfinder/yapf/yapf_costrail.hpp | 9 +++++++++ src/train_cmd.cpp | 3 +++ src/train_placement.cpp | 2 ++ 3 files changed, 14 insertions(+) diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 67fdd07ebb934..196403aa97ff4 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -607,6 +607,15 @@ class CYapfCostRailT : public CYapfCostBase { uint platform_length = GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir()))); /* Reduce the extra cost caused by passing-platform penalty (each platform receives it in the segment cost). */ extra_cost -= Yapf().PfGetSettings().rail_station_penalty * platform_length; + if (tf->m_is_extended_depot) { + DepotReservation depot_reservation = GetDepotReservation(n.GetLastTile()); + if (depot_reservation == DEPOT_RESERVATION_FULL_STOPPED_VEH) { + extra_cost += YAPF_INFINITE_PENALTY; + } else { + extra_cost += (HasDepotReservation(n.GetLastTile()) ? 2 : 1) * platform_length * Yapf().PfGetSettings().rail_station_penalty; + } + } + /* Add penalty for the inappropriate platform length. */ extra_cost += PlatformLengthPenalty(platform_length); } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 6e28db1aba69f..684e77fab0b31 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2391,6 +2391,7 @@ bool HandleTrainEnterDepot(Train *v) for (Train *u = t; u != nullptr; u = u->Next()) u->track |= TRACK_BIT_DEPOT; t->force_proceed = TFP_NONE; ClrBit(t->flags, VRF_TOGGLE_REVERSE); + UpdateExtendedDepotReservation(t, true); v->UpdateViewport(true, true); SetWindowClassesDirty(WC_TRAINS_LIST); SetWindowDirty(WC_VEHICLE_VIEW, v->index); @@ -2482,6 +2483,7 @@ static bool CheckTrainStayInDepot(Train *v) v->UpdateAcceleration(); ProcessOrders(v); if (CheckReverseTrain(v)) ReverseTrainDirection(v); + UpdateExtendedDepotReservation(v, false); InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id); /* Check whether it is safe to exit the depot. */ @@ -3316,6 +3318,7 @@ uint Train::Crash(bool flooded) for (Train *v = this; v != nullptr; v = v->Next()) { v->track &= ~TRACK_BIT_DEPOT; } + UpdateExtendedDepotReservation(this, false); InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile)); } /* Remove reserved tracks of platform ahead. */ diff --git a/src/train_placement.cpp b/src/train_placement.cpp index 5b5be9f9d7080..0f05da5ebc016 100644 --- a/src/train_placement.cpp +++ b/src/train_placement.cpp @@ -185,6 +185,7 @@ void TrainPlacement::LiftTrain(Train *train, DoCommandFlag flags) if ((flags & DC_EXEC) == 0) return; SetPlatformReservation(train->tile, false); + UpdateExtendedDepotReservation(train, false); UpdateSignalsOnSegment(train->tile, INVALID_DIAGDIR, train->owner); } @@ -307,6 +308,7 @@ void TrainPlacement::PlaceTrain(Train *train, DoCommandFlag flags) } SetPlatformReservation(train->tile, true); + UpdateExtendedDepotReservation(train, true); UpdateSignalsOnSegment(train->tile, INVALID_DIAGDIR, train->owner); } From c212ca48b957d3fed66e11b4bc9b2ea1b833f40b Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Tue, 30 Mar 2021 12:11:10 +0200 Subject: [PATCH 069/143] Add: Set a darker background in depot window for trains that cannot leave a depot. --- src/depot.cpp | 1 + src/depot_gui.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/depot.cpp b/src/depot.cpp index 6bf7c8f99d03a..91f36f860d9ee 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -199,6 +199,7 @@ void Depot::AfterAddRemove(TileArea ta, bool adding) this->RescanDepotTiles(); assert(!this->depot_tiles.empty()); this->xy = this->depot_tiles[0]; + InvalidateWindowData(WC_VEHICLE_DEPOT, this->index); } else { assert(this->IsInUse()); this->Disuse(); diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index f7fd753bab568..d31eb47d8a59c 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -34,6 +34,7 @@ #include "vehicle_cmd.h" #include "core/geometry_func.hpp" #include "depot_func.h" +#include "train_placement.h" #include "widgets/depot_widget.h" @@ -262,6 +263,7 @@ struct DepotWindow : Window { VehicleType type; bool generate_list; WidgetID hovered_widget; ///< Index of the widget being hovered during drag/drop. -1 if no drag is in progress. + std::vector problematic_vehicles; ///< Vector associated to vehicle_list, with a value of true for vehicles that cannot leave the depot. VehicleList vehicle_list; VehicleList wagon_list; uint unitnumber_digits; @@ -411,6 +413,11 @@ struct DepotWindow : Window { for (; num < maxval; ir = ir.Translate(0, this->resize.step_height)) { // Draw the rows Rect cell = ir; /* Keep track of horizontal cells */ for (uint i = 0; i < this->num_columns && num < maxval; i++, num++) { + /* Draw a dark red background if train cannot be placed. */ + if (this->type == VEH_TRAIN && this->problematic_vehicles[num] == 1) { + GfxFillRect(cell.left, cell.top, cell.right, cell.bottom, PC_DARK_GREY); + } + /* Draw all vehicles in the current row */ const Vehicle *v = this->vehicle_list[num]; this->DrawVehicleInDepot(v, cell); @@ -723,6 +730,14 @@ struct DepotWindow : Window { BuildDepotVehicleList(this->type, this->window_number, &this->vehicle_list, &this->wagon_list); this->generate_list = false; DepotSortList(&this->vehicle_list); + if (this->type == VEH_TRAIN) { + this->problematic_vehicles.clear(); + TrainPlacement tp; + for (uint num = 0; num < this->vehicle_list.size(); ++num) { + const Vehicle *v = this->vehicle_list[num]; + this->problematic_vehicles.push_back(!tp.CanFindAppropriatePlatform(Train::From(v), false)); + } + } uint new_unitnumber_digits = GetUnitNumberDigits(this->vehicle_list); /* Only increase the size; do not decrease to prevent constant changes */ From 91d529024260457cbdb0fde1270f8e54783b732a Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 15 May 2021 16:32:01 +0200 Subject: [PATCH 070/143] Add: Add road depot platforms. --- src/platform.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++-- src/platform_func.h | 38 +++++++++++++++++++++++-- src/platform_type.h | 1 + 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/src/platform.cpp b/src/platform.cpp index 6878ad4344f45..55e0871d62e1b 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -183,6 +183,39 @@ uint GetRailDepotPlatformLength(TileIndex tile) return len - 1; } +/** + * Get the length of a road depot platform. + * @pre IsDepotTypeTile(tile, TRANSPORT_ROAD) + * @param tile Tile to check + * @param rtt Whether to check for road or tram type. + * @return The length of the platform in tile length. + */ +uint GetRoadDepotPlatformLength(TileIndex tile, RoadTramType rtt) +{ + assert(IsExtendedRoadDepotTile(tile)); + + DiagDirection dir = GetRoadDepotDirection(tile); + TileIndexDiff delta = TileOffsByDiagDir(dir); + + TileIndex t = tile; + uint len = 0; + do { + len++; + if ((GetRoadBits(t, rtt) & DiagDirToRoadBits(dir)) == ROAD_NONE) break; + t -= delta; + } while (IsCompatibleRoadDepotTile(t, tile, rtt)); + + t = tile; + dir = ReverseDiagDir(dir); + do { + len++; + if ((GetRoadBits(t, rtt) & DiagDirToRoadBits(dir)) == ROAD_NONE) break; + t += delta; + } while (IsCompatibleRoadDepotTile(t, tile, rtt)); + + return len - 1; +} + /** * Get the length of a rail depot platform in a given direction. @@ -206,12 +239,37 @@ uint GetRailDepotPlatformLength(TileIndex tile, DiagDirection dir) return length; } +/** + * Get the length of a road depot platform in a given direction. + * @pre IsRoadDepotTile(tile) + * @param tile Tile to check + * @param dir Direction to check + * @param rtt Whether to check for road or tram type. + * @return The length of the platform in tile length in the given direction. + */ +uint GetRoadDepotPlatformLength(TileIndex tile, DiagDirection dir, RoadTramType rtt) +{ + TileIndex start_tile = tile; + uint length = 0; + assert(IsExtendedRoadDepotTile(tile)); + assert(dir < DIAGDIR_END); + + do { + length++; + if ((GetRoadBits(tile, rtt) & DiagDirToRoadBits(dir)) == ROAD_NONE) break; + tile += TileOffsByDiagDir(dir); + } while (IsCompatibleRoadDepotTile(tile, start_tile, rtt)); + + return length; +} + /** * Get the length of a platform. * @param tile Tile to check + * @param rtt Whether to check for road or tram type (only for road transport). * @return The length of the platform in tile length. */ -uint GetPlatformLength(TileIndex tile) +uint GetPlatformLength(TileIndex tile, RoadTramType rtt) { switch (GetPlatformType(tile)) { case PT_RAIL_STATION: @@ -220,6 +278,8 @@ uint GetPlatformLength(TileIndex tile) return 1; case PT_RAIL_DEPOT: return GetRailDepotPlatformLength(tile); + case PT_ROAD_DEPOT: + return GetRoadDepotPlatformLength(tile, rtt); default: NOT_REACHED(); } } @@ -229,9 +289,10 @@ uint GetPlatformLength(TileIndex tile) * @pre IsRailDepotTile(tile) * @param tile Tile to check * @param dir Direction to check + * @param rtt Whether to check for road or tram type (only for road transport). * @return The length of the platform in tile length in the given direction. */ -uint GetPlatformLength(TileIndex tile, DiagDirection dir) +uint GetPlatformLength(TileIndex tile, DiagDirection dir, RoadTramType rtt) { switch (GetPlatformType(tile)) { case PT_RAIL_STATION: @@ -240,6 +301,8 @@ uint GetPlatformLength(TileIndex tile, DiagDirection dir) return 1; case PT_RAIL_DEPOT: return GetRailDepotPlatformLength(tile, dir); + case PT_ROAD_DEPOT: + return GetRoadDepotPlatformLength(tile, dir, rtt); default: NOT_REACHED(); } } diff --git a/src/platform_func.h b/src/platform_func.h index 30a2dae28033d..baa59128741b8 100644 --- a/src/platform_func.h +++ b/src/platform_func.h @@ -13,6 +13,7 @@ #include "station_map.h" #include "depot_map.h" #include "platform_type.h" +#include "road_map.h" /** * Check if a tile is a valid continuation to a railstation tile. @@ -57,6 +58,31 @@ static inline bool IsCompatibleTrainDepotTile(TileIndex test_tile, TileIndex dep GetDepotIndex(test_tile) == GetDepotIndex(depot_tile); } +/** + * Check if a tile is a valid continuation to an extended road depot tile. + * The tile \a test_tile is a valid continuation to \a depot_tile, if all of the following are true: + * \li \a test_tile is an extended depot tile + * \li \a test_tile and \a depot_tile have the same road type and appropriate road bits + * \li the tracks on \a test_tile and \a depot_tile are in the same direction + * \li both tiles belong to the same depot + * @param test_tile Tile to test + * @param depot_tile Depot tile to compare with + * @param rtt Whether road or tram type. + * @pre IsExtendedRoadDepotTile(depot_tile) + * @return true if the two tiles are compatible + */ +static inline bool IsCompatibleRoadDepotTile(TileIndex test_tile, TileIndex depot_tile, RoadTramType rtt) +{ + assert(IsExtendedRoadDepotTile(depot_tile)); + if (!IsExtendedRoadDepotTile(test_tile)) return false; + if (GetDepotIndex(test_tile) != GetDepotIndex(depot_tile)) return false; + if (GetRoadType(depot_tile, rtt) != GetRoadType(test_tile, rtt)) return false; + + DiagDirection dir = DiagdirBetweenTiles(test_tile, depot_tile); + assert(dir != INVALID_DIAGDIR); + return (GetRoadBits(test_tile, rtt) & DiagDirToRoadBits(dir)) != ROAD_NONE; +} + /** * Returns the type of platform of a given tile. * @param tile Tile to check @@ -72,6 +98,9 @@ static inline PlatformType GetPlatformType(TileIndex tile) case MP_RAILWAY: if (IsExtendedRailDepotTile(tile)) return PT_RAIL_DEPOT; break; + case MP_ROAD: + if (IsExtendedRoadDepotTile(tile)) return PT_ROAD_DEPOT; + break; default: break; } @@ -110,9 +139,10 @@ static inline bool HasPlatformReservation(TileIndex tile) * platform type and (depending on the platform type) its railtype or other specs. * @param test_tile the tile to check * @param orig_tile the tile with the platform type we are interested in + * @param rtt Whether to check road or tram types (only for road transport); * @return whether the two tiles are compatible tiles for defining a platform */ -static inline bool IsCompatiblePlatformTile(TileIndex test_tile, TileIndex orig_tile) +static inline bool IsCompatiblePlatformTile(TileIndex test_tile, TileIndex orig_tile, RoadTramType rtt = RTT_ROAD) { switch (GetPlatformType(orig_tile)) { case PT_RAIL_STATION: @@ -121,6 +151,8 @@ static inline bool IsCompatiblePlatformTile(TileIndex test_tile, TileIndex orig_ return test_tile == orig_tile; case PT_RAIL_DEPOT: return IsCompatibleTrainDepotTile(test_tile, orig_tile); + case PT_ROAD_DEPOT: + return IsCompatibleRoadDepotTile(test_tile, orig_tile, rtt); default: NOT_REACHED(); } } @@ -128,8 +160,8 @@ static inline bool IsCompatiblePlatformTile(TileIndex test_tile, TileIndex orig_ void SetPlatformReservation(TileIndex start, DiagDirection dir, bool b); void SetPlatformReservation(TileIndex start, bool b); -uint GetPlatformLength(TileIndex tile); -uint GetPlatformLength(TileIndex tile, DiagDirection dir); +uint GetPlatformLength(TileIndex tile, RoadTramType rtt = RTT_ROAD); +uint GetPlatformLength(TileIndex tile, DiagDirection dir, RoadTramType rtt = RTT_ROAD); TileIndex GetPlatformExtremeTile(TileIndex tile, DiagDirection dir); TileArea GetPlatformTileArea(TileIndex tile); diff --git a/src/platform_type.h b/src/platform_type.h index 27765d92c78d1..e6c543465a91d 100644 --- a/src/platform_type.h +++ b/src/platform_type.h @@ -16,6 +16,7 @@ enum PlatformType { PT_RAIL_STATION, PT_RAIL_WAYPOINT, PT_RAIL_DEPOT, + PT_ROAD_DEPOT, PT_END, INVALID_PLATFORM_TYPE = PT_END, }; From 8feaa3f8178a40bbaa78a662e1a96f69e5f78fc0 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 19:54:33 +0200 Subject: [PATCH 071/143] Feature: Add construction of extended road depots. --- regression/regression/result.txt | 6 +- src/depot.cpp | 35 +++- src/economy_type.h | 2 - src/lang/english.txt | 9 +- src/road_cmd.cpp | 282 ++++++++++++++++++++++++------- src/road_cmd.h | 4 +- src/road_gui.cpp | 155 ++++++++++++++--- src/road_map.cpp | 8 +- src/road_map.h | 52 ++++-- src/saveload/afterload.cpp | 9 + src/saveload/company_sl.cpp | 4 +- src/script/api/script_road.cpp | 2 +- src/town_cmd.cpp | 2 +- src/widgets/road_widget.h | 1 + 14 files changed, 458 insertions(+), 113 deletions(-) diff --git a/regression/regression/result.txt b/regression/regression/result.txt index 27a81675a9d9d..7aaf66b68a5d5 100644 --- a/regression/regression/result.txt +++ b/regression/regression/result.txt @@ -7364,7 +7364,7 @@ ERROR: IsEnd() is invalid as Begin() is never called IsBuoyTile(): false IsLockTile(): false IsCanalTile(): false - GetBankBalance(): 1999979304 + GetBankBalance(): 1999979244 BuildWaterDepot(): true BuildDock(): true BuildBuoy(): true @@ -7377,7 +7377,7 @@ ERROR: IsEnd() is invalid as Begin() is never called IsBuoyTile(): true IsLockTile(): true IsCanalTile(): true - GetBankBalance(): 1999964680 + GetBankBalance(): 1999964620 --AIWaypointList(BUOY)-- Count(): 1 @@ -7396,7 +7396,7 @@ ERROR: IsEnd() is invalid as Begin() is never called IsBuoyTile(): false IsLockTile(): false IsCanalTile(): false - GetBankBalance(): 1999959285 + GetBankBalance(): 1999959225 BuildWaterDepot(): true BuildDock(): true BuildBuoy(): true diff --git a/src/depot.cpp b/src/depot.cpp index 91f36f860d9ee..15145c104a81f 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -282,12 +282,41 @@ void UpdateExtendedDepotReservation(Vehicle *v, bool reserve) assert(IsExtendedDepotTile(v->tile)); DepotReservation res_type = DEPOT_RESERVATION_EMPTY; - res_type = (v->vehstatus & VS_STOPPED) ? - DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + if (reserve) { + res_type = (v->vehstatus & VS_STOPPED) ? + DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + } switch (v->type) { - case VEH_ROAD: + case VEH_ROAD: { + assert(v == v->First()); + assert(IsDiagonalDirection(v->direction)); + bool is_facing_south = IsDiagDirFacingSouth(DirToDiagDir(v->direction)); + TileArea ta; + for (Vehicle *u = v; u != nullptr; u = u->Next()) ta.Add(u->tile); + for (TileIndex t : ta) { + res_type = DEPOT_RESERVATION_EMPTY; + + if (reserve) { + res_type = (v->vehstatus & VS_STOPPED) ? + DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + } + for (Vehicle *rv : Vehicle::Iterate()) { + if (res_type == DEPOT_RESERVATION_FULL_STOPPED_VEH) break; + if (rv->IsInDepot() && ta.Contains(rv->tile) && rv->First() != v) { + assert(rv->type == v->type); + [[maybe_unused]] DiagDirection diag_dir = DirToDiagDir(rv->direction); + assert(DiagDirToAxis(DirToDiagDir(v->direction)) == DiagDirToAxis(diag_dir)); + if (is_facing_south == IsDiagDirFacingSouth(DirToDiagDir(rv->direction))) { + res_type = (rv->vehstatus & VS_STOPPED) ? + DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + } + } + } + SetDepotReservation(t, res_type, is_facing_south); + } break; + } case VEH_SHIP: SetDepotReservation(v->tile, res_type); diff --git a/src/economy_type.h b/src/economy_type.h index e4d0ea077acf3..a4c08c4919463 100644 --- a/src/economy_type.h +++ b/src/economy_type.h @@ -241,8 +241,6 @@ static const int INVALID_PRICE_MODIFIER = MIN_PRICE_MODIFIER - 1; static const uint TUNNELBRIDGE_TRACKBIT_FACTOR = 4; /** Multiplier for how many regular track bits a level crossing counts. */ static const uint LEVELCROSSING_TRACKBIT_FACTOR = 2; -/** Multiplier for how many regular track bits a road depot counts. */ -static const uint ROAD_DEPOT_TRACKBIT_FACTOR = 2; /** Multiplier for how many regular track bits a bay stop counts. */ static const uint ROAD_STOP_TRACKBIT_FACTOR = 2; /** Multiplier for how many regular tiles a lock counts. */ diff --git a/src/lang/english.txt b/src/lang/english.txt index c78501d545cf5..f6ec5c41b52d6 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2919,8 +2919,10 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION :{BLACK}Build ro STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION :{BLACK}Build tramway section. Ctrl+Click to remove tramway section. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}Build road section using the Autoroad mode. Ctrl+Click to remove road section. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM :{BLACK}Build tramway section using the Autotram mode. Ctrl+Click to remove tramway section. Also press Shift to show cost estimate only -STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build road vehicle depot (for buying and servicing vehicles). Also press Shift to show cost estimate only -STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Build tram vehicle depot (for buying and servicing vehicles). Also press Shift to show cost estimate only +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build standard road vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_ROAD_VEHICLE_DEPOT :{BLACK}Build extended road vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Build standard tram vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAM_VEHICLE_DEPOT :{BLACK}Build extended tram vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT :{BLACK}Build waypoint on road. Ctrl+Click to select another waypoint to join. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT :{BLACK}Build waypoint on tramway. Ctrl+Click to select another waypoint to join. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}Build bus station. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only @@ -3181,7 +3183,8 @@ STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT_EXTENDED :Extended railwa STR_LAI_ROAD_DESCRIPTION_ROAD :Road STR_LAI_ROAD_DESCRIPTION_ROAD_WITH_STREETLIGHTS :Road with street lights STR_LAI_ROAD_DESCRIPTION_TREE_LINED_ROAD :Tree-lined road -STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT :Road vehicle depot +STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT :Standard road vehicle depot +STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT_EXTENDED :Extended road vehicle depot STR_LAI_ROAD_DESCRIPTION_ROAD_RAIL_LEVEL_CROSSING :Road/rail level crossing STR_LAI_ROAD_DESCRIPTION_TRAMWAY :Tramway diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 10f96de6cfa3d..3806b4e0658b7 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -313,6 +313,22 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R return CommandCost(); } +void UpdateRoadDepotDir(TileIndex tile) +{ + assert(IsExtendedRoadDepot(tile)); + RoadBits rb = GetAllRoadBits(tile); + DiagDirection dir = DIAGDIR_NE; + if (rb & ROAD_SE) { + dir = DIAGDIR_SE; + } else if (rb & ROAD_SW) { + dir = DIAGDIR_SW; + } else if (rb & ROAD_NW) { + dir = DIAGDIR_NW; + } else { + assert(rb & ROAD_NE); + } + SetRoadDepotDirection(tile, dir); +} /** * Delete a piece of road. @@ -525,16 +541,29 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec } case ROAD_TILE_DEPOT: { - if (!HasRoadTypeRoad(tile) || !HasRoadTypeTram(tile)) return CMD_ERROR; - if (flags & DC_EXEC) { + /* Depot must have at least one road bit. */ + RoadBits new_rb = (GetRoadBits(tile, rtt) & ~pieces); + if (new_rb == ROAD_NONE && GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) return CMD_ERROR; + + uint num_removed_bits = CountBits(pieces & GetRoadBits(tile, rtt)); + CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD] * num_removed_bits); + + if ((flags & DC_EXEC) && num_removed_bits != 0) { + SetRoadBits(tile, new_rb, rtt); + Company *c = Company::GetIfValid(GetTileOwner(tile)); - c->infrastructure.road[GetRoadType(tile, rtt)] -= ROAD_DEPOT_TRACKBIT_FACTOR; + c->infrastructure.road[GetRoadType(tile, rtt)] -= num_removed_bits; DirtyCompanyInfrastructureWindows(c->index); - SetRoadType(tile, rtt, INVALID_ROADTYPE); - Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), false); + + if (new_rb == ROAD_NONE) { + SetRoadType(tile, rtt, INVALID_ROADTYPE); + Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), false); + } + + if (IsExtendedRoadDepot(tile)) UpdateRoadDepotDir(tile); MarkTileDirtyByTile(tile); } - return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); + return cost; } default: NOT_REACHED(); @@ -722,8 +751,24 @@ CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, R if (HasTileRoadType(tile, rtt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); break; - case ROAD_TILE_DEPOT: - if (DiagDirToRoadBits(GetRoadDepotDirection(tile)) == pieces) { + case ROAD_TILE_DEPOT: { + Owner owner = GetRoadOwner(tile, rtt); + if (owner != OWNER_NONE) { + CommandCost ret = CheckOwnership(owner, tile); + if (ret.Failed()) return ret; + } + + if (IsExtendedRoadDepot(tile)) { + RoadType tile_rt = GetRoadType(tile, rtt); + if (tile_rt != INVALID_ROADTYPE && rt != tile_rt) return CMD_ERROR; + Axis axis = DiagDirToAxis(GetRoadDepotDirection(tile)); + RoadBits rb = (axis == AXIS_X ? ROAD_X : ROAD_Y) & pieces; + if (rb != pieces) return CMD_ERROR; + existing = GetRoadBits(tile, rtt); + if ((rb & ~existing) == ROAD_NONE) return_cmd_error(STR_ERROR_ALREADY_BUILT); + cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * CountBits(rb & ~existing)); + break; + } else if (GetRoadBits(tile, OtherRoadTramType(rtt)) == pieces) { /* Check if we can add a new road/tram type if none present. */ if (HasTileRoadType(tile, rtt)) { return_cmd_error(STR_ERROR_ALREADY_BUILT); @@ -732,6 +777,7 @@ CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, R cost.AddCost(_price[PR_BUILD_DEPOT_ROAD]); break; } + } goto do_clear; @@ -908,7 +954,13 @@ do_clear:; RoadTileType rttype = GetRoadTileType(tile); if (rttype == ROAD_TILE_DEPOT) { SetRoadType(tile, rtt, rt); - UpdateCompanyRoadInfrastructure(rt, _current_company, ROAD_DEPOT_TRACKBIT_FACTOR); + if (IsExtendedRoadDepot(tile)) { + SetRoadBits(tile, pieces | GetRoadBits(tile, rtt), rtt); + /* Do not add or remove to company infrastructure for depots. Already acounted for. */ + UpdateRoadDepotDir(tile); + } else { + SetRoadBits(tile, GetRoadBits(tile, OtherRoadTramType(rtt)), rtt); + } Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), true); break; } else if (existing == ROAD_NONE || rttype == ROAD_TILE_CROSSING) { @@ -1165,8 +1217,11 @@ std::tuple CmdRemoveLongRoad(DoCommandFlag flags, TileIndex * @param tile tile where to build the depot * @param flags operation to perform * @param rt road type - * @param dir entrance direction + * @param orig_dir entrance direction * @param adjacent allow adjacent depots + * @param extended build extended depot + * @param half_start build only one trackbit in start tile if building an extended depot + * @param half_end build only one trackbit in end tile if building an extended depot * @param depot_id depot to join to * @param end_tile end tile of the depot to be built * @return the cost of this operation or an error @@ -1174,12 +1229,36 @@ std::tuple CmdRemoveLongRoad(DoCommandFlag flags, TileIndex * @todo When checking for the tile slope, * distinguish between "Flat land required" and "land sloped in wrong direction" */ -CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile) +CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection orig_dir, bool adjacent, bool extended, bool half_start, bool half_end, DepotID join_to, TileIndex end_tile) { - if (!ValParamRoadType(rt) || !IsValidDiagDirection(dir)) return CMD_ERROR; + if (!ValParamRoadType(rt) || !IsValidDiagDirection(orig_dir)) return CMD_ERROR; + + if (Company::IsValidHumanID(_current_company) && !HasBit(_settings_game.depot.road_depot_types, extended)) { + return_cmd_error(STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE); + } TileArea ta(tile, end_tile); - assert(ta.w == 1 || ta.h == 1); + assert(extended || ta.w == 1 || ta.h == 1); + + Axis axis = DiagDirToAxis(orig_dir); + RoadTramType rtt = GetRoadTramType(rt); + uint start_coord = 0; + uint end_coord = 0; + + DiagDirection dir = orig_dir; + if (extended) { + start_coord = axis == AXIS_X ? TileX(tile) : TileY(tile); + end_coord = axis == AXIS_X ? TileX(end_tile) : TileY(end_tile); + + dir = AxisToDiagDir(axis); + + /* Swap direction, also the half-tile drag var (bit 0 and 1) */ + if (start_coord > end_coord || start_coord == end_coord) { + dir = ReverseDiagDir(dir); + half_start = !half_start; + half_end = !half_end; + } + } /* Create a new depot or find a depot to join to. */ Depot *depot = nullptr; @@ -1187,58 +1266,135 @@ CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, if (ret.Failed()) return ret; uint8_t num_new_depot_tiles = 0; - uint8_t num_rotated_depot_tiles = 0; + uint8_t num_overbuilt_depot_tiles = 0; CommandCost cost(EXPENSES_CONSTRUCTION); + int allowed_z = -1; + uint num_new_pieces = 0; + uint invalid_dirs = extended ? 5 << axis : 1 << dir; for (Tile t : ta) { + RoadBits rb = ROAD_NONE; if (IsBridgeAbove(t)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); - Slope tileh = GetTileSlope(t); + auto [tileh, z] = GetTileSlopeZ(t); + int flat_z = z + GetSlopeMaxZ(tileh); + if (tileh != SLOPE_FLAT) { if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } + + if (extended && IsSteepSlope(tileh)) return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + /* Forbid building if the tile faces a slope in a invalid direction. */ + for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) { + if (HasBit(invalid_dirs, dir) && !CanBuildDepotByTileh(dir, tileh)) { + return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + } + } + cost.AddCost(_price[PR_BUILD_FOUNDATION]); } - /* Check whether a depot tile exists and it needs to be rotated. */ - if (IsRoadDepotTile(t) && - GetDepotIndex(t) == join_to && - (HasRoadTypeTram(t) ? rt == GetRoadTypeTram(t) : rt == GetRoadTypeRoad(t))) { - if (dir == GetRoadDepotDirection(t)) continue; + /* The level of this tile must be equal to allowed_z. */ + if (allowed_z < 0) { + /* First tile. */ + allowed_z = flat_z; + } else if (allowed_z != flat_z) { + return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + } - ret = EnsureNoVehicleOnGround(t); - if (ret.Failed()) return ret; + /* Check whether this is a compatible depot tile. */ + if (IsRoadDepotTile(t) && GetDepotIndex(t) == join_to && rt == GetRoadType(t, rtt)) { + if (extended) { + if (IsExtendedRoadDepotTile(t) && + axis == DiagDirToAxis(GetRoadDepotDirection(t))) { + /* Already exists and has the right axis: Check new roadbits. */ + goto rb_for_extended_depot; + } + } else { + if (!IsExtendedRoadDepotTile(t)) { + if (dir == GetRoadDepotDirection(t)) continue; - num_rotated_depot_tiles++; - if (flags & DC_EXEC) { - SetRoadDepotExitDirection(t, dir); - MarkTileDirtyByTile(t); + /* If another roadtype exists (road/tram), depot cannot be rotated. */ + if (GetRoadTypeRoad(t) != INVALID_ROADTYPE && GetRoadTypeTram(t) != INVALID_ROADTYPE) { + return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED); + } + + /* Overbuild the depot tile and change its exit direction. */ + num_overbuilt_depot_tiles++; + if (flags & DC_EXEC) { + rb = DiagDirToRoadBits(orig_dir); + SetRoadBits(t, rb, rtt); + SetRoadDepotDirection(t, orig_dir); + MarkTileDirtyByTile(t); + } + continue; + } } + } - } else { - cost.AddCost(Command::Do(flags, t)); - if (cost.Failed()) return cost; + cost.AddCost(Command::Do(flags, t)); + if (cost.Failed()) return cost; + /* Check which road bits to build. */ + if (extended) { +rb_for_extended_depot: + uint axis_coord = axis == AXIS_X ? TileX(t) : TileY(t); + /* Road parts only have to be built at the start tile or at the end tile. */ + if (!half_end && axis_coord == end_coord) { + rb = DiagDirToRoadBits(ReverseDiagDir(dir)); + } + if (half_start && axis_coord == start_coord) { + rb = DiagDirToRoadBits(dir); + } + if (rb == ROAD_NONE) { + rb = AxisToRoadBits(axis); + } + assert(rb != ROAD_NONE); + if (IsRoadDepotTile(t)) { + RoadType old_rt = GetRoadType(t, rtt); + if (old_rt != INVALID_ROADTYPE && old_rt != rt) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED); + RoadBits old_rb = GetAllRoadBits(t); + if ((old_rb & AxisToRoadBits(axis)) != old_rb) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED); + old_rb = GetRoadBits(t, rtt); + if ((rb & ~old_rb) == ROAD_NONE) return_cmd_error(STR_ERROR_ALREADY_BUILT); + num_new_pieces += CountBits(rb & ~old_rb); + num_overbuilt_depot_tiles++; + rb |= old_rb; + } else { + num_new_pieces += CountBits(rb); + num_new_depot_tiles++; + } + } else { + rb = DiagDirToRoadBits(orig_dir); + num_new_pieces += 1; num_new_depot_tiles++; - if (flags & DC_EXEC) { - MakeRoadDepot(t, _current_company, depot->index, dir, rt); - MarkTileDirtyByTile(t); + } + + if (flags & DC_EXEC) { + if (!IsRoadDepotTile(t)) MakeRoadDepot(t, _current_company, depot->index, orig_dir, rt); + if (GetRoadType(t, rtt) == INVALID_ROADTYPE) SetRoadType(t, rtt, rt); + SetRoadBits(t, rb, rtt); + if (extended) { + SB(t.m5(), 5, 1, true); + UpdateRoadDepotDir(t); } + + MarkTileDirtyByTile(t); } } - if (num_new_depot_tiles + num_rotated_depot_tiles == 0) return CommandCost(); + if (num_new_depot_tiles + num_overbuilt_depot_tiles == 0) return CommandCost(); + + cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * (num_new_depot_tiles + num_overbuilt_depot_tiles)); if (flags & DC_EXEC) { - /* A road depot has two road bits. */ - UpdateCompanyRoadInfrastructure(rt, _current_company, num_new_depot_tiles * ROAD_DEPOT_TRACKBIT_FACTOR); + UpdateCompanyRoadInfrastructure(rt, _current_company, num_new_pieces); depot->AfterAddRemove(ta, true); if (join_to == NEW_DEPOT) MakeDefaultName(depot); } - cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * (num_new_depot_tiles + num_rotated_depot_tiles)); return cost; } @@ -1255,16 +1411,16 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags) CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); RoadType rt = GetRoadTypeRoad(tile); RoadType tt = GetRoadTypeTram(tile); - if (rt != INVALID_ROADTYPE && tt != INVALID_ROADTYPE) cost.AddCost(_price[PR_CLEAR_DEPOT_ROAD]); + if (rt != INVALID_ROADTYPE) cost.AddCost(_price[PR_CLEAR_DEPOT_ROAD]); + if (tt != INVALID_ROADTYPE) cost.AddCost(_price[PR_CLEAR_DEPOT_ROAD]); if (flags & DC_EXEC) { Depot *depot = Depot::GetByTile(tile); Company *c = Company::GetIfValid(depot->owner); if (c != nullptr) { - /* A road depot has two road bits. */ - RoadType rt = GetRoadTypeRoad(tile); - if (rt != INVALID_ROADTYPE) c->infrastructure.road[rt] -= ROAD_DEPOT_TRACKBIT_FACTOR; - if (tt != INVALID_ROADTYPE) c->infrastructure.road[tt] -= ROAD_DEPOT_TRACKBIT_FACTOR; + /* A road depot has two road types. */ + if (rt != INVALID_ROADTYPE) c->infrastructure.road[rt] -= CountBits(GetRoadBits(tile, RTT_ROAD)); + if (tt != INVALID_ROADTYPE) c->infrastructure.road[tt] -= CountBits(GetRoadBits(tile, RTT_TRAM)); DirtyCompanyInfrastructureWindows(c->index); } @@ -1500,18 +1656,13 @@ void DrawRoadCatenary(const TileInfo *ti) if (IsTileType(ti->tile, MP_ROAD)) { switch (GetRoadTileType(ti->tile)) { case ROAD_TILE_NORMAL: + case ROAD_TILE_DEPOT: road = GetRoadBits(ti->tile, RTT_ROAD); tram = GetRoadBits(ti->tile, RTT_TRAM); break; case ROAD_TILE_CROSSING: tram = road = (GetCrossingRailAxis(ti->tile) == AXIS_Y ? ROAD_X : ROAD_Y); break; - case ROAD_TILE_DEPOT: { - DiagDirection dir = GetRoadDepotDirection(ti->tile); - road = DiagDirToRoadBits(dir); - tram = DiagDirToRoadBits(dir); - break; - } default: NOT_REACHED(); } } else if (IsTileType(ti->tile, MP_STATION)) { @@ -1883,13 +2034,15 @@ static void DrawTile_Road(TileInfo *ti) const RoadTypeInfo *main_rti = tram_rti != nullptr ? tram_rti : road_rti; DiagDirection dir = GetRoadDepotDirection(ti->tile); - uint road_offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); - uint tram_offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); + uint road_offset = GetRoadSpriteOffset(SLOPE_FLAT, GetRoadBits(ti->tile, RTT_ROAD)); + uint tram_offset = GetRoadSpriteOffset(SLOPE_FLAT, GetRoadBits(ti->tile, RTT_TRAM)); PaletteID pal = PAL_NONE; const DrawTileSprites *dts = &_road_depot[dir]; - DrawGroundSprite(dts->ground.sprite, pal); + SpriteID image = SPR_ROAD_Y + (road_rti == nullptr ? tram_offset : road_offset) - 19; + DrawGroundSprite(image, pal); + DrawRoadOverlays(ti, pal, road_rti, tram_rti, road_offset, tram_offset); int relocation = GetCustomRoadSprite(main_rti, ti->tile, ROTSG_DEPOT); @@ -1962,6 +2115,8 @@ void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt) } else if (rtt == RTT_TRAM) { DrawSprite(SPR_TRAMWAY_TRAM + road_offset, PAL_NONE, x, y); DrawSprite(SPR_TRAMWAY_OVERLAY + road_offset, PAL_NONE, x, y); + } else { + DrawSprite(SPR_ROAD_Y + road_offset - 19, PAL_NONE, x, y); } if (default_gfx) { @@ -2220,11 +2375,11 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u default: case ROAD_TILE_DEPOT: { - DiagDirection dir = GetRoadDepotDirection(tile); + Axis axis = DiagDirToAxis(GetRoadDepotDirection(tile)); - if (side != INVALID_DIAGDIR && side != dir) break; + if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break; - trackdirbits = TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir)); + trackdirbits = TrackBitsToTrackdirBits(AxisToTrackBits(axis)); break; } } @@ -2281,7 +2436,7 @@ static void GetTileDesc_Road(TileIndex tile, TileDesc *td) } case ROAD_TILE_DEPOT: - td->str = STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT; + td->str = IsExtendedDepot(tile) ? STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT_EXTENDED : STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT; td->build_date = Depot::GetByTile(tile)->build_date; break; @@ -2349,14 +2504,14 @@ static void ChangeTileOwner_Road(TileIndex tile, Owner old_owner, Owner new_owne if (new_owner == INVALID_OWNER) { Command::Do(DC_EXEC | DC_BANKRUPT, tile); } else { - /* A road depot has two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */ - RoadType rt = GetRoadTypeRoad(tile); - if (rt == INVALID_ROADTYPE) rt = GetRoadTypeTram(tile); - Company::Get(old_owner)->infrastructure.road[rt] -= 2; - Company::Get(new_owner)->infrastructure.road[rt] += 2; - SetTileOwner(tile, new_owner); for (RoadTramType rtt : _roadtramtypes) { + RoadType rt = GetRoadTypeRoad(tile); + if (rt != INVALID_ROADTYPE) { + uint pieces = CountBits(GetRoadBits(tile, rtt)); + Company::Get(old_owner)->infrastructure.road[rt] -= pieces; + Company::Get(new_owner)->infrastructure.road[rt] += pieces; + } if (GetRoadOwner(tile, rtt) == old_owner) { SetRoadOwner(tile, rtt, new_owner); } @@ -2405,7 +2560,10 @@ static CommandCost TerraformTile_Road(TileIndex tile, DoCommandFlag flags, int z break; case ROAD_TILE_DEPOT: - if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRoadDepotDirection(tile))) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]); + if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRoadDepotDirection(tile)) && + (!IsExtendedRoadDepot(tile) || AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(GetRoadDepotDirection(tile))))) { + return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]); + } break; case ROAD_TILE_NORMAL: { @@ -2582,8 +2740,6 @@ CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_s uint num_pieces = CountBits(GetAnyRoadBits(tile, rtt)); if (tt == MP_STATION && IsBayRoadStopTile(tile)) { num_pieces *= ROAD_STOP_TRACKBIT_FACTOR; - } else if (tt == MP_ROAD && IsRoadDepot(tile)) { - num_pieces *= ROAD_DEPOT_TRACKBIT_FACTOR; } found_convertible_road = true; diff --git a/src/road_cmd.h b/src/road_cmd.h index 2ae7e170e69a9..b7a54c5c4aa9a 100644 --- a/src/road_cmd.h +++ b/src/road_cmd.h @@ -23,7 +23,7 @@ void UpdateNearestTownForRoadTiles(bool invalidate); CommandCost CmdBuildLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, DisallowedRoadDirections drd, bool start_half, bool end_half, bool is_ai); std::tuple CmdRemoveLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, bool start_half, bool end_half); CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, RoadType rt, DisallowedRoadDirections toggle_drd, TownID town_id); -CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile); +CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, bool extended, bool half_start, bool half_end, DepotID join_to, TileIndex end_tile); CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RoadType to_type); DEF_CMD_TRAIT(CMD_BUILD_LONG_ROAD, CmdBuildLongRoad, CMD_AUTO | CMD_NO_WATER | CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION) @@ -34,7 +34,7 @@ DEF_CMD_TRAIT(CMD_CONVERT_ROAD, CmdConvertRoad, 0, CommandCallback CcPlaySound_CONSTRUCTION_OTHER; CommandCallback CcBuildRoadTunnel; -void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex start_tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile); +void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex start_tile, RoadType rt, DiagDirection dir, bool adjacent, bool extended, bool half_start, bool half_end, DepotID join_to, TileIndex end_tile); void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8_t width, uint8_t length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, RoadStopClassID spec_class, uint16_t spec_index, StationID, bool); #endif /* ROAD_CMD_H */ diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 53a71d0d2cbef..4b99c39f845d3 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -52,7 +52,7 @@ #include "safeguards.h" static void ShowRVStationPicker(Window *parent, RoadStopType rs); -static void ShowRoadDepotPicker(Window *parent); +static void ShowRoadDepotPicker(Window *parent, bool extended_depot); static void ShowBuildRoadWaypointPicker(Window *parent); static bool _remove_button_clicked; @@ -171,16 +171,45 @@ void ConnectRoadToStructure(TileIndex tile, DiagDirection direction) } } -void CcRoadDepot(Commands, const CommandCost &result, TileIndex start_tile, RoadType, DiagDirection dir, bool, DepotID, TileIndex end_tile) +void CcRoadDepot(Commands , const CommandCost &result, TileIndex start_tile, RoadType, DiagDirection orig_dir, bool, bool extended, bool half_start, bool half_end, DepotID, TileIndex end_tile) { if (result.Failed()) return; if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, start_tile); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); + Axis axis = DiagDirToAxis(orig_dir); + uint start_coord; + uint end_coord; + bool build_start = true; + bool build_end = false; + + DiagDirection dir = orig_dir; + if (extended) { + build_start = half_end; + build_end = !half_start; + start_coord = axis == AXIS_X ? TileX(start_tile) : TileY(start_tile); + end_coord = axis == AXIS_X ? TileX(end_tile) : TileY(end_tile); + + dir = AxisToDiagDir(axis); + + /* Swap direction, also the half-tile drag var (bit 0 and 1) */ + if (start_coord > end_coord || start_coord == end_coord) { + dir = ReverseDiagDir(dir); + build_start = !build_start; + build_end = !build_end; + } + } + + TileArea ta(start_tile, end_tile); for (TileIndex tile : ta) { - ConnectRoadToStructure(tile, dir); + if (build_start && !ta.Contains(tile + TileOffsByDiagDir(dir))) { + ConnectRoadToStructure(tile, dir); + } + if (build_end && !ta.Contains(tile + TileOffsByDiagDir(ReverseDiagDir(dir)))) { + ConnectRoadToStructure(tile, ReverseDiagDir(dir)); + } } } @@ -373,7 +402,13 @@ struct BuildRoadToolbarWindow : Window { { if (_game_mode == GM_NORMAL && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); - if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_ROT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + + if (_game_mode == GM_NORMAL && + ((this->HasWidget(WID_ROT_DEPOT) && this->IsWidgetLowered(WID_ROT_DEPOT)) || + (this->HasWidget(WID_ROT_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_ROT_EXTENDED_DEPOT)))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } + this->Window::Close(); } @@ -390,6 +425,7 @@ struct BuildRoadToolbarWindow : Window { bool can_build = CanBuildVehicleInfrastructure(VEH_ROAD, rtt); this->SetWidgetsDisabledState(!can_build, WID_ROT_DEPOT, + WID_ROT_EXTENDED_DEPOT, WID_ROT_BUILD_WAYPOINT, WID_ROT_BUS_STATION, WID_ROT_TRUCK_STATION); @@ -403,12 +439,14 @@ struct BuildRoadToolbarWindow : Window { if (_game_mode != GM_EDITOR) { if (!can_build) { /* Show in the tooltip why this button is disabled. */ - this->GetWidget(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); + if (this->HasWidget(WID_ROT_DEPOT)) this->GetWidget(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); + if (this->HasWidget(WID_ROT_EXTENDED_DEPOT)) this->GetWidget(WID_ROT_EXTENDED_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); this->GetWidget(WID_ROT_BUILD_WAYPOINT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); this->GetWidget(WID_ROT_BUS_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); this->GetWidget(WID_ROT_TRUCK_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); } else { - this->GetWidget(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT); + if (this->HasWidget(WID_ROT_DEPOT)) this->GetWidget(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT); + if (this->HasWidget(WID_ROT_EXTENDED_DEPOT)) this->GetWidget(WID_ROT_EXTENDED_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAM_VEHICLE_DEPOT); this->GetWidget(WID_ROT_BUILD_WAYPOINT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT : STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT); this->GetWidget(WID_ROT_BUS_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION); this->GetWidget(WID_ROT_TRUCK_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION); @@ -433,7 +471,8 @@ struct BuildRoadToolbarWindow : Window { this->GetWidget(WID_ROT_ROAD_Y)->widget_data = rti->gui_sprites.build_y_road; this->GetWidget(WID_ROT_AUTOROAD)->widget_data = rti->gui_sprites.auto_road; if (_game_mode != GM_EDITOR) { - this->GetWidget(WID_ROT_DEPOT)->widget_data = rti->gui_sprites.build_depot; + if (this->HasWidget(WID_ROT_DEPOT)) this->GetWidget(WID_ROT_DEPOT)->widget_data = rti->gui_sprites.build_depot; + if (this->HasWidget(WID_ROT_EXTENDED_DEPOT)) this->GetWidget(WID_ROT_EXTENDED_DEPOT)->widget_data = rti->gui_sprites.build_depot; } this->GetWidget(WID_ROT_CONVERT_ROAD)->widget_data = rti->gui_sprites.convert_road; this->GetWidget(WID_ROT_BUILD_TUNNEL)->widget_data = rti->gui_sprites.build_tunnel; @@ -544,8 +583,9 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_DEPOT: - if (HandlePlacePushButton(this, WID_ROT_DEPOT, this->rti->cursor.depot, HT_RECT)) { - ShowRoadDepotPicker(this); + case WID_ROT_EXTENDED_DEPOT: + if (HandlePlacePushButton(this, widget, this->rti->cursor.depot, HT_RECT)) { + ShowRoadDepotPicker(this, widget == WID_ROT_EXTENDED_DEPOT); this->last_started_action = widget; } break; @@ -642,11 +682,19 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_DEPOT: + case WID_ROT_EXTENDED_DEPOT: { CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD); + _place_road_dir = DiagDirToAxis(_road_depot_orientation); + _place_road_start_half_x = (_place_road_dir == AXIS_X) && (_tile_fract_coords.x >= 8); + _place_road_start_half_y = (_place_road_dir == AXIS_Y) && (_tile_fract_coords.y >= 8); + VpSetPlaceSizingLimit(_settings_game.depot.depot_spread); - VpStartPlaceSizing(tile, (DiagDirToAxis(_road_depot_orientation) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_DEPOT); + ViewportPlaceMethod vpm = VPM_X_AND_Y_LIMITED; + if (this->last_started_action == WID_ROT_DEPOT) vpm = (DiagDirToAxis(_road_depot_orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED; + VpStartPlaceSizing(tile, vpm, DDSP_BUILD_DEPOT); break; + } case WID_ROT_BUILD_WAYPOINT: PlaceRoad_Waypoint(tile); @@ -681,7 +729,11 @@ struct BuildRoadToolbarWindow : Window { { if (_game_mode != GM_EDITOR && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true); - if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_ROT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + if (_game_mode == GM_NORMAL && + ((this->HasWidget(WID_ROT_DEPOT) && this->IsWidgetLowered(WID_ROT_DEPOT)) || + (this->HasWidget(WID_ROT_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_ROT_EXTENDED_DEPOT)))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } this->RaiseButtons(); this->SetWidgetDisabledState(WID_ROT_REMOVE, true); @@ -730,7 +782,14 @@ struct BuildRoadToolbarWindow : Window { _place_road_dir = AXIS_Y; _place_road_end_half = pt.y & 8; } + break; + case DDSP_BUILD_DEPOT: + if (_place_road_dir == AXIS_X) { + _place_road_end_half = pt.x & 8; + } else { + _place_road_end_half = pt.y & 8; + } break; default: @@ -817,11 +876,14 @@ struct BuildRoadToolbarWindow : Window { break; case DDSP_BUILD_DEPOT: { - bool adjacent = _ctrl_pressed; StringID error_string = this->rti->strings.err_depot; + bool adjacent = _ctrl_pressed; + bool extended = last_started_action == WID_ROT_EXTENDED_DEPOT; + bool half_start = _place_road_start_half_x || _place_road_start_half_y; + bool half_end = _place_road_end_half; auto proc = [=](DepotID join_to) -> bool { - return Command::Post(error_string, CcRoadDepot, start_tile, _cur_roadtype, _road_depot_orientation, adjacent, join_to, end_tile); + return Command::Post(error_string, CcRoadDepot, start_tile, _cur_roadtype, _road_depot_orientation, adjacent, extended, half_start, half_end, join_to, end_tile); }; ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_ROAD); @@ -923,6 +985,27 @@ struct BuildRoadToolbarWindow : Window { }, TramToolbarGlobalHotkeys}; }; +/** + * Add the depot icons depending on availability of construction. + * @return Panel with road depot buttons. + */ +static std::unique_ptr MakeNWidgetRoadDepot() +{ + auto hor = std::make_unique(); + + if (HasBit(_settings_game.depot.road_depot_types, 0)) { + /* Add the widget for building standard road depots. */ + hor->Add(std::make_unique(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT, SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT)); + } + + if (HasBit(_settings_game.depot.road_depot_types, 1)) { + /* Add the widget for building extended road depots. */ + hor->Add(std::make_unique(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_EXTENDED_DEPOT, SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_ROAD_VEHICLE_DEPOT)); + } + + return hor; +} + static constexpr NWidgetPart _nested_build_road_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), @@ -938,8 +1021,7 @@ static constexpr NWidgetPart _nested_build_road_widgets[] = { SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOROAD, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), - NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT), - SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT), + NWidgetFunction(MakeNWidgetRoadDepot), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION), @@ -983,8 +1065,7 @@ static constexpr NWidgetPart _nested_build_tramway_widgets[] = { SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOTRAM, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), - NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT), - SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT), + NWidgetFunction(MakeNWidgetRoadDepot), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION), @@ -1114,14 +1195,28 @@ Window *ShowBuildRoadScenToolbar(RoadType roadtype) } struct BuildRoadDepotWindow : public PickerWindowBase { - BuildRoadDepotWindow(WindowDesc &desc, Window *parent) : PickerWindowBase(desc, parent) + BuildRoadDepotWindow(WindowDesc &desc, Window *parent, bool extended_depot) : PickerWindowBase(desc, parent) { this->CreateNestedTree(); + /* Fix direction for extended depots. */ + if (extended_depot) { + switch (_road_depot_orientation) { + case DIAGDIR_NE: + _road_depot_orientation++; + break; + case DIAGDIR_NW: + _road_depot_orientation--; + break; + default: break; + } + } + this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation); if (RoadTypeIsTram(_cur_roadtype)) { this->GetWidget(WID_BROD_CAPTION)->widget_data = STR_BUILD_DEPOT_TRAM_ORIENTATION_CAPTION; for (WidgetID i = WID_BROD_DEPOT_NE; i <= WID_BROD_DEPOT_NW; i++) { + if (!this->HasWidget(i)) continue; this->GetWidget(i)->tool_tip = STR_BUILD_DEPOT_TRAM_ORIENTATION_SELECT_TOOLTIP; } } @@ -1209,9 +1304,29 @@ static WindowDesc _build_road_depot_desc( _nested_build_road_depot_widgets ); -static void ShowRoadDepotPicker(Window *parent) +static const NWidgetPart _nested_build_extended_road_depot_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROD_CAPTION), SetDataTip(STR_BUILD_DEPOT_ROAD_ORIENTATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), SetPadding(WidgetDimensions::unscaled.picker), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_SW), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_SE), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _build_extended_road_depot_desc( + WDP_AUTO, nullptr, 0, 0, + WC_BUILD_DEPOT, WC_BUILD_TOOLBAR, + WDF_CONSTRUCTION, + _nested_build_extended_road_depot_widgets +); + +static void ShowRoadDepotPicker(Window *parent, bool extended_depot) { - new BuildRoadDepotWindow(_build_road_depot_desc, parent); + new BuildRoadDepotWindow(extended_depot ? _build_extended_road_depot_desc : _build_road_depot_desc, parent, extended_depot); } template diff --git a/src/road_map.cpp b/src/road_map.cpp index 66fe49010f6e1..9fb6b03dacb3c 100644 --- a/src/road_map.cpp +++ b/src/road_map.cpp @@ -38,9 +38,11 @@ RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge case MP_ROAD: switch (GetRoadTileType(tile)) { default: - case ROAD_TILE_NORMAL: return GetRoadBits(tile, rtt); - case ROAD_TILE_CROSSING: return GetCrossingRoadBits(tile); - case ROAD_TILE_DEPOT: return DiagDirToRoadBits(GetRoadDepotDirection(tile)); + case ROAD_TILE_NORMAL: + case ROAD_TILE_DEPOT: + return GetRoadBits(tile, rtt); + case ROAD_TILE_CROSSING: + return GetCrossingRoadBits(tile); } case MP_STATION: diff --git a/src/road_map.h b/src/road_map.h index 06c000384cd71..ac184a25f228b 100644 --- a/src/road_map.h +++ b/src/road_map.h @@ -118,16 +118,38 @@ debug_inline static bool IsRoadDepotTile(Tile t) return IsTileType(t, MP_ROAD) && IsRoadDepot(t); } +/** + * Return whether a road depot tile is an extended one. + * @param t Tile to query. + * @return True if extended road depot tile. + */ +static inline bool IsExtendedRoadDepot(Tile t) +{ + assert(IsTileType(t, MP_ROAD)); + assert(IsRoadDepot(t)); + return HasBit(t.m5(), 5); +} + +/** + * Return whether a tile is an extended road depot tile. + * @param t Tile to query. + * @return True if extended road depot tile. + */ +static inline bool IsExtendedRoadDepotTile(Tile t) +{ + return IsTileType(t, MP_ROAD) && IsRoadDepot(t) && IsExtendedRoadDepot(t); +} + /** * Get the present road bits for a specific road type. * @param t The tile to query. - * @param rt Road type. - * @pre IsNormalRoad(t) + * @param rtt Road tram type. + * @pre IsNormalRoad(t) || IsRoadDepotTile(t) * @return The present road bits for the road type. */ inline RoadBits GetRoadBits(Tile t, RoadTramType rtt) { - assert(IsNormalRoad(t)); + assert(IsNormalRoad(t) || IsRoadDepotTile(t)); if (rtt == RTT_TRAM) return (RoadBits)GB(t.m3(), 0, 4); return (RoadBits)GB(t.m5(), 0, 4); } @@ -152,7 +174,7 @@ inline RoadBits GetAllRoadBits(Tile tile) */ inline void SetRoadBits(Tile t, RoadBits r, RoadTramType rtt) { - assert(IsNormalRoad(t)); // XXX incomplete + assert(IsNormalRoad(t) || IsRoadDepotTile(t)); if (rtt == RTT_TRAM) { SB(t.m3(), 0, 4, r); } else { @@ -556,19 +578,28 @@ inline void TerminateRoadWorks(Tile t) SB(t.m7(), 0, 4, 0); } +/** + * Set the direction of the exit of a road depot. + * @param t The tile to query. + * @return Diagonal direction of the depot exit. + */ +static inline void SetRoadDepotDirection(Tile t, DiagDirection dir) +{ + assert(IsRoadDepot(t)); + SB(t.m6(), 6, 2, dir); +} /** - * Get the direction of the exit of a road depot. + * Get the direction of the exit of a road depot (or the image of the depot for extended road depots). * @param t The tile to query. * @return Diagonal direction of the depot exit. */ inline DiagDirection GetRoadDepotDirection(Tile t) { assert(IsRoadDepot(t)); - return (DiagDirection)GB(t.m5(), 0, 2); + return (DiagDirection)GB(t.m6(), 6, 2); } - RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance = false); /** @@ -680,7 +711,7 @@ inline void MakeRoadCrossing(Tile t, Owner road, Owner tram, Owner rail, Axis ro inline void SetRoadDepotExitDirection(Tile tile, DiagDirection dir) { assert(IsRoadDepotTile(tile)); - SB(tile.m5(), 0, 2, dir); + SB(tile.m6(), 6, 2, dir); } /** @@ -698,8 +729,9 @@ inline void MakeRoadDepot(Tile tile, Owner owner, DepotID depot_id, DiagDirectio tile.m2() = depot_id; tile.m3() = 0; tile.m4() = INVALID_ROADTYPE; - tile.m5() = ROAD_TILE_DEPOT << 6 | dir; - SB(tile.m6(), 2, 4, 0); + tile.m5() = ROAD_TILE_DEPOT << 6; + SB(tile.m6(), 0, 6, 0); + SB(tile.m6(), 6, 2, dir); tile.m7() = owner; tile.m8() = INVALID_ROADTYPE << 6; SetRoadType(tile, GetRoadTramType(rt), rt); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index b91e10fad3608..ad587d1d04fab 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2875,6 +2875,15 @@ bool AfterLoadGame() break; } } + + for (auto t : Map::Iterate()) { + if (!IsRoadDepotTile(t)) continue; + DiagDirection dir = (DiagDirection)GB(t.m5(), 0, 2); + SB(t.m5(), 0, 6, 0); + RoadBits rb = DiagDirToRoadBits(dir); + SetRoadBits(t, rb, HasRoadTypeRoad(t) ? RTT_ROAD : RTT_TRAM); + SB(t.m6(), 6, 2, dir); + } } /* In old versions it was possible to remove an airport while a plane was diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index 8e49af0eedce4..69d8cc515b535 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -134,8 +134,8 @@ void AfterLoadCompanyStats() RoadType rt = GetRoadType(tile, rtt); if (rt == INVALID_ROADTYPE) continue; c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rtt)); - /* A level crossings and depots have two road bits. */ - if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rtt)) : 2; + /* Level crossings have two road bits. */ + if (c != nullptr) c->infrastructure.road[rt] += (IsNormalRoad(tile) || IsRoadDepot(tile)) ? CountBits(GetRoadBits(tile, rtt)) : 2; } break; } diff --git a/src/script/api/script_road.cpp b/src/script/api/script_road.cpp index 9b29e2d4b9e38..73c6fa5da7aa2 100644 --- a/src/script/api/script_road.cpp +++ b/src/script/api/script_road.cpp @@ -535,7 +535,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD DiagDirection entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? DIAGDIR_SE : DIAGDIR_NW) : (::TileX(tile) < ::TileX(front) ? DIAGDIR_SW : DIAGDIR_NE); - return ScriptObject::Command::Do(tile, ScriptObject::GetRoadType(), entrance_dir, false, INVALID_DEPOT, tile); + return ScriptObject::Command::Do(tile, ScriptObject::GetRoadType(), entrance_dir, false, false, false, false, INVALID_DEPOT, tile); } /* static */ bool ScriptRoad::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id) diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 33db4fc6673cb..e50b2044cee7d 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1255,7 +1255,7 @@ static bool CanRoadContinueIntoNextTile(const Town *t, const TileIndex tile, con /* If the next tile is a road depot, allow if it's facing the right way. */ if (IsTileType(next_tile, MP_ROAD)) { - return IsRoadDepot(next_tile) && GetRoadDepotDirection(next_tile) == ReverseDiagDir(road_dir); + return IsRoadDepot(next_tile) && (GetRoadBits(next_tile, RTT_ROAD) & DiagDirToRoadBits(ReverseDiagDir(road_dir))) != ROAD_NONE; } /* If the next tile is a railroad track, check if towns are allowed to build level crossings. diff --git a/src/widgets/road_widget.h b/src/widgets/road_widget.h index 679545728a2e8..2fbf3c657393f 100644 --- a/src/widgets/road_widget.h +++ b/src/widgets/road_widget.h @@ -19,6 +19,7 @@ enum RoadToolbarWidgets : WidgetID { WID_ROT_AUTOROAD, ///< Autorail. WID_ROT_DEMOLISH, ///< Demolish. WID_ROT_DEPOT, ///< Build depot. + WID_ROT_EXTENDED_DEPOT, ///< Build extended depot. WID_ROT_BUILD_WAYPOINT, ///< Build waypoint. WID_ROT_BUS_STATION, ///< Build bus station. WID_ROT_TRUCK_STATION, ///< Build truck station. From 6d844ac97aeb35c3d284945a070dfa63f2663a84 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 2 Oct 2021 17:41:10 +0200 Subject: [PATCH 072/143] Change: Adapt pathfinders for extended road depots. --- src/pathfinder/follow_track.hpp | 27 ++++++++++++++++----------- src/pathfinder/yapf/yapf_road.cpp | 17 +++++++++++++++-- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index 2bdd6dffadc70..33439d9ae8a3a 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -101,7 +101,7 @@ struct CFollowTrackT { assert(IsTram()); // this function shouldn't be called in other cases - if (IsNormalRoadTile(tile)) { + if (IsNormalRoadTile(tile) || IsExtendedRoadDepotTile(tile)) { RoadBits rb = GetRoadBits(tile, RTT_TRAM); switch (rb) { case ROAD_NW: return DIAGDIR_NW; @@ -272,14 +272,16 @@ struct CFollowTrackT } } - /* road depots can be also left in one direction only */ + /* road depots can be also left in one direction sometimes */ if (IsRoadTT() && IsDepotTypeTile(m_old_tile, TT())) { - DiagDirection exitdir = GetRoadDepotDirection(m_old_tile); - if (exitdir != m_exitdir) { + RoadTramType rtt = IsTram() ? RTT_TRAM : RTT_ROAD; + RoadBits rb = GetRoadBits(m_old_tile, rtt); + if ((rb & DiagDirToRoadBits(m_exitdir)) == ROAD_NONE) { m_err = EC_NO_WAY; return false; } } + return true; } @@ -306,16 +308,17 @@ struct CFollowTrackT /* road and rail depots can also be entered from one direction only */ if (IsRoadTT() && IsDepotTypeTile(m_new_tile, TT())) { - DiagDirection exitdir = GetRoadDepotDirection(m_new_tile); - if (ReverseDiagDir(exitdir) != m_exitdir) { - m_err = EC_NO_WAY; - return false; - } /* don't try to enter other company's depots */ if (GetTileOwner(m_new_tile) != m_veh_owner) { m_err = EC_OWNER; return false; } + RoadTramType rtt = IsTram() ? RTT_TRAM : RTT_ROAD; + RoadBits rb = GetRoadBits(m_new_tile, rtt); + if ((rb & DiagDirToRoadBits(ReverseDiagDir(m_exitdir))) == ROAD_NONE) { + m_err = EC_NO_WAY; + return false; + } } if (IsRailTT() && IsStandardRailDepotTile(m_new_tile)) { DiagDirection exitdir = GetRailDepotDirection(m_new_tile); @@ -404,9 +407,11 @@ struct CFollowTrackT if (IsExtendedRailDepot(m_old_tile)) return false; exitdir = GetRailDepotDirection(m_old_tile); break; - case TRANSPORT_ROAD: - exitdir = GetRoadDepotDirection(m_old_tile); + case TRANSPORT_ROAD: { + if (GetRoadBits(m_old_tile, IsTram() ? RTT_TRAM : RTT_ROAD) != DiagDirToRoadBits(m_exitdir)) return false; + exitdir = ReverseDiagDir(m_exitdir); break; + } default: NOT_REACHED(); } diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index 910751f227c92..c1876dad13fa2 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -66,6 +66,19 @@ class CYapfCostRoadT /* Increase the cost for level crossings */ if (IsLevelCrossing(tile)) { cost += Yapf().PfGetSettings().road_crossing_penalty; + } else if (IsRoadDepot(tile) && IsExtendedRoadDepot(tile)) { + switch (GetDepotReservation(tile, IsDiagDirFacingSouth(TrackdirToExitdir(trackdir)))) { + case DEPOT_RESERVATION_FULL_STOPPED_VEH: + cost += 16 * YAPF_TILE_LENGTH; + break; + case DEPOT_RESERVATION_IN_USE: + cost += 8 * YAPF_TILE_LENGTH; + break; + case DEPOT_RESERVATION_EMPTY: + cost += YAPF_TILE_LENGTH; + break; + default: NOT_REACHED(); + } } break; @@ -135,7 +148,7 @@ class CYapfCostRoadT } /* stop if we have just entered the depot */ - if (IsRoadDepotTile(tile) && trackdir == DiagDirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) { + if (IsRoadDepotTile(tile) && !IsExtendedRoadDepotTile(tile) && trackdir == DiagDirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) { /* next time we will reverse and leave the depot */ break; } @@ -201,7 +214,7 @@ class CYapfDestinationAnyDepotRoadT /** Called by YAPF to detect if node ends in the desired destination */ inline bool PfDetectDestination(Node &n) { - return IsRoadDepotTile(n.m_segment_last_tile); + return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td); } inline bool PfDetectDestinationTile(TileIndex tile, Trackdir) From c98234e21b1c561adba87640a7ac4a727e91ce7d Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 13 Nov 2021 19:02:29 +0100 Subject: [PATCH 073/143] Codechange: Always return the first vehicle when looking for close road vehicles. --- src/roadveh_cmd.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 9b0ffe5589c5d..32f0587a0de6e 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -714,6 +714,8 @@ static RoadVehicle *RoadVehFindCloseTo(RoadVehicle *v, int x, int y, Direction d if (update_blocked_ctr && ++front->blocked_ctr > 1480) return nullptr; + rvf.best = rvf.best->First(); + return RoadVehicle::From(rvf.best); } @@ -1202,7 +1204,8 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) if (v->IsFrontEngine()) { const Vehicle *u = RoadVehFindCloseTo(v, gp.x, gp.y, v->direction); if (u != nullptr) { - v->cur_speed = u->First()->cur_speed; + assert(u == u->First()); + v->cur_speed = u->cur_speed; return false; } } @@ -1320,7 +1323,8 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) if (v->IsFrontEngine()) { const Vehicle *u = RoadVehFindCloseTo(v, x, y, new_dir); if (u != nullptr) { - v->cur_speed = u->First()->cur_speed; + assert(u == u->First()); + v->cur_speed = u->cur_speed; /* We might be blocked, prevent pathfinding rerun as we already know where we are heading to. */ v->path.tile.push_front(tile); v->path.td.push_front(dir); @@ -1436,7 +1440,8 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) if (v->IsFrontEngine()) { const Vehicle *u = RoadVehFindCloseTo(v, x, y, new_dir); if (u != nullptr) { - v->cur_speed = u->First()->cur_speed; + assert(u == u->First()); + v->cur_speed = u->cur_speed; /* We might be blocked, prevent pathfinding rerun as we already know where we are heading to. */ v->path.tile.push_front(v->tile); v->path.td.push_front(dir); @@ -1486,7 +1491,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) RoadVehicle *u = RoadVehFindCloseTo(v, x, y, new_dir); if (u != nullptr) { - u = u->First(); + assert(u == u->First()); /* There is a vehicle in front overtake it if possible */ if (v->overtaking == 0) RoadVehCheckOvertake(v, u); if (v->overtaking == 0) v->cur_speed = u->cur_speed; From 13ee21d9755092199ab91e9a77659b9631a90a37 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 13 Nov 2021 19:15:47 +0100 Subject: [PATCH 074/143] Add: Hide stopped road vehicles in extended depots that block the path of another vehicle. --- src/roadveh_cmd.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 32f0587a0de6e..e71f2e4496cd4 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -683,6 +683,27 @@ static Vehicle *EnumCheckRoadVehClose(Vehicle *v, void *data) return nullptr; } +/** + * Hide a stopped and visible road vehicle in an extended depot. + * @param v The road vehicle + * @pre v->IsStoppedInDepot() && IsExtendedRoadDepotTile(v->tile) + */ +static void LiftRoadVehicleInDepot(RoadVehicle *v) +{ + assert(v->IsStoppedInDepot()); + assert(IsExtendedRoadDepotTile(v->tile)); + for (RoadVehicle *rv = v; rv != nullptr; rv = rv->Next()) { + rv->vehstatus |= VS_HIDDEN; + rv->tile = v->tile; + rv->direction = v->direction; + rv->x_pos = v->x_pos; + rv->y_pos = v->y_pos; + rv->UpdatePosition(); + rv->Vehicle::UpdateViewport(true); + } + UpdateExtendedDepotReservation(v, false); +} + static RoadVehicle *RoadVehFindCloseTo(RoadVehicle *v, int x, int y, Direction dir, bool update_blocked_ctr = true) { RoadVehFindData rvf; @@ -716,6 +737,15 @@ static RoadVehicle *RoadVehFindCloseTo(RoadVehicle *v, int x, int y, Direction d rvf.best = rvf.best->First(); + /* If the best vehicle is a road vehicle stopped in an extended depot, + * it is in the way of the moving vehicle. Hide the stopped vehicle + * inside the depot. */ + if (rvf.best->IsStoppedInDepot()) { + assert(IsExtendedRoadDepotTile(rvf.best->tile)); + LiftRoadVehicleInDepot(RoadVehicle::From(rvf.best)); + return nullptr; + } + return RoadVehicle::From(rvf.best); } From 4e6e1b32a6d14c118ed9ec03617192eafb6ed927 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 19:55:15 +0200 Subject: [PATCH 075/143] Change: Buying and controlling road vehicles in extended road depots. --- src/lang/english.txt | 1 + src/road_cmd.cpp | 55 +++++-- src/roadveh_cmd.cpp | 338 ++++++++++++++++++++++++++++++++----------- src/vehicle_cmd.cpp | 12 ++ 4 files changed, 309 insertions(+), 97 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index f6ec5c41b52d6..b30880ac20664 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5378,6 +5378,7 @@ STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_YET_EXPLANATION :{WHITE}Start a STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL :{WHITE}Can't make train pass signal at danger... STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN :{WHITE}Can't reverse direction of train... STR_ERROR_TRAIN_START_NO_POWER :Train has no power +STR_ERROR_ROAD_VEHICLE_START_NO_POWER :Road vehicle has no power STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN :{WHITE}Can't make road vehicle turn around... diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 3806b4e0658b7..e6c8bca26e191 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -42,6 +42,7 @@ #include "road_cmd.h" #include "landscape_cmd.h" #include "rail_cmd.h" +#include "platform_func.h" #include "table/strings.h" #include "table/roadtypes.h" @@ -2476,21 +2477,51 @@ static const uint8_t _roadveh_enter_depot_dir[4] = { TRACKDIR_X_SW, TRACKDIR_Y_NW, TRACKDIR_X_NE, TRACKDIR_Y_SE }; -static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int, int) +static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int x, int y) { if (GetRoadTileType(tile) != ROAD_TILE_DEPOT || v->type != VEH_ROAD) return VETSB_CONTINUE; - RoadVehicle *rv = RoadVehicle::From(v); - if (rv->frame == RVC_DEPOT_STOP_FRAME && - _roadveh_enter_depot_dir[GetRoadDepotDirection(tile)] == rv->state) { - rv->state = RVSB_IN_DEPOT; - rv->vehstatus |= VS_HIDDEN; - rv->direction = ReverseDir(rv->direction); - if (rv->Next() == nullptr) VehicleEnterDepot(rv->First()); - rv->tile = tile; - - InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile)); - return VETSB_ENTERED_WORMHOLE; + if (IsExtendedRoadDepot(tile)) { + v = v->First(); + if (!IsExtendedRoadDepotTile(v->tile)) return VETSB_CONTINUE; + DepotID depot_id = GetDepotIndex(v->tile); + if (!v->current_order.IsType(OT_GOTO_DEPOT) || + v->current_order.GetDestination() != depot_id) { + return VETSB_CONTINUE; + } + for (Vehicle *u = v; u != nullptr; u = u->Next()) { + if (!IsExtendedRoadDepotTile(u->tile) || GetDepotIndex(u->tile) != depot_id) return VETSB_CONTINUE; + if (!IsDiagonalDirection(u->direction)) return VETSB_CONTINUE; + if (DiagDirToAxis(DirToDiagDir(u->direction)) != + DiagDirToAxis(GetRoadDepotDirection(v->tile))) { + return VETSB_CONTINUE; + } + } + + /* Stop position on platform is half the front vehicle length of the road vehicle. */ + int stop_pos = RoadVehicle::From(v)->gcache.cached_veh_length / 2; + DiagDirection dir = DirToDiagDir(v->direction); + int depot_ahead = (GetPlatformLength(tile, dir, GetRoadTramType(RoadVehicle::From(v)->roadtype)) - 1) * TILE_SIZE; + if (depot_ahead > stop_pos) return VETSB_CONTINUE; + + x = v->x_pos & 0xF; + y = v->y_pos & 0xF; + + if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y); + if (dir == DIAGDIR_SE || dir == DIAGDIR_SW) x = TILE_SIZE - x; + if (abs(stop_pos - x) <= 1) return VETSB_ENTERED_DEPOT_PLATFORM; + } else { + RoadVehicle *rv = RoadVehicle::From(v); + if (rv->frame == RVC_DEPOT_STOP_FRAME && + _roadveh_enter_depot_dir[GetRoadDepotDirection(tile)] == rv->state) { + rv->state = RVSB_IN_DEPOT; + rv->vehstatus |= VS_HIDDEN; + rv->direction = ReverseDir(rv->direction); + if (rv->Next() == nullptr) VehicleEnterDepot(rv->First()); + rv->tile = tile; + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile)); + return VETSB_ENTERED_WORMHOLE; + } } return VETSB_CONTINUE; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index e71f2e4496cd4..020b1ea690423 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -254,11 +254,12 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length) /** * Find an adequate tile for placing an engine. * @param[in,out] tile A tile of the depot. + * @param[in,out] is_exit_facing_south Whether the depot tile is facing south. * @param e Engine to be built. - * @param flags Flags of the command. - * @return CommandCost() or an error message if the depot is not appropriate. + * @param already_built Whether the vehicle already exists (for vehicle replacement). + * @return CommandCost() or an error message if the depot has no appropriate tiles. */ -CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, const Engine *e, DoCommandFlag flags) +CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, bool &is_exit_facing_south, const Engine *e, bool already_built) { assert(IsRoadDepotTile(tile)); @@ -269,23 +270,43 @@ CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, const Engine *e, DoCo const RoadTypeInfo *rti = GetRoadTypeInfo(rt); if ((dep->r_types.road_types & rti->powered_roadtypes) == 0) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE); - /* Use same tile if possible when replacing. */ - if (flags & DC_AUTOREPLACE) { - /* Use same tile if possible when replacing. */ - if (HasTileAnyRoadType(tile, rti->powered_roadtypes)) return CommandCost(); + /* Use same tile if possible when replacing or trying to leave the depot. */ + if (HasTileAnyRoadType(tile, rti->powered_roadtypes) && already_built) return CommandCost(); + + for (auto t : dep->depot_tiles) { + if (!HasTileAnyRoadType(t, rti->powered_roadtypes)) continue; + if (!IsExtendedDepot(t)) return CommandCost(); + if (GetDepotReservation(t, is_exit_facing_south) == DEPOT_RESERVATION_EMPTY) { + tile = t; + return CommandCost(); + } else if (GetDepotReservation(t, !is_exit_facing_south) == DEPOT_RESERVATION_EMPTY) { + is_exit_facing_south = !is_exit_facing_south; + tile = t; + return CommandCost(); + } } - tile = INVALID_TILE; - for (auto &depot_tile : dep->depot_tiles) { - if (!HasTileAnyRoadType(depot_tile, rti->powered_roadtypes)) continue; - tile = depot_tile; - break; - } + return_cmd_error(STR_ERROR_DEPOT_FULL_DEPOT); +} - assert(tile != INVALID_TILE); - return CommandCost(); +DiagDirection GetRoadDepotExit(TileIndex tile, RoadTramType rtt, DiagDirection dir) +{ + assert(IsRoadDepot(tile)); + RoadBits rb = GetRoadBits(tile, rtt); + if ((rb & DiagDirToRoadBits(dir)) != ROAD_NONE) return dir; + if (rb & ROAD_SE) return DIAGDIR_SE; + if (rb & ROAD_SW) return DIAGDIR_SW; + if (rb & ROAD_NE) return DIAGDIR_NE; + if (rb & ROAD_NW) return DIAGDIR_NW; + return INVALID_DIAGDIR; } +struct RoadDriveEntry { + uint8_t x, y; +}; + +#include "table/roadveh_movement.h" + /** * Build a road vehicle. * @param flags type of operation. @@ -299,24 +320,36 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin assert(IsRoadDepotTile(tile)); RoadType rt = e->u.road.roadtype; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + DiagDirection dir = GetRoadDepotExit(tile, RoadTramType(rt), GetRoadDepotDirection(tile)); + bool facing_south = IsValidDiagDirection(dir) ? IsDiagDirFacingSouth(dir) : false; + + if ((flags & DC_AUTOREPLACE) == 0) { + CommandCost check = FindDepotTileForPlacingEngine(tile, facing_south, e, false); + if (check.Failed()) return check; + dir = GetRoadDepotExit(tile, RoadTramType(rt), GetRoadDepotDirection(tile)); + } - CommandCost check = FindDepotTileForPlacingEngine(tile, e, flags); - if (check.Failed()) return check; + if (IsExtendedRoadDepotTile(tile) && facing_south != IsDiagDirFacingSouth(dir)) dir = ReverseDiagDir(dir); if (flags & DC_EXEC) { const RoadVehicleInfo *rvi = &e->u.road; RoadVehicle *v = new RoadVehicle(); *ret = v; - v->direction = DiagDirToDir(GetRoadDepotDirection(tile)); + v->direction = DiagDirToDir(dir); v->owner = _current_company; v->tile = tile; - int x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2; - int y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2; - v->x_pos = x; - v->y_pos = y; - v->z_pos = GetSlopePixelZ(x, y, true); + if (IsExtendedRoadDepotTile(tile)) { + const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(rt)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + DiagDirToDiagTrackdir(dir)]; + v->frame = RVC_DEPOT_START_FRAME; + v->x_pos = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); + v->y_pos = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF); + } else { + v->x_pos = TileX(tile) * TILE_SIZE + TILE_SIZE / 2; + v->y_pos = TileY(tile) * TILE_SIZE + TILE_SIZE / 2; + } + v->z_pos = GetSlopePixelZ(v->x_pos, v->y_pos, true); v->state = RVSB_IN_DEPOT; v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; @@ -360,6 +393,7 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin for (RoadVehicle *u = v; u != nullptr; u = u->Next()) { u->cargo_cap = u->GetEngine()->DetermineCapacity(u); u->refit_cap = 0; + u->state = RVSB_IN_DEPOT; v->InvalidateNewGRFCache(); u->InvalidateNewGRFCache(); } @@ -369,6 +403,12 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin v->UpdatePosition(); + if (IsExtendedDepot(v->tile) && (flags & DC_AUTOREPLACE) == 0) { + v->vehstatus &= ~VS_HIDDEN; + UpdateExtendedDepotReservation(v, true); + v->UpdateViewport(true, true); + } + CheckConsistencyOfArticulatedVehicle(v); } @@ -654,7 +694,7 @@ struct RoadVehFindData { static Vehicle *EnumCheckRoadVehClose(Vehicle *v, void *data) { - if (v->type != VEH_ROAD || v->IsInDepot()) return nullptr; + if (v->type != VEH_ROAD || (v->vehstatus & VS_HIDDEN) == 0) return nullptr; RoadVehFindData *rvf = (RoadVehFindData*)data; @@ -843,7 +883,7 @@ static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data) { const OvertakeData *od = (OvertakeData*)data; - return (v->type == VEH_ROAD && v->First() == v && v != od->u && v != od->v) ? v : nullptr; + return (v->type == VEH_ROAD && v->First() == v && v != od->u && v != od->v && ((v->vehstatus & VS_HIDDEN) == 0)) ? v : nullptr; } /** @@ -880,6 +920,9 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u) /* Don't overtake in stations */ if (IsTileType(v->tile, MP_STATION) || IsTileType(u->tile, MP_STATION)) return; + /* Don't overtake in road depot platforms. */ + if (IsExtendedRoadDepotTile(v->tile)) return; + /* For now, articulated road vehicles can't overtake anything. */ if (v->HasArticulatedPart()) return; @@ -961,9 +1004,17 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts); if (IsTileType(tile, MP_ROAD)) { - if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir)) { - /* Road depot owned by another company or with the wrong orientation */ - trackdirs = TRACKDIR_BIT_NONE; + if (IsRoadDepot(tile)) { + if (!IsTileOwner(tile, v->owner)) { + trackdirs = TRACKDIR_BIT_NONE; + } else if (IsExtendedRoadDepotTile(tile)) { + if (tile != v->tile) { + RoadBits rb = GetRoadBits(tile, GetRoadTramType(v->roadtype)) & DiagDirToRoadBits(ReverseDiagDir(enterdir)); + if (rb == ROAD_NONE) trackdirs = TRACKDIR_BIT_NONE; + } + } else if (GetRoadDepotDirection(tile) == enterdir) { // Standard depot + trackdirs = TRACKDIR_BIT_NONE; + } } } else if (IsTileType(tile, MP_STATION) && IsBayRoadStopTile(tile)) { /* Standard road stop (drive-through stops are treated as normal road) */ @@ -1065,60 +1116,118 @@ found_best_track:; return best_track; } -struct RoadDriveEntry { - uint8_t x, y; -}; +void HandleRoadVehicleEnterDepot(RoadVehicle *v) +{ + assert(IsRoadDepotTile(v->tile)); -#include "table/roadveh_movement.h" + if (IsExtendedRoadDepot(v->tile)) { + assert(v == v->First()); + for (RoadVehicle *u = v; u != nullptr; u = u->Next()) { + assert(u->direction == v->direction); + assert(IsExtendedRoadDepotTile(u->tile)); + assert(GetDepotIndex(u->tile) == GetDepotIndex(v->tile)); + u->state = RVSB_IN_DEPOT; + u->cur_speed = 0; + u->UpdateViewport(true, true); // revise: probably unneded + } + + v->StartService(); + UpdateExtendedDepotReservation(v, true); + + SetWindowClassesDirty(WC_ROADVEH_LIST); + SetWindowDirty(WC_VEHICLE_VIEW, v->index); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); + } else { + VehicleEnterDepot(v); + } +} bool RoadVehLeaveDepot(RoadVehicle *v, bool first) { /* Don't leave unless v and following wagons are in the depot. */ for (const RoadVehicle *u = v; u != nullptr; u = u->Next()) { - if (u->state != RVSB_IN_DEPOT || u->tile != v->tile) return false; + if (!u->IsInDepot()) return false; } - DiagDirection dir = GetRoadDepotDirection(v->tile); - v->direction = DiagDirToDir(dir); + bool visible_vehicle = first && (v->vehstatus & VS_HIDDEN) == 0; + + if (first && (v->vehstatus & VS_HIDDEN) != 0) { + TileIndex new_tile = v->tile; + bool facing_south = IsDiagDirFacingSouth(DirToDiagDir(v->direction)); + if (FindDepotTileForPlacingEngine(new_tile, facing_south, Engine::Get(v->engine_type), true).Failed()) return false; + if (IsExtendedDepot(v->tile)) { + UpdateExtendedDepotReservation(v, false); + v->tile = new_tile; + UpdateExtendedDepotReservation(v, true); + } + + DiagDirection dir = GetRoadDepotExit(v->tile, RoadTramType(v->roadtype), DirToDiagDir(v->direction)); + if (facing_south != IsDiagDirFacingSouth(dir)) dir = ReverseDiagDir(dir); + assert(dir != INVALID_DIAGDIR); + for (Vehicle *u = v; u != nullptr; u = u->Next()) { + u->direction = DiagDirToDir(dir); + u->tile = v->tile; + } + } - Trackdir tdir = DiagDirToDiagTrackdir(dir); - const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir]; + int x = v->x_pos; + int y = v->y_pos; + Trackdir tdir = v->GetVehicleTrackdir(); - int x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); - int y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF); + if ((v->vehstatus & VS_HIDDEN) != 0) { + const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir]; + + x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); + y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF); + } if (first) { /* We are leaving a depot, but have to go to the exact same one; re-enter */ if (v->current_order.IsType(OT_GOTO_DEPOT) && IsRoadDepotTile(v->tile) && v->current_order.GetDestination() == GetDepotIndex(v->tile)) { - VehicleEnterDepot(v); + if (IsExtendedRoadDepot(v->tile)) { + v->StartService(); + } else { + VehicleEnterDepot(v); + } return true; } - - if (RoadVehFindCloseTo(v, x, y, v->direction, false) != nullptr) return true; - + if ((v->vehstatus & VS_HIDDEN) != 0 && RoadVehFindCloseTo(v, x, y, v->direction, false) != nullptr) return true; VehicleServiceInDepot(v); + v->LeaveUnbunchingDepot(); StartRoadVehSound(v); - - /* Vehicle is about to leave a depot */ + /* Vehicle is about to leave a depot. */ v->cur_speed = 0; } - v->vehstatus &= ~VS_HIDDEN; v->state = tdir; - v->frame = RVC_DEPOT_START_FRAME; - v->x_pos = x; - v->y_pos = y; + if ((v->vehstatus & VS_HIDDEN) != 0) { + v->vehstatus &= ~VS_HIDDEN; + v->x_pos = x; + v->y_pos = y; + v->frame = RVC_DEPOT_START_FRAME; + } else if (v->Next() != nullptr && (v->Next()->vehstatus & VS_HIDDEN) == 0){ + for (RoadVehicle *u = v->Next(); u != nullptr; u = u->Next()) { + u->state = DiagDirToDiagTrackdir(DirToDiagDir(u->direction)); + u->UpdatePosition(); + u->UpdateInclination(true, true); + } + } + v->UpdatePosition(); v->UpdateInclination(true, true); + if (first && IsExtendedDepot(v->tile)) { + UpdateExtendedDepotReservation(v, false); + } + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); - return true; + return !visible_vehicle; } static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicle *prev, TileIndex tile, DiagDirection entry_dir, bool already_reversed) @@ -1138,7 +1247,7 @@ static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicl if (IsTileType(tile, MP_TUNNELBRIDGE)) { diag_dir = GetTunnelBridgeDirection(tile); } else if (IsRoadDepotTile(tile)) { - diag_dir = ReverseDiagDir(GetRoadDepotDirection(tile)); + diag_dir = ReverseDiagDir(IsExtendedRoadDepot(tile) ? DirToDiagDir(v->direction) : GetRoadDepotDirection(tile)); } if (diag_dir == INVALID_DIAGDIR) return INVALID_TRACKDIR; @@ -1206,6 +1315,53 @@ static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadType rt, RoadB return ret.Succeeded(); } +/** Check whether there is a close vehicle ahead and act as needed. + * @param v Moving vehicle + * @param x x coordinate to check + * @param y y coordinate to check + * @param dir direction of the vehicle + * @return whether a close vehicle is found. + */ +bool CheckCloseVehicle(RoadVehicle *v, int x, int y, Direction dir) +{ + if (!v->IsFrontEngine() || IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) return false; + /* Vehicle is not in a road stop. + * Check for another vehicle to overtake */ + RoadVehicle *u = RoadVehFindCloseTo(v, x, y, dir); + + if (u == nullptr) return false; + assert(u == u->First()); + + /* There is a vehicle in front overtake it if possible */ + if (v->overtaking == 0) RoadVehCheckOvertake(v, u); + if (v->overtaking == 0) v->cur_speed = u->cur_speed; + + /* In case we are in a road depot platform, why not try to start servicing? */ + if (IsExtendedRoadDepotTile(v->tile) && v->current_order.IsType(OT_GOTO_DEPOT)) { + DepotID depot_id = GetDepotIndex(v->tile); + if (v->current_order.GetDestination() != depot_id) return true; + if (!u->IsInDepot() || GetDepotIndex(u->tile) != depot_id) return true; + for (u = v; u != nullptr; u = u->Next()) { + if (!IsExtendedRoadDepotTile(u->tile) || GetDepotIndex(u->tile) != depot_id) return true; + if (v->direction != u->direction) return true; + } + HandleRoadVehicleEnterDepot(v); + return true; + } + + /* In case an RV is stopped in a road stop, why not try to load? */ + if (v->cur_speed == 0 && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && + v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) && + v->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) && + GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) { + Station *st = Station::GetByTile(v->tile); + v->last_station_visited = st->index; + RoadVehArrivesAt(v, st); + v->BeginLoading(); + } + return true; +} + bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) { if (v->overtaking != 0) { @@ -1264,18 +1420,21 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking][v->frame + 1]; if (rd.x & RDE_NEXT_TILE) { - TileIndex tile = v->tile + TileOffsByDiagDir((DiagDirection)(rd.x & 3)); + DiagDirection diag_dir = (DiagDirection)(rd.x & 3); + TileIndex tile = v->tile + TileOffsByDiagDir(diag_dir); Trackdir dir; + bool extended_depot_turn = IsExtendedRoadDepotTile(v->tile) && + (GetRoadBits(v->tile, GetRoadTramType(v->roadtype)) & DiagDirToRoadBits(diag_dir)) == ROAD_NONE; if (v->IsFrontEngine()) { /* If this is the front engine, look for the right path. */ - if (HasTileAnyRoadType(tile, v->compatible_roadtypes)) { - dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3)); + if (HasTileAnyRoadType(tile, v->compatible_roadtypes) && !extended_depot_turn) { + dir = RoadFindPathToDest(v, tile, diag_dir); } else { - dir = _road_reverse_table[(DiagDirection)(rd.x & 3)]; + dir = _road_reverse_table[diag_dir]; } } else { - dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false); + dir = FollowPreviousRoadVehicle(v, prev, tile, diag_dir, false); } if (dir == INVALID_TRACKDIR) { @@ -1302,7 +1461,10 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) case TRACKDIR_RVREV_SW: needed = ROAD_NE; break; case TRACKDIR_RVREV_NW: needed = ROAD_SE; break; } - if ((v->Previous() != nullptr && v->Previous()->tile == tile) || + if (extended_depot_turn) { + tile = v->tile; + start_frame = RVC_TURN_AROUND_START_FRAME_SHORT_TRAM; + } else if ((v->Previous() != nullptr && v->Previous()->tile == tile) || (v->IsFrontEngine() && IsNormalRoadTile(tile) && !HasRoadWorks(tile) && HasTileAnyRoadType(tile, v->compatible_roadtypes) && (needed & GetRoadBits(tile, RTT_TRAM)) != ROAD_NONE)) { @@ -1319,8 +1481,9 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) } else if (!v->IsFrontEngine() || !CanBuildTramTrackOnTile(v->owner, tile, v->roadtype, needed) || ((~needed & GetAnyRoadBits(v->tile, RTT_TRAM, false)) == ROAD_NONE)) { /* * Taking the 'small' corner for trams only happens when: - * - We are not the from vehicle of an articulated tram. + * - We are not the front vehicle of an articulated tram. * - Or when the company cannot build on the next tile. + * - Or when the extended depot doesn't have the appropriate tram bit to continue. * * The 'small' corner means that the vehicle is on the end of a * tram track and needs to start turning there. To do this properly @@ -1363,6 +1526,11 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) } uint32_t r = VehicleEnterTile(v, tile, x, y); + if (HasBit(r, VETS_ENTERED_DEPOT_PLATFORM) && v->Next() == nullptr && v == v->First()) { + HandleRoadVehicleEnterDepot(RoadVehicle::From(v)); + return false; + } + if (HasBit(r, VETS_CANNOT_ENTER)) { if (!IsTileType(tile, MP_TUNNELBRIDGE)) { v->cur_speed = 0; @@ -1419,6 +1587,9 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) } v->x_pos = x; v->y_pos = y; + if (prev != nullptr && prev->IsInDepot() && (prev->vehstatus & VS_HIDDEN) == 0) { + v->state = RVSB_IN_DEPOT; + } v->UpdatePosition(); RoadZPosAffectSpeed(v, v->UpdateInclination(true, true)); return true; @@ -1429,7 +1600,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) Trackdir dir; uint turn_around_start_frame = RVC_TURN_AROUND_START_FRAME; - if (RoadTypeIsTram(v->roadtype) && !IsRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, RTT_TRAM, true))) { + if (RoadTypeIsTram(v->roadtype) && !IsRoadDepotTile(v->tile) && !IsExtendedRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, RTT_TRAM, true))) { /* * The tram is turning around with one tram 'roadbit'. This means that * it is using the 'big' corner 'drive data'. However, to support the @@ -1495,6 +1666,9 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) v->x_pos = x; v->y_pos = y; + if (prev != nullptr && prev->IsInDepot() && (prev->vehstatus & VS_HIDDEN) == 0) { + v->state = RVSB_IN_DEPOT; + } v->UpdatePosition(); RoadZPosAffectSpeed(v, v->UpdateInclination(true, true)); return true; @@ -1504,8 +1678,10 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) * it's on a depot tile, check if it's time to activate the next vehicle in * the chain yet. */ if (v->Next() != nullptr && IsRoadDepotTile(v->tile)) { - if (v->frame == v->gcache.cached_veh_length + RVC_DEPOT_START_FRAME) { - RoadVehLeaveDepot(v->Next(), false); + if ((v->Next()->vehstatus & VS_HIDDEN)) { + if (v->frame == v->gcache.cached_veh_length + RVC_DEPOT_START_FRAME) { + RoadVehLeaveDepot(v->Next(), false); + } } } @@ -1515,30 +1691,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) Direction new_dir = RoadVehGetSlidingDirection(v, x, y); - if (v->IsFrontEngine() && !IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) { - /* Vehicle is not in a road stop. - * Check for another vehicle to overtake */ - RoadVehicle *u = RoadVehFindCloseTo(v, x, y, new_dir); - - if (u != nullptr) { - assert(u == u->First()); - /* There is a vehicle in front overtake it if possible */ - if (v->overtaking == 0) RoadVehCheckOvertake(v, u); - if (v->overtaking == 0) v->cur_speed = u->cur_speed; - - /* In case an RV is stopped in a road stop, why not try to load? */ - if (v->cur_speed == 0 && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && - v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) && - v->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) && - GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) { - Station *st = Station::GetByTile(v->tile); - v->last_station_visited = st->index; - RoadVehArrivesAt(v, st); - v->BeginLoading(); - } - return false; - } - } + if (CheckCloseVehicle(v, x, y, new_dir)) return false; Direction old_dir = v->direction; if (new_dir != old_dir) { @@ -1634,6 +1787,16 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) v->y_pos = y; v->UpdatePosition(); RoadZPosAffectSpeed(v, v->UpdateInclination(false, true)); + + /* After updating the position, check whether the vehicle can stop in a depot platform. */ + if (IsExtendedRoadDepotTile(v->tile) && v->Next() == nullptr) { + RoadVehicle *first = RoadVehicle::From(v)->First(); + if (HasBit(VehicleEnterTile(first, first->tile, first->x_pos, first->y_pos), VETS_ENTERED_DEPOT_PLATFORM)) { + HandleRoadVehicleEnterDepot(first); + return false; + } + } + return true; } @@ -1655,6 +1818,8 @@ static bool RoadVehController(RoadVehicle *v) return true; } + if (v->ContinueServicing()) return true; + ProcessOrders(v); v->HandleLoading(); @@ -1816,6 +1981,9 @@ Trackdir RoadVehicle::GetVehicleTrackdir() const if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR; if (this->IsInDepot()) { + if (IsExtendedRoadDepot(this->tile)) { + return DiagDirToDiagTrackdir(DirToDiagDir(this->direction)); + } /* We'll assume the road vehicle is facing outwards */ return DiagDirToDiagTrackdir(GetRoadDepotDirection(this->tile)); } diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index 1a23c07a23934..b2f94fdda5c76 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -253,6 +253,9 @@ CommandCost CmdSellVehicle(DoCommandFlag flags, VehicleID v_id, bool sell_chain, ret = CommandCost(EXPENSES_NEW_VEHICLES, -front->value); if (flags & DC_EXEC) { + if (front->type == VEH_ROAD && IsExtendedDepot(v->tile) && (flags & DC_AUTOREPLACE) == 0) { + UpdateExtendedDepotReservation(v, false); + } if (front->IsPrimaryVehicle() && backup_order) OrderBackup::Backup(front, client_id); delete front; } @@ -610,7 +613,16 @@ CommandCost CmdStartStopVehicle(DoCommandFlag flags, VehicleID veh_id, bool eval } case VEH_SHIP: + break; case VEH_ROAD: + if ((v->vehstatus & VS_STOPPED) && !(flags & DC_AUTOREPLACE) && v->IsStoppedInDepot()) { + Depot *dep = Depot:: GetByTile(v->tile); + + /* Check that the vehicle can drive on some tile of the depot */ + RoadType rt = Engine::Get(v->engine_type)->u.road.roadtype; + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + if ((dep->r_types.road_types & rti->powered_roadtypes) == 0) return_cmd_error(STR_ERROR_ROAD_VEHICLE_START_NO_POWER); + } break; case VEH_AIRCRAFT: { From 437c7e5eebe7977465f4cbbf650651d1f08b05d8 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 16 Jun 2021 19:40:46 +0200 Subject: [PATCH 076/143] Change: This is a testing version, so make it difficult to load savegames with other versions of OpenTTD. --- src/saveload/saveload.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index c67a65359a46c..345c639eb0e9f 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -398,6 +398,8 @@ enum SaveLoadVersion : uint16_t { SLV_EXTENDED_DEPOTS, ///< 321 PR#8480 Extended depots for rail, road and water transport. + SLV_PATCHED = UINT16_MAX - 6, ///< Make it difficult to load any savegame made with + // this patched version in any other version of OpenTTD (unless it uses the same saveload version trick). SL_MAX_VERSION, ///< Highest possible saveload version }; From 5670c89c7ef076cd1fffd55b746eef204af16f70 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Mon, 12 Jul 2021 16:59:02 +0200 Subject: [PATCH 077/143] Doc: Changes in the landscape grid due to extended depots. --- docs/landscape.html | 21 ++++++++++- docs/landscape_grid.html | 81 ++++++++++++++++++++++++---------------- 2 files changed, 69 insertions(+), 33 deletions(-) diff --git a/docs/landscape.html b/docs/landscape.html index 40d73c7fe4c49..331678f8091ab 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -547,6 +547,11 @@

      Landscape

    • m5 bit 4: pbs reservation state
    • +
    • m5 bit 5 clear: standard depot
    • +
    • m5 bit 5 set: extended depot +
        +
      • m4 bits 6..7: depot reservation state
      • +
  • @@ -666,7 +671,7 @@

    Landscape

    • m1 bits 4..0: owner of the depot
    • m2: Depot index
    • -
    • m5 bits 1..0: exit towards: +
    • m6 bits 6..7: exit towards: @@ -689,6 +694,15 @@

      Landscape

      0 
    • +
    • m3 bits 0..3: road layout road type 1 (tram)
    • +
    • m5 bits 0..3: road layout road type 0 (normal road)
    • +
    • m5 bit 5 clear: standard depot
    • +
    • m5 bit 5 set: extended depot +
        +
      • m4 bits 6..7: depot reservation state towards north direction
      • +
      • m6 bits 4..5: depot reservation state towards south direction
      • +
      +
    • m7 bits 4..0: owner of the road type 0 (normal road)
    @@ -1127,6 +1141,11 @@

    Landscape

    +
  • m5 bit 5 clear: standard depot (for depots only)
  • +
  • m5 bit 5 set: extended depot +
      +
    • m4 bits 6..7: depot reservation state (for depots only)
    • +
    diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index 6e59a3acac673..fb85f30152c20 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -79,8 +79,8 @@

    Landscape

    0 ground - XXXX XX XX - XXXX XXXX + XXXX XX XX + XXXX XXXX OOO1 OOOO OOOO OOOO OOOO OOOO XXX XOOOO @@ -95,16 +95,16 @@

    Landscape

    XXX XXXXX - 1 + 1 rail - XOOX XXXX + XOOX XXXX OOOO XXXX OOOO OOOO OOOO OOOO OOOO XXXX OO XXXXXX - OOOO OOOO - OOOO OOOO - OOOO OOOO OOXX XXXX + OOOO OOOO + OOOO OOOO + OOOO OOOO OOXX XXXX rail with signals @@ -114,22 +114,27 @@

    Landscape

    O1 XXXXXX - depot - XXXX XXXX XXXX XXXX - OOOO OOOO + standard rail depot + XXXX XXXX XXXX XXXX + OOOO OOOO OOOO XXXX - 1OOX OOXX + 1OOX OOXX + + + extended rail depot + XXOO XXXX + 1O1X OOXX - 2 + 2 road OOOX XXXX XXXX XXXX XXXX XXXX XXXX XXXX - OOXX XXXX + OO XXXXXX OO XX XXXX - OO XXXOOO - OOX OXXXX + OOXX XOOO + OOXO XXXX OOOO XXXX XXOO OOOO @@ -137,20 +142,27 @@

    Landscape

    OOOX XXXX XXXX OOOO O1 X XOOOX - OO XXXOOO + OOXX XOOO OOX XXXXX OOOO XXXX XX XXXXXX - road depot - OOOX XXXX - XXXX XXXX XXXX XXXX - XXXX OOOO - 1OOO OOXX - OOOO OOOO - OOX XXXXX - OOOO XXXX XXOO OOOO + standard road depot + OOOX XXXX + XXXX XXXX XXXX XXXX + XXXX XXXX + 1OOO XXXX + XXOO OOOO + OOX XXXXX + OOOO XXXX XXOO OOOO + + extended road depot + XX XXXXXX + 1O1O XXXX + XX XXOOOO + + 3 finished house @@ -240,16 +252,16 @@

    Landscape

    OOOO OOOO - 6 + 6 sea, shore - X XX XXXXX + X XX XXXXX OOOO OOOO OOOO OOOO - OOOO OOOO + OOOO OOOO OOOO OOOO OOOO OOOX - OOOO OOOO - OOOO OOOO - OOOO OOOO OOOO OOOO + OOOO OOOO + OOOO OOOO + OOOO OOOO OOOO OOOO canal, river @@ -262,11 +274,16 @@

    Landscape

    O1OOXX XX - shipdepot - XXXX XXXX XXXX XXXX + standard ship depot + XXXX XXXX XXXX XXXX OOOO OOOO 1OOOOOX X + + extended ship depot + XXOO OOOO + 1O1OOOX X + 8 finished industry From c6ec70d1a00f6bcf2b7d4c0e003729d8f9199517 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 6 Mar 2021 22:09:56 +0100 Subject: [PATCH 078/143] Doc: Changes in the landscape grid by multi-tile airports. --- docs/landscape_grid.html | 56 +++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index fb85f30152c20..57b76bc42d8e3 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -79,8 +79,8 @@

    Landscape

    0 ground - XXXX XX XX - XXXX XXXX + XXXX XX XX + XXXX XXXX OOO1 OOOO OOOO OOOO OOOO OOOO XXX XOOOO @@ -193,27 +193,60 @@

    Landscape

    OOOO OOOO OOOO OOOO - 5 + 5 rail station - OXX XXXXX - XXXX XXXX XXXX XXXX + OXX XXXXX + XXXX XXXX XXXX XXXX XXXX OOOO XXXX XXXX XXXX XXXX XXXXX XXX - XXXX XXXX + XXXX XXXX OOOO OOOO OOXX XXXX rail waypoint OOOO OOOX + + airport infrastructure + XXXX XXXX + XXXX XXXX + OOOX OOOO + XXXX XOOO + XXOO OOOO XXXX XXXX + + + airport tracks + OO1O OOOO + OO OO XXXXXX XXXXXX + + + airport apron + O1XX OOOO + XX OO XXXXXX XXXXXX + + + airport hangar + 1OXO OOOO + XX OO XXXXXX XXXXXX + + + airport runway (plain) + 11OO OOOO + X XXX XXXXXX XXXXXX + + + airport runway (start/end) + 11XX OOOO + X OXX XXXXXX XXXXXX + road stop XXXX OOOO OOXX XXXX OOOO OXXX - OXXX XOOO + OXXX XOOO OOOX XXXX OOOO XXXX XX XXXXXX @@ -222,20 +255,13 @@

    Landscape

    XXXX XXOO XOOO XXXX XXOO OOOO - - airport - XXXX OOOO - OOOO OOOO - XXXX XXXX - XXXX XXXX - OOOO OOOO OOOO OOOO - dock OOOO OOOO OOOO OOOO OOOO OXXX OOOO OOOO + OOOO OOOO OOOO OOOO buoy From b0d08448acd7970ae9bc5baa2741ce9e24b96d5b Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 20:38:39 +0200 Subject: [PATCH 079/143] Add: Add a new saveload version for multi-tile airports. # Conflicts: # src/saveload/saveload.h --- src/saveload/saveload.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 345c639eb0e9f..66dd1dfab3e51 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -397,6 +397,7 @@ enum SaveLoadVersion : uint16_t { SLV_KEEP_REMOVED_DEPOTS, ///< 320 PR#XXXXX Keep remove depots for a while. SLV_EXTENDED_DEPOTS, ///< 321 PR#8480 Extended depots for rail, road and water transport. + SLV_MULTITILE_AIRPORTS, ///< 322 PR#XXXX Multi-tile airports. SLV_PATCHED = UINT16_MAX - 6, ///< Make it difficult to load any savegame made with // this patched version in any other version of OpenTTD (unless it uses the same saveload version trick). From 679616e2ee760b9315e6e9a81df73745bd0665e4 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 6 Mar 2021 22:59:55 +0100 Subject: [PATCH 080/143] Doc: Add files describing how to calculate the total noise an airport generates. --- docs/Airport noise.html | 35 +++++++++++++++++++++++++++++++++++ docs/Airport noise.ods | Bin 0 -> 14323 bytes 2 files changed, 35 insertions(+) create mode 100644 docs/Airport noise.html create mode 100644 docs/Airport noise.ods diff --git a/docs/Airport noise.html b/docs/Airport noise.html new file mode 100644 index 0000000000000..4717b016dc179 --- /dev/null +++ b/docs/Airport noise.html @@ -0,0 +1,35 @@ + +- no title specified
               
      

    Base noise

    Runway noise

           
     

    Gravel

    0

    1

           
     

    Asphalt

    1

    3

           
     

    Water

    1

    0

           
               
     

    Airport name

    Elements

    Total noise of the multi-tile airport

    Original airport stats

    Diference of noise

    Multi-tiled – original

     

    Runways

    Normal aprons

    Heliports/Helipads

    Gravel

    Asphalt

    Water

    Surface type

    Original noise

     

    Small

    1

    2

    0

    3

    6

    3

    Gravel

    3

    0

     

    Commuter

    1

    3

    2

    6

    9

    6

    Asphalt

    4

    5

     

    City

    1

    3

    0

    4

    7

    4

    Asphalt

    5

    2

     

    Metropolitan

    2

    3

    0

    5

    10

    4

    Asphalt

    8

    2

     

    Internacional

    2

    6

    2

    10

    15

    9

    Asphalt

    17

    2

     

    Intercontinental

    4

    8

    2

    14

    23

    11

    Asphalt

    25

    2

     

    Heliport

    0

    0

    1

    1

    2

    2

    Asphalt

    1

    1

     

    Helidepot

    0

    0

    1

    1

    2

    2

    Asphalt

    2

    0

     

    Helistation

    0

    0

    3

    3

    4

    4

    Asphalt

    3

    1

               
               
               
               
               
               
    \ No newline at end of file diff --git a/docs/Airport noise.ods b/docs/Airport noise.ods new file mode 100644 index 0000000000000000000000000000000000000000..51cb3bd274f9bc4f425376d351e61c1cf27ce8fa GIT binary patch literal 14323 zcmch8WmKHW)-DdggM}c$o#4SGXmEE8?ljQ2ySuwfaCdhLjk|kr_W+m7`R2@=xie?o z@6X-cy}GJa?WgM9CB3_LJ+cyzP#9ofaA05${>%#g791gTU|?XsKCeY!mZp}5cFxv@ zy4Kbfruw>erT{B?2P*?QfG*G!NC&Vsv@!tb+glo1+0g;5Z4Gq|fF_29cC!D0`3m!I z!G28%0<4TojqPoJLjy9=+W`O;db+ms|2r)s3xKYj;r~|awWs#~y)B%->7g~i+TQw? z>VN4Y-rwoj0*q}9fx!QLi@$7Qt!rgy@n2db_?s3@Ep?3zf%F2Vc9y!C|*;49Y9@ueM1Yw*YO0{((BvX+WwjiPL>vsV6qahza|kF z#Q&)OYlQy}7V)))rLL8!ks;8I&ep(aG8Q@j>SFR=EiCT10K?u`b?VX!W-)MHYQ3vSCq^x_uhX7P_YVCC^7o^{k0KrZ$5+zm|kzY z;c$R9-x#UL*&Sv@Io}fUgsyNFWmUuL*2}jA))Pj1e-3{mIqYZWi`OhI(Z`%{DaX;3 zeY`pFwQb5bhx_XpO~lyzY7k<6np?by%Za{={Z!xi8cE#OG^P`ogBC95 zm%V4Yv6uIo6}1jR7N0@6#t%_@wHE|RUn+i?ulCkt5*i!~%oh^u)ttYU>ff2~x20-m zr~Aw7;R+*GOAM&3w=__nR6a@ky@?I6- zgN}}9i!L@?SSOh8l(||WO$9dLO6|m#j0ilr+=RG2zOBZe?2AdD4iB9mvSu;+apL6F zV}`2?A8ip3X+rh8uTi+Cu&;d;5e>mDJ&?^cMtXX5k_+2(Uv{R^Tscsle?TmQlW#c3!3`EAA@rY#L_VE|V@i3IGq z=*!H!h^RWS`Kz|;d`#<`Xtgnu<$CVxz)JkrfrWov7eG5_3q#yCY7K|%WQ9T97`LSApoi2#iG)aU?ot2 z{4|2NLPFob8TT>mHF}AV#zn*!O9a5n(=x|x>QQx*jo26oC0Zuu<0v--3NyZY2T~7m z+jmYKHEK?lc`wsSpUJ(j2k%D19jZ-2*N=kw)Vqw{c~vyYa9FIxPqdWwQ{80AooPEGDR#^S@*ZxQ zGbW#$y~_s@Lm=kzYna;l-R!stZ_3Od+1Px~*Q_BBr`IN1KbblyoN`eK9drAi@@toX=B->*Qs99!~3I zEbpwTA!!U6E=^J?mPH70s~hb14&3)2rj#kKkZwRYfnL2&&y}N#2%v=27Sq9 z8YpQ3Lv&+O`5viN67dSTJM#`p%Og1pw=u>OqOw_gQ3PJ5uG%fY%gyc3UPBwkF+^nd zn8Uc^9gHC*c+*=ft1cO%{5h2>rIytP8~`z1Zj3UnUTzbUvQ&;$5S2Y5_ZK`eyd874 zz-t5`|JQeAEnrCMswX0OsFbmX@4gdK=r-p|TkS9U43B37lNd>aRObo3xe5^d8d0y~3n|ojdT^LnAdqJbT~iv8qh@q>Zv2U)h4+yt-u3;psXzL=N`8 z1lV?_dA)Cm%ONbTRV7wU=CMC)NyO{Zm&si2xxs8V`;ix4D{X7)ee!dwX z2N$v|52wA!OF}p*8uBewL6nmLtUkqMC2rQ<4tdea1syc3iN8BNo`J8iJ87neuc)&W zv64R*hNCqq$uzIdTK4=n=BW|RC9aH*kD@!ZPXiG@5!V%PD2(@bwqQSb>~QfC>Ncts zVymI5CUoWf1T%rw;dU)W+}>766?&1ToNbLub$ksgFHzrfE1g&be7SNOgEBTQ1VMLv zI#>De^v)wU8?eFfv5KMh z{fvc;A&htY8}W6Jr56P~+0o2rI|_&68kH|r%*-Ow^%cBk;0otJ3#>YaVXsskXwFa&Dfz3XBC@6v9+wP|B}*R=rI_-*l4SD*EJ{}GL@zQbqMcCSy7 z;!YJhY!+7M+RgED3G_5oJt=8-+s$#pCk_i?TMWx0m}IoRGbbs{J`z?;%LnM=mXCSL z{UpwN8!|H4#1zoE3=8e-mpd{q&vjV6O%Js`KL{9tPT!+1Ohv7nst9*scLaTo^OKP+ zcKjOHuE>+6U%kuVA=_!jN%RyKKL1s*|LQXxqEhdWX zP~IF!y8W(~d7gy(4V6y3{+bQw?%Ws(sM?+wqw4-ac&O)Q)e~Ou?8-R;1J==eT__ve z^#djK;Q?0Yv=QgnrSM^Br6}QW%NUs<2E{7R?dKy zxXDq?su-fxNn{OdId0vWV!ksshWA)^7>?r8Gt2f7+X_pt62N1+Wk%_?b0NE#!a1eW zZq_1P08TQd37V$K4F@w;BR6dek3`If{444Yf!c38n}=@0&~boxZFbw6PB-h5(z)qW zSiGd$<~`SGvQgP%RAiRq`xu4^f5Q zQ{ZN}WICkau*-46gkPFy${QHfd>xkxwZrR^7~$5WJGZBz>@lG{U8pm)ij8{Fe`K>K z9FK}s%yDpH?Q+J)pu)~5@jyx$Hu9vzNuCDc5xl}d2 z*<$RoNk0`4q`fbSU=F4Cq~-bqCFWhEoKegD^nkukvvc@ddgHQJTudVDucT{SSuAIU z<6@**;Tu6Eo!Y^va@x8()6s{cAFi#Xt)1c860RiCF4!%-ZHS%9H9Bis*i@DKa+m4J zFFTR^}K{4K6jaf(B!7e406ie^{BsE_9LGH%HCOz83sYhVAG3wN2P^QU3> zH=0j2dWh1eDj)ep?5*qF+^Z3>rq-|QKBV0h7TN~oKj^2Q4EGjzj-_Ofn`mo&ZgZ?u zr#*mZKK{IaWIslvefuJz`h@(;HN>+HfrEeb{N@<`n`?;h>OtrOtnB{BDL>X)idkgG zXtW)dU%uE6-V!UZM_5-rEcW$JO@v0>N#bZKd= zjXd@Z{5m^n7q<^1Gb9@G^W1vdmR*``(gjUtHUBQl2lS{MJ3L>w0)4D7F^H|4)4NOF zEemaDK2e3ll`Eq#GW`*CTC6AkB?>#UwwD<+OLE__@6>sPEQsfvrh5m&bp^_B*bJGd znyu6>++(V>^?^J%;iaW@7*cYhv=p~a*%ql`W<53 zHD>qB>RW@)4K-0^H}3tE&x!;vId!P%s4HR_3Kr6$;R=I7j@V2aPid3M{BM|(71F#B zArbT_oAVM@8b)?L!!4|HjK$22FxUt_G1&AJv!V;+?zI9rOK&dH$i_KMq|oXhTD{tq0tlB zFBZ>fIIx}2Svy{^h<`@pKA?}3Y+U5Z_!1dq#+76|Jf@7T0<2=p!osjYQ0(;$-|Q9+ zu}y_^FfbGlMv-)Yi&`#@e;bDZF<(lKps8`&%IMnUse8d`Z^6uY+fC2bw&v@X=cyJQ%jO!uR2#)s$gs=b z*~6%n#iB%t)(dfRP4QU{OMBMH1hDSe$+wrPt7KqD*p`PV;;Nei<-cIpui?X} z|EO7y83v&8`y^7rf{)%s@AtOoZe`4q`NN5sR4w&jKZo6-jVEXB4C8%xz9Md^93>e#AHvc)eN3K%s7t=mo) z6TP6+xnmzDVm0P9&NPw%*2e zc9qO^8@z=VbW3q6BiWiNUuI@8Scxdv3gGrHI$%}9XP7Zo^On;l6JyZ|84gJ~z*d{6 z8+62rgbEa|Bgd7DB1WBK3q?0Z%mjMFMAUJ$!nMO{Xa znfE(&o@11&7n)Q31MfZ#9p4iAi%81CH|1O;6jvp+d}8SUZ9zM6U}Q)+9HoriHmpQq zaPzlz%q-W5k|%)`)LSEKMoJDRbe6048JO!<(@)IMHn&T}zVAF5u``5}EXN|hMbjSb zKGebEdphtfsdICY?z%QSJ{D=Ai@$z23t^A&=e!948_+hjrw+!GJ9jG}0y`!U9gsJ! zW0jw&N<8=(#rNY|w)p1^LjUX{0uoSjvj(NR8NfVsk4G15I^i}9V& zdKBas|KRRPhyqOEp`%i7@hT*RUjdeoa~w2kOuO@R#ftS9+D<-5I@*3Qx7}lpZBT|z z0GI?7KJ8&IUkw_#U1qC9*d}LS=r|CuWXGkQ+)>D2$J2H3!H-*{5T8b@5P$3r#`|x zH$YDK1uY6llqJsnla%)!%@-`%@^mYc0up~mbQ5oEMKj)8GkIs(iv0K(Z@U<;Hk8vP z%#6TNUvr00WhV=N+Vqm}9Fb#iw#4Z;H@+1bp()0H83CcJI_fTnGNk78h)bH2cbN4! zFEtBDN-RT0EK|T)A+|dU%v%{&ODja1Kt5o1QK|;kI_8xKZ5IjEhB~sKw4$6-XzW6=-N@XKH2q+wsw{x|U-OJ9hh;&cxPnMudrm@pUbT|8TCh z4n8QM1x$_^=}jtB^!+uiY)cs=GqSkMZQ8LgbPV@tDx1t|Wao8aUEr8Yg{fJAzyuTUD0((kByY}>jU7nux53JVp+Lb)Eq z26sjXfGgy87j@{tsc>aO(@mu?4;#FcGNtlcqfG2!_=%zImSZ7TK7Dv?JK20FPUXIj ztq)+4;8elfscvoZ+)rP|FP?Wu2-aIVR4FJ*Na5l-Dp+e>1sPrfBcQJImeMQ<2D+`p z<#+nwyUzN|@@s7X14%(7xPtNgUu8RL%`u|sao@hp-`!E#Y5qy_9l*cV(I&6h5@h6y-sVT5Y!VdYX<0vaHn;kIZddPk%T)7CpWr!NZoNbMPhCvzA zL5`u7LPYmMA+XX%>|oe~Zudias@OYmX&3?WC%h95gA-8=Jly}z*F z+z?qCk|_qEy1&VeiYNfP0{2ApEB%oD!I`yBGw|{JiBtfs|JLxvYYS0;a{xK`$>{A{ zgHC%0H(xU^ghrWS1-N;z5e`f7vTjn7C<$9bEUd}c-7AO3v;CRA=rfnEEhhd>1rHPu ze1@hh>$pz#;f)`LB>m)k+Kp>iULx+`OhNVEWKKJ?*?;~px+MHcXf$=2wqeJOE53Jn zk6mt<;p;XL&mjYDMfWz(7E1J7?KP*>Ja+mG#Jmo5Gp5D??d7f(HoOIFJ5mTA7oQwf>$d@i;l`D;Qrnp~c z=FHBYTd&`I|8_iM*g{p&0-rSN8@-^;ArzWXpla&c6F=26e_vj@W!}=D-P|U&a?!k8 zBzUg@&Zv9cgrrJtrj*4m+TFUW<+rhgDU!z|d)@Qu$6ca7tTsKE%U-jJ_o zsb~`);Il5aX<1#)F3HdLxdm0ii7mya6hu)g5a6psPH9Y$?j4Gk84xP5jDEvz*+Gyk zpT}7{rM zPD_M0Qm6*rpfRkAirG+g5=OEu4MmcjzID#HD>pE`ktj-|KMdhD4skZK(yrl)VJ&Ed z18Chf4SB8ci^LFHV4!}1BHS5Wq=K?k<{&QxH#AiX(!n`fI!=7sObQ@^Rio~YQ9j4| zDSpgKi2Qvcm&yF`0)@aVu7+3F^cnKkX06u38v=v|1A`*@eY5^O+IkOCB^7)PzW)0B zii|3n*jwsZ>6%&q>Fs_`(pg&>2g^!-L4wEm6%0TU6BUww{oVru0|$e9jU9k_;zg&u zW^iOB6@_23n3$L!K762|A^Z68BQrA%J3BoyGcz~$>zkXOpG`=JLrjcUNJvOZieExP zLS9~2Sy@zFT|!Y&QCnM9Lqo&BK+(iR#oS!o+FDyrPtV-k+|EwV+1be5-4qA}y1Kgh z_*e!6*o1`GMMOBo#=80X`bI_uhJ}SCCVHi&`lO|W#m2^FWW=VXre^TblpLb)Wvike0^hnSN)9bhn}%b7hO@Iql%R#48aX_O9c^@h2`Q zAytxn4)*Cjiwlno{T*V#%BmZf+q=1yZ^s*m1<#~oW{Wc(v>*}Ro}H5jC%|TdW@b-a zKgE|R&UK13)}&@gOZBJW1zh{ZMeOM(Gx_7D;|6U<_G6?2F$BPo*`>=;`rL|khLSty zk0;gZWR?|?MBFn7(^U@CWcI~*SFYHey+#+=#Z z;v*3+XBHRuj0;3We;aqnTFc5gO5gr{>!T6Ke9Y3gllA+^PnL+5&XHBZ97mW>khosyMrDR{*X^g3fJ`rvkWDZ)nN9@%(quZ(T(DE#) zR$f@WxUsbehfv0*XC`0C|9liX01gN#2O(VtXAD|-;(^>3kN2BgJ=|Nk`CjZE&6cE) z@Qh!7bvcC|E36Jk4*fJTI~`>_79W1DT5&Zj7V@=PrKA8o*+7cqJ1ho!VxY0@o%IBu z-ROdCU`t)Yg(p7~=+6IoQ;Z##nQkA+7+v=W_#`ba6;MM`F+*@-(m+c@A&%gZ^QC^N zFSI}PIcFsN-c)|T?$e8^D~~CX;|_)>hi-p?jBI8bqv(O)ZEM%nTi zOX^C0JSv(EjGn5uOs8{uFG+%$XVRJRQ}1mT2X+L-UaTGHv>VMOM>y_YlFT4Mw2DRc78nED!sr?L?$xW%QNbf;E~ zgM`MN7{oaq(b>p$D3g!|v%Jxr>GXBLCfVild_|tcAg>&kqQu>9?S?%MXp15@_D$jW zRQh48B1IBw%x2;H#Or`?U>Wk_F-gM)+nur??y*%v9%ol&G#(F9;mZR{5$n6_9md@w zq?jrP7>7EIK_i2-XvFt-LQlr}$ju+do3}ESq+E`J505o7LmF4%LzjpYkUi`@yHUE{ zbMx-;9<##H&6-As#!EIedbC>+q>7?&;2OxB2i^?cFQf0|rTuGrxyMd>0Xm--DL#x9 zW)eOPI9HxX1h*uPaAAqWJbTyt>F){daRyJN0Yy;~n)Lc!Ib zYbVv#Z#qjD3jogF|8%yoU6@eMjSc8ET~?DgZ2aSVpNUgjBU|_9N1ab+$Q>`k=8ZkUvwR;KIQKqUMg?|OsCyh= z@=iMHW;w1d2;R8dA?;lVKaPH#2iyUX(`u!?HkG@6xS!O*6y`d9d=^T&4P=hq(_kB2 zU)ZE3<9c#$5^Z0(e%QL5qVDOtzNuNc)Ol&RyF3N32dXobpump0rNelV(>9r6rd=zi zJ;{CAceua*%Hq0&OB;|68tb;_j9rM3>iqIDlEU-+z^P&DY{OO<3GI+7vxU4oWWqQQ z4vyV0u+F1ed)_n;i#~QN*?d~%q!xyDY|b()MN~j<^YOHMVWUg_r&Z6;j)=4j%(_!P zwpnQ3N@Ur|i%aMdP3QI!pX08hagO=Y;u1&4A(e-q7oTh@KI8(|ks>e6W#VT%c%P*t zI)6trwzlv{K=78|t=}%!%MX-x1+KRNh2dND9D9CSUshn&QWfi_3fAp4a%KT=&mlF* z=;9x3)Nnm4YW+48P14=ipSN_3DbUz1Yv0y49pp^Jkf*Qu#UAdl%e=(!6uG~}c?y3T zj;Yj1Za2ZR?_@N20DPX>`qDZeQ|GW%4V(qsv~pc9+#E2s?|T#X0mdJ&)I^S zW_OzPt+(qxW8MLlPO4W=^i~pq&me>sTOG%>#=4ED+Zgo$=uaO{?W|+pG(Z3NH1@(< z>Yx)|IsI+4u(j|_3WH2B-5$B?j36(Hi|sA=iI zyT=4{&`#o3D{mmh(6agMvJ2Bz#LwGPj_+>@QW{m#qn3^j`{eeJb}gKJsqa?I>stUZ zs~Y207qO{R8we)))6D~Q%^v7yNx;Q*uCgt<@-gkLZF^sUJ)<#-ClpY*#gbQNySjSm zAkF+z%#wXQ6|nZGSu#c7v!zR-a}1^Iz{Pjcn!f%}+R(%}XF2mlvxReaX{F8DquKb! z$j)eNNzwyZ{YHzf_tcru+O~P?x^3OsOpT$j?b8)&BA2{D(}O3MmPhy}%~b^6Ru_*n z%|zm+`<#I}_pOccL%Z@Cjj0Yd++H4W*1d_ftiWgYXo|f};=xnjl07!6#)WE^%LB&7 z=Q)55MxM-EB=U0&uVz%CBd8B@ia_UyJb%w@koT@bhME4yGAOZZ0jK0)1v_d>b+rjq zmLmO~-E<|bak2K%_a<_BRSEs~i!NW)DdjDDOLu64*ovb1^Tqdd)uhSeePFGL<&eL+ zB;of8m>KAON1di{)};0^+%l7A)SOlK zAlXM-)=R7xjrZYY6pGXV0l~*KbgrV3iWbmt;d{iYI1RTrC?IXLpKWRg0{P1t_|vYE zzFNgXQ>u6##SB1@Jt33XtiuSOLvE!<(Ov+!l^5ImsBBZzYlBUFACm}B>4fSKafkUq zFe@ly)!=wpts1Siq$zS!9`1MV!%nA!A%2r7N=JE;;6Pcjy(4Eplwct8)?RMt6bY6Q zEj75>4>_+4mWyoW4a3NM^&!5GPhXG50dq|Gv{;sCRk(jtlKLI*UM+14F+p$q!1$XY z%J}*vSz7%DRA4W%Z(m&8q7f*04VTrx+PsLw$SnO_J-jRea+CIQ32YxMu!*|uqZuTU zW3H3m46=%H2r7wUDr0WtaR$cZTeyh)}mv8`YKHs2kStwc_b3jy7x<(og zx2s}hK_aijs8_W*!t@q_8q0`-Lcc^*-KM}cs=BGdMB>F1EhAGd$S=kNy;SsL?7Wq| z1)m=IAg6)FdyX88B%e}&HyXuX*_-u(IeiUlD$lAraF}il(4(HL0$~m8PLimdt6xf1K#hb4P}9p;Uc5*t@zlRC(H5+&z4r z4_r`%%g;)r+?^)720xY+dG&CJF)${a->X;070G+)d{$GA_Duy3M!1w9H_dvtzC^6oS82Cz`JXx>6g3?gkem%~m{uFTkC^>;%A!0P90i&GJ>)`d8fq_UxV zD)lxZ@&V%WAC-CIMJMxB#hKp*Bb(#FA zh6gE9(c9 z9i$?hT!Z}TsW6h68ED|pvZ<&KzJT4!qM#6_Khf(}?|nEWB1uWar6S}6gf6B5U3b8K z{Q>(n`9wi3d&;V$W6V1!i&u&p&tHVRu-<|QpP$x4Ubst8Kd;c%0{X4TRNV2j2n>sb z#2XCvo-S6g0h+$px}{AiQn7+_@-=I9n;`?VV|@0=XUs$3-2s8P1bL{*>A3S%g=%Jx za!dFW7%xf76DvKBkZkM0n^Vlu`7%aD((bjY&+=7k1{$uoANon3hTHNu?^B?~SS2Mu z*utXrYTpgSc!C@`=8OE|qDplpqGcUcdE;^?uZxt0vU9{AyiG$Fh^jHM76MQUC#C%f zU((EKT<;^^MMmgUtH1z?rcew7dQ~LZ@`lG(4X^ZG9PNn28A`Y zedNnbH#9YE5yr(;L(6q>>bOXH*@c z$YK*X+3bnBi7%be*qPhh1<;G@@Mw*XHwUPs!`p_jphIHg7DG!Rz<7h(5TtO#JDC#^ zqL&d!VU1P5y;W##GZa67QktF#DeG574W>dzsK;9j`MOYDG;qYmOko~Qt%zXMC85RV zk@>XeF3k*IHLI|#ibevSxFMT>L^PtxC?ZK~$v2-ZP#Soor&J@ugW*PlhAk0@RX&#F zo%2!1ln^R9PlG{csCC6^L6S68Cpa&Ric1Hkp{Xn6U@G{McS@GnL~UH|JEg*mBugA3 zj(Av{NsAmbe9_CSviLy$f$McD#^6{c2c3NxUF9uiqWJR6)P4L)e1G$`&weq9A-Ip)Csp zv3$WZ!6FRfJ_h78!Q^n&DOMqL#>P}srI8Rr7{gm1`P(0C&V#4lRrM4ntuB+}us0vV zvdkERyc5illhFG&SF)ywbBB9<%R%o(;B+qQ;Y}F42;Cetr+TT)%FO z5cItD`u8c&@Bi6;I}-Z;pLtohdg%rsl5~os^uL~bfr$xA3zZ9e_4((!b=4DPwnRRpTg-+kQH+}e1xJP!r}a2d z;l@DPQse~V3ByKH45h;=O79{woM%3{$u=vZLrU0+Xuu9cilZl8-l`@*Cok)Lmwn*alo#oVBuzZPe>9-qt{OEn;$yDltyH}$IyU|zulSn$D?8&4dd+{f{fE|HQ9}L(#f Z|FDJ2Ns`mEvW&OJUSTg_0{U7jx!*T!s literal 0 HcmV?d00001 From 4985d293f897c14a771e409e5fd48ac08f49f3be Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 4 Mar 2023 10:30:50 +0100 Subject: [PATCH 081/143] Remove: Do not show maintenance cost on the build airport window. --- src/airport_gui.cpp | 7 ------- src/lang/english.txt | 2 -- 2 files changed, 9 deletions(-) diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index 727a8e1f6448e..e572c96f76f85 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -436,13 +436,6 @@ class BuildAirportWindow : public PickerWindowBase { top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; } - if (_settings_game.economy.infrastructure_maintenance) { - Money monthly = _price[PR_INFRASTRUCTURE_AIRPORT] * as->maintenance_cost >> 3; - SetDParam(0, monthly * 12); - DrawString(r.left, r.right, top, TimerGameEconomy::UsingWallclockUnits() ? STR_STATION_BUILD_INFRASTRUCTURE_COST_PERIOD : STR_STATION_BUILD_INFRASTRUCTURE_COST_YEAR); - top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; - } - /* strings such as 'Size' and 'Coverage Area' */ top = DrawStationCoverageAreaText(r.left, r.right, top, SCT_ALL, rad, false) + WidgetDimensions::scaled.vsep_normal; top = DrawStationCoverageAreaText(r.left, r.right, top, SCT_ALL, rad, true); diff --git a/src/lang/english.txt b/src/lang/english.txt index b30880ac20664..c196bca6f7eba 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2781,8 +2781,6 @@ STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP :{BLACK}Don't hi STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP :{BLACK}Highlight coverage area of proposed site STR_STATION_BUILD_ACCEPTS_CARGO :{BLACK}Accepts: {GOLD}{CARGO_LIST} STR_STATION_BUILD_SUPPLIES_CARGO :{BLACK}Supplies: {GOLD}{CARGO_LIST} -STR_STATION_BUILD_INFRASTRUCTURE_COST_YEAR :{BLACK}Maintenance cost: {GOLD}{CURRENCY_SHORT}/year -STR_STATION_BUILD_INFRASTRUCTURE_COST_PERIOD :{BLACK}Maintenance cost: {GOLD}{CURRENCY_SHORT}/period # Join station window STR_JOIN_STATION_CAPTION :{WHITE}Join station From 2cb485a9e62af4c084db85bbde864396905d4273 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 6 Mar 2021 23:13:17 +0100 Subject: [PATCH 082/143] Add: Include air type definitions for airports. --- src/CMakeLists.txt | 1 + src/air_type.h | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/air_type.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ad909c65f568f..13d5ea183ad5b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -40,6 +40,7 @@ add_files( ) add_files( + air_type.h aircraft.h aircraft_cmd.cpp aircraft_cmd.h diff --git a/src/air_type.h b/src/air_type.h new file mode 100644 index 0000000000000..9a5b26683b940 --- /dev/null +++ b/src/air_type.h @@ -0,0 +1,89 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file air_type.h The different types of air tracks. */ + +#ifndef AIR_TYPE_H +#define AIR_TYPE_H + +#include "core/enum_type.hpp" + +typedef uint32_t AirTypeLabel; + +/** Enumeration for all possible airtypes. */ +enum AirType : uint8_t { + AIRTYPE_BEGIN = 0, ///< Used for iterations + AIRTYPE_GRAVEL = 0, ///< Gravel surface + AIRTYPE_ASPHALT = 1, ///< Asphalt surface + AIRTYPE_WATER = 2, ///< Water surface + AIRTYPE_END = 16, ///< Used for iterations + INVALID_AIRTYPE = 0xFF, ///< Flag for invalid airtype + + DEF_AIRTYPE_FIRST = AIRTYPE_END, ///< Default airtype: first available + DEF_AIRTYPE_LAST, ///< Default airtype: last available + DEF_AIRTYPE_MOST_USED, ///< Default airtype: most used +}; + +/** Allow incrementing of airtype variables */ +DECLARE_POSTFIX_INCREMENT(AirType) + +/** The different airtypes we support, but then a bitmask of them. */ +enum AirTypes : uint64_t { + AIRTYPES_NONE = 0, ///< No rail types + AIRTYPES_GRAVEL = 1 << AIRTYPE_GRAVEL, ///< Gravel surface + AIRTYPES_ASPHALT = 1 << AIRTYPE_ASPHALT, ///< Asphalt surface + AIRTYPES_WATER = 1 << AIRTYPE_WATER, ///< Water surface + AIRTYPES_ALL = AIRTYPES_GRAVEL | AIRTYPES_ASPHALT | AIRTYPES_WATER, + INVALID_AIRTYPES = UINT16_MAX, ///< Invalid airtypes +}; +DECLARE_ENUM_AS_BIT_SET(AirTypes) + +/** Types of tiles an airport can have. */ +enum AirportTileType : uint8_t { + ATT_BEGIN = 0, + ATT_INFRASTRUCTURE_NO_CATCH = ATT_BEGIN, // 0000 + ATT_INFRASTRUCTURE_WITH_CATCH = 1, // 0001 + ATT_SIMPLE_TRACK = 2, // 0010 + ATT_WAITING_POINT = 3, // 0011 + ATT_APRON_NORMAL = 4, // 0100 + ATT_APRON_HELIPAD = 5, // 0101 + ATT_APRON_HELIPORT = 6, // 0110 + ATT_APRON_BUILTIN_HELIPORT = 7, // 0111 + ATT_HANGAR_STANDARD = 8, // 1000 + ATT_HANGAR_EXTENDED = 10, // 1010 + ATT_RUNWAY_MIDDLE = 12, // 1100 + ATT_RUNWAY_END = 13, // 1101 + ATT_RUNWAY_START_NO_LANDING = 14, // 1110 + ATT_RUNWAY_START_ALLOW_LANDING = 15, // 1111 + ATT_END, + + ATT_INVALID, + + ATT_NUM_BITS = 4, + ATT_INFRA_LAYOUT_NUM_BITS = 3, + ATT_INFRA_LAYOUT_BITS = 0, + ATT_APRON_LAYOUT_NUM_BITS = 2, + ATT_APRON_LAYOUT_BITS = 1, + ATT_HANGAR_LAYOUT_NUM_BITS = 2, + ATT_HANGAR_LAYOUT_BITS = 2, + ATT_RUNWAY_LAYOUT_NUM_BITS = 2, + ATT_RUNWAY_LAYOUT_BITS = 3, + ATT_RUNWAY_START_LAYOUT_NUM_BITS = 3, + ATT_RUNWAY_START_LAYOUT_BITS = 7, +}; + +enum ApronType : uint8_t { + APRON_BEGIN = 0, + APRON_APRON = APRON_BEGIN, + APRON_HELIPAD, + APRON_HELIPORT, + APRON_BUILTIN_HELIPORT, + APRON_END, + APRON_INVALID = APRON_END, +}; + +#endif /* AIR_TYPE_H */ From a42a0054bdac2a358f7730500da75230413d1f2e Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 20:41:06 +0200 Subject: [PATCH 083/143] Add: Add airtype for each aircraft engine. # Conflicts: # src/engine_type.h --- src/engine_type.h | 2 + src/table/engines.h | 99 +++++++++++++++++++++++++-------------------- 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/src/engine_type.h b/src/engine_type.h index d5a28ced3ea15..f7fcbdd8e3c60 100644 --- a/src/engine_type.h +++ b/src/engine_type.h @@ -11,6 +11,7 @@ #define ENGINE_TYPE_H #include "economy_type.h" +#include "air_type.h" #include "rail_type.h" #include "road_type.h" #include "cargo_type.h" @@ -108,6 +109,7 @@ struct AircraftVehicleInfo { uint8_t mail_capacity; ///< Mail capacity (bags). uint16_t passenger_capacity; ///< Passenger capacity (persons). uint16_t max_range; ///< Maximum range of this aircraft. + AirType airtype; ///< Airport tile types this aircraft can use. }; /** Information about a road vehicle. */ diff --git a/src/table/engines.h b/src/table/engines.h index e1f42c1c1ed6c..6a6fa19f6d068 100644 --- a/src/table/engines.h +++ b/src/table/engines.h @@ -542,6 +542,7 @@ static const RailVehicleInfo _orig_rail_vehicle_info[] = { #undef O #undef C #undef R +#undef A #undef V #undef N #undef E @@ -596,62 +597,74 @@ static const ShipVehicleInfo _orig_ship_vehicle_info[] = { * @param g max_speed (1 unit = 8 mph = 12.8 km-ish/h) (is converted to km-ish/h by the macro) * @param h mail_capacity (bags) * @param i passenger_capacity (persons) + * @param j air type */ -#define AVI(a, b, c, d, e, f, g, h, i) { a, b, c, d, e, f, (g * 128) / 10, h, i, 0 } +#define AVI(a, b, c, d, e, f, g, h, i, j) { a, b, c, d, e, f, (g * 128) / 10, h, i, 0, j } #define H AIR_HELI #define P AIR_CTOL #define J AIR_CTOL | AIR_FAST + +#define G AIRTYPE_GRAVEL +#define A AIRTYPE_ASPHALT +#define W AIRTYPE_WATER + static const AircraftVehicleInfo _orig_aircraft_vehicle_info[] = { /* image_index sfx acceleration * | cost_factor | | max_speed * | | running_cost | | mail_capacity * | | | subtype | | | | passenger_capacity - * | | | | | | | | | */ - AVI( 1, 14, 85, P, SND_08_TAKEOFF_PROPELLER, 18, 37, 4, 25 ), // 0 Sampson U52 - AVI( 0, 15, 100, P, SND_08_TAKEOFF_PROPELLER, 20, 37, 8, 65 ), // 1 Coleman Count - AVI( 2, 16, 130, J, SND_09_TAKEOFF_JET, 35, 74, 10, 90 ), // 2 FFP Dart - AVI( 8, 75, 250, J, SND_3B_TAKEOFF_JET_FAST, 50, 181, 20, 100 ), // 3 Yate Haugan - AVI( 5, 15, 98, P, SND_08_TAKEOFF_PROPELLER, 20, 37, 6, 30 ), // 4 Bakewell Cotswald LB-3 - AVI( 6, 18, 240, J, SND_09_TAKEOFF_JET, 40, 74, 30, 200 ), // 5 Bakewell Luckett LB-8 - AVI( 2, 17, 150, P, SND_09_TAKEOFF_JET, 35, 74, 15, 100 ), // 6 Bakewell Luckett LB-9 - AVI( 2, 18, 245, J, SND_09_TAKEOFF_JET, 40, 74, 30, 150 ), // 7 Bakewell Luckett LB80 - AVI( 3, 19, 192, J, SND_09_TAKEOFF_JET, 40, 74, 40, 220 ), // 8 Bakewell Luckett LB-10 - AVI( 3, 20, 190, J, SND_09_TAKEOFF_JET, 40, 74, 25, 230 ), // 9 Bakewell Luckett LB-11 - AVI( 2, 16, 135, J, SND_09_TAKEOFF_JET, 35, 74, 10, 95 ), // 10 Yate Aerospace YAC 1-11 - AVI( 2, 18, 240, J, SND_09_TAKEOFF_JET, 40, 74, 35, 170 ), // 11 Darwin 100 - AVI( 4, 17, 155, J, SND_09_TAKEOFF_JET, 40, 74, 15, 110 ), // 12 Darwin 200 - AVI( 7, 30, 253, J, SND_3D_TAKEOFF_JET_BIG, 40, 74, 50, 300 ), // 13 Darwin 300 - AVI( 4, 18, 210, J, SND_09_TAKEOFF_JET, 40, 74, 25, 200 ), // 14 Darwin 400 - AVI( 4, 19, 220, J, SND_09_TAKEOFF_JET, 40, 74, 25, 240 ), // 15 Darwin 500 - AVI( 4, 27, 230, J, SND_09_TAKEOFF_JET, 40, 74, 40, 260 ), // 16 Darwin 600 - AVI( 3, 25, 225, J, SND_09_TAKEOFF_JET, 40, 74, 35, 240 ), // 17 Guru Galaxy - AVI( 4, 20, 235, J, SND_09_TAKEOFF_JET, 40, 74, 30, 260 ), // 18 Airtaxi A21 - AVI( 4, 19, 220, J, SND_09_TAKEOFF_JET, 40, 74, 25, 210 ), // 19 Airtaxi A31 - AVI( 4, 18, 170, J, SND_09_TAKEOFF_JET, 40, 74, 20, 160 ), // 20 Airtaxi A32 - AVI( 4, 26, 210, J, SND_09_TAKEOFF_JET, 40, 74, 20, 220 ), // 21 Airtaxi A33 - AVI( 6, 16, 125, P, SND_09_TAKEOFF_JET, 50, 74, 10, 80 ), // 22 Yate Aerospace YAe46 - AVI( 2, 17, 145, P, SND_09_TAKEOFF_JET, 40, 74, 10, 85 ), // 23 Dinger 100 - AVI( 11, 16, 130, P, SND_09_TAKEOFF_JET, 40, 74, 10, 75 ), // 24 AirTaxi A34-1000 - AVI( 10, 16, 149, P, SND_09_TAKEOFF_JET, 40, 74, 10, 85 ), // 25 Yate Z-Shuttle - AVI( 15, 17, 170, P, SND_09_TAKEOFF_JET, 40, 74, 18, 65 ), // 26 Kelling K1 - AVI( 12, 18, 210, J, SND_09_TAKEOFF_JET, 40, 74, 25, 110 ), // 27 Kelling K6 - AVI( 13, 20, 230, J, SND_09_TAKEOFF_JET, 40, 74, 60, 180 ), // 28 Kelling K7 - AVI( 14, 21, 220, J, SND_09_TAKEOFF_JET, 40, 74, 65, 150 ), // 29 Darwin 700 - AVI( 16, 19, 160, J, SND_09_TAKEOFF_JET, 40, 181, 45, 85 ), // 30 FFP Hyperdart 2 - AVI( 17, 24, 248, J, SND_3D_TAKEOFF_JET_BIG, 40, 74, 80, 400 ), // 31 Dinger 200 - AVI( 18, 80, 251, J, SND_3B_TAKEOFF_JET_FAST, 50, 181, 45, 130 ), // 32 Dinger 1000 - AVI( 20, 13, 85, P, SND_45_TAKEOFF_PROPELLER_TOYLAND_1, 18, 37, 5, 25 ), // 33 Ploddyphut 100 - AVI( 21, 18, 100, P, SND_46_TAKEOFF_PROPELLER_TOYLAND_2, 20, 37, 9, 60 ), // 34 Ploddyphut 500 - AVI( 22, 25, 140, P, SND_09_TAKEOFF_JET, 40, 74, 12, 90 ), // 35 Flashbang X1 - AVI( 23, 32, 220, J, SND_3D_TAKEOFF_JET_BIG, 40, 74, 40, 200 ), // 36 Juggerplane M1 - AVI( 24, 80, 255, J, SND_3B_TAKEOFF_JET_FAST, 50, 181, 30, 100 ), // 37 Flashbang Wizzer - AVI( 9, 15, 81, H, SND_09_TAKEOFF_JET, 20, 25, 15, 40 ), // 38 Tricario Helicopter - AVI( 19, 17, 77, H, SND_09_TAKEOFF_JET, 20, 40, 20, 55 ), // 39 Guru X2 Helicopter - AVI( 25, 15, 80, H, SND_09_TAKEOFF_JET, 20, 25, 10, 40 ), // 40 Powernaut Helicopter + * | | | | | | | | | air type (gravel, asphalt, water...) + * | | | | | | | | | | */ + AVI( 1, 14, 85, P, SND_08_TAKEOFF_PROPELLER, 18, 37, 4, 25, G ), // 0 Sampson U52 + AVI( 0, 15, 100, P, SND_08_TAKEOFF_PROPELLER, 20, 37, 8, 65, G ), // 1 Coleman Count + AVI( 2, 16, 130, J, SND_09_TAKEOFF_JET, 35, 74, 10, 90, A ), // 2 FFP Dart + AVI( 8, 75, 250, J, SND_3B_TAKEOFF_JET_FAST, 50, 181, 20, 100, A ), // 3 Yate Haugan + AVI( 5, 15, 98, P, SND_08_TAKEOFF_PROPELLER, 20, 37, 6, 30, G ), // 4 Bakewell Cotswald LB-3 + AVI( 6, 18, 240, J, SND_09_TAKEOFF_JET, 40, 74, 30, 200, A ), // 5 Bakewell Luckett LB-8 + AVI( 2, 17, 150, P, SND_09_TAKEOFF_JET, 35, 74, 15, 100, G ), // 6 Bakewell Luckett LB-9 + AVI( 2, 18, 245, J, SND_09_TAKEOFF_JET, 40, 74, 30, 150, A ), // 7 Bakewell Luckett LB80 + AVI( 3, 19, 192, J, SND_09_TAKEOFF_JET, 40, 74, 40, 220, A ), // 8 Bakewell Luckett LB-10 + AVI( 3, 20, 190, J, SND_09_TAKEOFF_JET, 40, 74, 25, 230, A ), // 9 Bakewell Luckett LB-11 + AVI( 2, 16, 135, J, SND_09_TAKEOFF_JET, 35, 74, 10, 95, A ), // 10 Yate Aerospace YAC 1-11 + AVI( 2, 18, 240, J, SND_09_TAKEOFF_JET, 40, 74, 35, 170, A ), // 11 Darwin 100 + AVI( 4, 17, 155, J, SND_09_TAKEOFF_JET, 40, 74, 15, 110, A ), // 12 Darwin 200 + AVI( 7, 30, 253, J, SND_3D_TAKEOFF_JET_BIG, 40, 74, 50, 300, A ), // 13 Darwin 300 + AVI( 4, 18, 210, J, SND_09_TAKEOFF_JET, 40, 74, 25, 200, A ), // 14 Darwin 400 + AVI( 4, 19, 220, J, SND_09_TAKEOFF_JET, 40, 74, 25, 240, A ), // 15 Darwin 500 + AVI( 4, 27, 230, J, SND_09_TAKEOFF_JET, 40, 74, 40, 260, A ), // 16 Darwin 600 + AVI( 3, 25, 225, J, SND_09_TAKEOFF_JET, 40, 74, 35, 240, A ), // 17 Guru Galaxy + AVI( 4, 20, 235, J, SND_09_TAKEOFF_JET, 40, 74, 30, 260, A ), // 18 Airtaxi A21 + AVI( 4, 19, 220, J, SND_09_TAKEOFF_JET, 40, 74, 25, 210, A ), // 19 Airtaxi A31 + AVI( 4, 18, 170, J, SND_09_TAKEOFF_JET, 40, 74, 20, 160, A ), // 20 Airtaxi A32 + AVI( 4, 26, 210, J, SND_09_TAKEOFF_JET, 40, 74, 20, 220, A ), // 21 Airtaxi A33 + AVI( 6, 16, 125, P, SND_09_TAKEOFF_JET, 50, 74, 10, 80, G ), // 22 Yate Aerospace YAe46 + AVI( 2, 17, 145, P, SND_09_TAKEOFF_JET, 40, 74, 10, 85, G ), // 23 Dinger 100 + AVI( 11, 16, 130, P, SND_09_TAKEOFF_JET, 40, 74, 10, 75, G ), // 24 AirTaxi A34-1000 + AVI( 10, 16, 149, P, SND_09_TAKEOFF_JET, 40, 74, 10, 85, G ), // 25 Yate Z-Shuttle + AVI( 15, 17, 170, P, SND_09_TAKEOFF_JET, 40, 74, 18, 65, G ), // 26 Kelling K1 + AVI( 12, 18, 210, J, SND_09_TAKEOFF_JET, 40, 74, 25, 110, A ), // 27 Kelling K6 + AVI( 13, 20, 230, J, SND_09_TAKEOFF_JET, 40, 74, 60, 180, A ), // 28 Kelling K7 + AVI( 14, 21, 220, J, SND_09_TAKEOFF_JET, 40, 74, 65, 150, A ), // 29 Darwin 700 + AVI( 16, 19, 160, J, SND_09_TAKEOFF_JET, 40, 181, 45, 85, A ), // 30 FFP Hyperdart 2 + AVI( 17, 24, 248, J, SND_3D_TAKEOFF_JET_BIG, 40, 74, 80, 400, A ), // 31 Dinger 200 + AVI( 18, 80, 251, J, SND_3B_TAKEOFF_JET_FAST, 50, 181, 45, 130, A ), // 32 Dinger 1000 + AVI( 20, 13, 85, P, SND_45_TAKEOFF_PROPELLER_TOYLAND_1, 18, 37, 5, 25, G ), // 33 Ploddyphut 100 + AVI( 21, 18, 100, P, SND_46_TAKEOFF_PROPELLER_TOYLAND_2, 20, 37, 9, 60, G ), // 34 Ploddyphut 500 + AVI( 22, 25, 140, P, SND_09_TAKEOFF_JET, 40, 74, 12, 90, G ), // 35 Flashbang X1 + AVI( 23, 32, 220, J, SND_3D_TAKEOFF_JET_BIG, 40, 74, 40, 200, A ), // 36 Juggerplane M1 + AVI( 24, 80, 255, J, SND_3B_TAKEOFF_JET_FAST, 50, 181, 30, 100, A ), // 37 Flashbang Wizzer + AVI( 9, 15, 81, H, SND_09_TAKEOFF_JET, 20, 25, 15, 40, G ), // 38 Tricario Helicopter + AVI( 19, 17, 77, H, SND_09_TAKEOFF_JET, 20, 40, 20, 55, G ), // 39 Guru X2 Helicopter + AVI( 25, 15, 80, H, SND_09_TAKEOFF_JET, 20, 25, 10, 40, G ), // 40 Powernaut Helicopter }; #undef J #undef P #undef H + +#undef G +#undef A +#undef W + #undef AVI /** From 489989bfc32c5c6e66ed0b60c58385c01cc8d4a6 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Fri, 25 Aug 2023 14:49:03 +0200 Subject: [PATCH 084/143] Add: Include sprites for airtypes. --- media/baseset/openttd.grf | Bin 554417 -> 1023208 bytes media/baseset/openttd.grf.hash | 2 +- media/baseset/openttd/CMakeLists.txt | 13 + media/baseset/openttd/airport_asphalt.png | Bin 0 -> 68333 bytes media/baseset/openttd/airport_gravel.png | Bin 0 -> 86716 bytes media/baseset/openttd/airport_water.png | Bin 0 -> 61063 bytes media/baseset/openttd/airtypes.nfo | 473 ++++++++++++++ media/baseset/openttd/openttd.nfo | 1 + media/baseset/openttd/preview_city.png | Bin 0 -> 26221 bytes media/baseset/openttd/preview_commuter.png | Bin 0 -> 18229 bytes media/baseset/openttd/preview_helidepot.png | Bin 0 -> 19786 bytes media/baseset/openttd/preview_heliport.png | Bin 0 -> 21455 bytes media/baseset/openttd/preview_helistation.png | Bin 0 -> 21014 bytes .../openttd/preview_intercontinental.png | Bin 0 -> 19724 bytes .../baseset/openttd/preview_international.png | Bin 0 -> 24429 bytes .../baseset/openttd/preview_metropolitan.png | Bin 0 -> 25215 bytes media/baseset/openttd/preview_small.png | Bin 0 -> 52963 bytes .../openttd/preview_small_temperate.png | Bin 0 -> 34438 bytes src/airport.h | 4 +- src/newgrf.cpp | 1 + src/newgrf_airporttiles.cpp | 2 +- src/table/airporttile_ids.h | 19 +- src/table/airporttiles.h | 21 + src/table/sprites.h | 38 +- src/table/station_land.h | 598 +++++++++++++++++- 25 files changed, 1164 insertions(+), 8 deletions(-) create mode 100755 media/baseset/openttd/airport_asphalt.png create mode 100755 media/baseset/openttd/airport_gravel.png create mode 100755 media/baseset/openttd/airport_water.png create mode 100644 media/baseset/openttd/airtypes.nfo create mode 100755 media/baseset/openttd/preview_city.png create mode 100644 media/baseset/openttd/preview_commuter.png create mode 100755 media/baseset/openttd/preview_helidepot.png create mode 100755 media/baseset/openttd/preview_heliport.png create mode 100755 media/baseset/openttd/preview_helistation.png create mode 100755 media/baseset/openttd/preview_intercontinental.png create mode 100755 media/baseset/openttd/preview_international.png create mode 100755 media/baseset/openttd/preview_metropolitan.png create mode 100755 media/baseset/openttd/preview_small.png create mode 100755 media/baseset/openttd/preview_small_temperate.png diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf index 6cee79959ece163e16291971c1397b1bb5f231b3..519aa8fa8875a9cd50b821b96bcdf03bf7f25dea 100644 GIT binary patch delta 466683 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

    ?N$!TB+n#7CZW3c<7{Cw>aZ4;W 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