From f8bc259ec06ebe9c4b0fd37e34b416d2e4a52571 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Wed, 10 May 2023 20:22:04 +0200 Subject: [PATCH] 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 757f18d371..026fe129cb 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 542abc5f62..0ba6ee3cd3 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 9c8a732afd..41512153bf 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 31eb43322d..2b251064e4 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 9252e3d516..203d7784f2 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 5c88f10c44..96adeb23ab 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 8577169c32..498817772d 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 bbf1e04365..be4307307c 100644 --- a/src/train.h +++ b/src/train.h @@ -353,4 +353,6 @@ protected: // These functions should not be called outside acceleration code. } }; +bool HasCompatibleDepotTile(TileIndex tile, const Train *t); + #endif /* TRAIN_H */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 9b77dddd04..d6c65140ca 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;