1
0
Fork 0
pull/9577/merge
Joan Josep 2024-07-29 23:12:14 +03:00 committed by GitHub
commit b88e6c0cd8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
74 changed files with 2004 additions and 550 deletions

View File

@ -41,6 +41,7 @@
#include "framerate_type.h" #include "framerate_type.h"
#include "aircraft_cmd.h" #include "aircraft_cmd.h"
#include "vehicle_cmd.h" #include "vehicle_cmd.h"
#include "depot_base.h"
#include "table/strings.h" #include "table/strings.h"
@ -136,7 +137,7 @@ static StationID FindNearestHangar(const Aircraft *v)
if (v->current_order.IsType(OT_GOTO_STATION) || if (v->current_order.IsType(OT_GOTO_STATION) ||
(v->current_order.IsType(OT_GOTO_DEPOT) && (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0)) { (v->current_order.IsType(OT_GOTO_DEPOT) && (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0)) {
last_dest = Station::GetIfValid(v->last_station_visited); 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 { } else {
last_dest = GetTargetAirportIfValid(v); last_dest = GetTargetAirportIfValid(v);
next_dest = Station::GetIfValid(v->GetNextStoppingStation().value); next_dest = Station::GetIfValid(v->GetNextStoppingStation().value);
@ -407,9 +408,10 @@ ClosestDepot Aircraft::FindClosestDepot()
if (station == INVALID_STATION) return ClosestDepot(); if (station == INVALID_STATION) return ClosestDepot();
st = Station::Get(station); 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) 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. */ * 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; 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); assert(st != nullptr);
/* only goto depot if the target airport has a depot */ /* only goto depot if the target airport has a depot */
if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) { 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); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
} else if (v->current_order.IsType(OT_GOTO_DEPOT)) { } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
v->current_order.MakeDummy(); v->current_order.MakeDummy();
@ -892,7 +894,7 @@ static bool AircraftController(Aircraft *v)
/* Jump into our "holding pattern" state machine if possible */ /* Jump into our "holding pattern" state machine if possible */
if (v->pos >= afc->nofelements) { if (v->pos >= afc->nofelements) {
v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N); 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 */ /* If not possible, just get out of here fast */
v->state = FLYING; v->state = FLYING;
UpdateAircraftCache(v); UpdateAircraftCache(v);
@ -1449,7 +1451,7 @@ static void AircraftLandAirplane(Aircraft *v)
void AircraftNextAirportPos_and_Order(Aircraft *v) void AircraftNextAirportPos_and_Order(Aircraft *v)
{ {
if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) { 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); const Station *st = GetTargetAirportIfValid(v);
@ -1488,7 +1490,9 @@ void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
VehicleServiceInDepot(v); VehicleServiceInDepot(v);
v->LeaveUnbunchingDepot(); v->LeaveUnbunchingDepot();
SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos); 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); SetWindowClassesDirty(WC_AIRCRAFT_LIST);
} }
@ -1539,7 +1543,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap
return; return;
/* We are leaving a hangar, but have to go to the exact same one; re-enter */ /* 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); VehicleEnterDepot(v);
return; return;
} }
@ -1548,7 +1552,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap
if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return; if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
/* We are already at the target airport, we need to find a terminal */ /* 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: /* FindFreeTerminal:
* 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */ * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
if (v->subtype == AIR_HELICOPTER) { if (v->subtype == AIR_HELICOPTER) {
@ -1599,7 +1603,7 @@ static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *
case OT_GOTO_STATION: // ready to fly to another airport case OT_GOTO_STATION: // ready to fly to another airport
break; break;
case OT_GOTO_DEPOT: // visit hangar for servicing, sale, etc. 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; break;
case OT_CONDITIONAL: case OT_CONDITIONAL:
/* In case of a conditional order we just have to wait a tick /* In case of a conditional order we just have to wait a tick
@ -2103,7 +2107,7 @@ static bool AircraftEventHandler(Aircraft *v, int loop)
/* Check the distance to the next destination. This code works because the target /* 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. */ * airport is only updated after take off and not on the ground. */
Station *cur_st = Station::GetIfValid(v->targetairport); 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) { 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); uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
@ -2169,18 +2173,7 @@ void UpdateAirplanesOnNewStation(const Station *st)
if (!v->IsNormalAircraft() || v->targetairport != st->index) continue; if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
assert(v->state == FLYING); 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); v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
UpdateAircraftCache(v); 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);
} }

View File

@ -71,7 +71,10 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
switch (type) { switch (type) {
case VEH_TRAIN: { case VEH_TRAIN: {
/* make sure the railtypes are compatible */ /* 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 */ /* 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; 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: case VEH_ROAD:
if (!_settings_game.depot.allow_no_comp_roadtype_replacements) {
/* make sure the roadtypes are compatible */ /* 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 ((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 */ /* 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; if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false;
}
break; break;
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
@ -519,6 +526,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
{ {
Vehicle *old_head = *chain; Vehicle *old_head = *chain;
assert(old_head->IsPrimaryVehicle()); assert(old_head->IsPrimaryVehicle());
TileIndex tile = old_head->tile;
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0); CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0);
@ -661,6 +669,9 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
if ((flags & DC_EXEC) != 0) CheckCargoCapacity(new_head); 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. /* 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. * 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 :) */ * Note: The vehicle attach callback is disabled here :) */

View File

@ -38,6 +38,7 @@
#include "querystring_gui.h" #include "querystring_gui.h"
#include "stringfilter_type.h" #include "stringfilter_type.h"
#include "hotkeys.h" #include "hotkeys.h"
#include "depot_base.h"
#include "widgets/build_vehicle_widget.h" #include "widgets/build_vehicle_widget.h"
@ -1163,12 +1164,31 @@ enum BuildVehicleHotkeys {
BVHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string 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. */ /** GUI for building vehicles. */
struct BuildVehicleWindow : Window { struct BuildVehicleWindow : Window {
VehicleType vehicle_type; ///< Type of vehicles shown in the window. VehicleType vehicle_type; ///< Type of vehicles shown in the window.
union { union {
RailType railtype; ///< Rail type to show, or #INVALID_RAILTYPE. RailTypes railtypes; ///< Rail types to show, or #INVALID_RAILTYPES.
RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE. RoadTypes roadtypes; ///< Road types to show, or #INVALID_ROADTYPES.
} filter; ///< Filter to apply. } filter; ///< Filter to apply.
bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction
uint8_t sort_criteria; ///< Current sort criterium. uint8_t sort_criteria; ///< Current sort criterium.
@ -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->vehicle_type = type;
this->listview_mode = tile == INVALID_TILE; this->listview_mode = depot_id == INVALID_DEPOT;
this->window_number = this->listview_mode ? (int)type : tile.base(); this->window_number = GetBuildVehicleWindowNumber(depot_id, type);
this->sel_engine = INVALID_ENGINE; this->sel_engine = INVALID_ENGINE;
@ -1240,16 +1260,13 @@ struct BuildVehicleWindow : Window {
this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9); this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9);
if (tile == INVALID_TILE) { this->FinishInitNested(this->window_number);
this->FinishInitNested(type);
} else {
this->FinishInitNested(tile);
}
this->querystrings[WID_BV_FILTER] = &this->vehicle_editbox; this->querystrings[WID_BV_FILTER] = &this->vehicle_editbox;
this->vehicle_editbox.cancel_button = QueryString::ACTION_CLEAR; 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 ? depot->owner : _local_company;
this->eng_list.ForceRebuild(); this->eng_list.ForceRebuild();
this->GenerateBuildList(); // generate the list, since we need it in the next line this->GenerateBuildList(); // generate the list, since we need it in the next line
@ -1264,25 +1281,16 @@ struct BuildVehicleWindow : Window {
/** Set the filter type according to the depot type */ /** Set the filter type according to the depot type */
void UpdateFilterByTile() void UpdateFilterByTile()
{ {
Depot *depot = this->listview_mode ? nullptr : Depot::Get(this->window_number);
switch (this->vehicle_type) { switch (this->vehicle_type) {
default: NOT_REACHED(); default: NOT_REACHED();
case VEH_TRAIN: case VEH_TRAIN:
if (this->listview_mode) { this->filter.railtypes = this->listview_mode ? INVALID_RAILTYPES : depot->r_types.rail_types;
this->filter.railtype = INVALID_RAILTYPE;
} else {
this->filter.railtype = GetRailType(this->window_number);
}
break; break;
case VEH_ROAD: case VEH_ROAD:
if (this->listview_mode) { this->filter.roadtypes = this->listview_mode ? INVALID_ROADTYPES : depot->r_types.road_types;
this->filter.roadtype = INVALID_ROADTYPE;
} else {
this->filter.roadtype = GetRoadTypeRoad(this->window_number);
if (this->filter.roadtype == INVALID_ROADTYPE) {
this->filter.roadtype = GetRoadTypeTram(this->window_number);
}
}
break; break;
case VEH_SHIP: case VEH_SHIP:
@ -1326,7 +1334,7 @@ struct BuildVehicleWindow : Window {
if (!this->listview_mode) { if (!this->listview_mode) {
/* Query for cost and refitted capacity */ /* Query for cost and refitted capacity */
auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command<CMD_BUILD_VEHICLE>::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<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, Depot::Get(this->window_number)->xy, this->sel_engine, true, cargo, INVALID_CLIENT_ID);
if (ret.Succeeded()) { if (ret.Succeeded()) {
this->te.cost = ret.GetCost() - e->GetCost(); this->te.cost = ret.GetCost() - e->GetCost();
this->te.capacity = refit_capacity; this->te.capacity = refit_capacity;
@ -1401,7 +1409,7 @@ struct BuildVehicleWindow : Window {
EngineID eid = e->index; EngineID eid = e->index;
const RailVehicleInfo *rvi = &e->u.rail; 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; if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
/* Filter now! So num_engines and num_wagons is valid */ /* Filter now! So num_engines and num_wagons is valid */
@ -1461,7 +1469,7 @@ struct BuildVehicleWindow : Window {
if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
EngineID eid = e->index; EngineID eid = e->index;
if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue; 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 */ /* Filter by name or NewGRF extra text */
if (!FilterByText(e)) continue; if (!FilterByText(e)) continue;
@ -1501,7 +1509,7 @@ struct BuildVehicleWindow : Window {
this->eng_list.clear(); 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. /* Make list of all available planes.
* Also check to see if the previously selected plane is still available, * Also check to see if the previously selected plane is still available,
@ -1613,10 +1621,15 @@ struct BuildVehicleWindow : Window {
CargoID cargo = this->cargo_filter_criteria; CargoID cargo = this->cargo_filter_criteria;
if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO; 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) { if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) {
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, depot->xy, sel_eng, true, cargo, INVALID_CLIENT_ID);
} else { } else {
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); Command<CMD_BUILD_VEHICLE>::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. */ /* Update last used variant in hierarchy and refresh if necessary. */
@ -1736,11 +1749,21 @@ struct BuildVehicleWindow : Window {
switch (widget) { switch (widget) {
case WID_BV_CAPTION: case WID_BV_CAPTION:
if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) { if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
const RailTypeInfo *rti = GetRailTypeInfo(this->filter.railtype); 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); SetDParam(0, rti->strings.build_caption);
}
} else if (this->vehicle_type == VEH_ROAD && !this->listview_mode) { } else if (this->vehicle_type == VEH_ROAD && !this->listview_mode) {
const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype); 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); SetDParam(0, rti->strings.build_caption);
}
} else { } else {
SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type); SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
} }
@ -1930,17 +1953,11 @@ static WindowDesc _build_vehicle_desc(
&BuildVehicleWindow::hotkeys &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)); 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);
} }

View File

@ -193,6 +193,7 @@ enum Commands : uint16_t {
CMD_BUILD_BRIDGE, ///< build a bridge CMD_BUILD_BRIDGE, ///< build a bridge
CMD_BUILD_RAIL_STATION, ///< build a rail station CMD_BUILD_RAIL_STATION, ///< build a rail station
CMD_BUILD_TRAIN_DEPOT, ///< build a train depot CMD_BUILD_TRAIN_DEPOT, ///< build a train depot
CMD_REMOVE_TRAIN_DEPOT, ///< remove a train depot
CMD_BUILD_SINGLE_SIGNAL, ///< build a signal CMD_BUILD_SINGLE_SIGNAL, ///< build a signal
CMD_REMOVE_SINGLE_SIGNAL, ///< remove a signal CMD_REMOVE_SINGLE_SIGNAL, ///< remove a signal
CMD_TERRAFORM_LAND, ///< terraform a tile CMD_TERRAFORM_LAND, ///< terraform a tile

View File

@ -15,9 +15,13 @@
#include "core/pool_func.hpp" #include "core/pool_func.hpp"
#include "vehicle_gui.h" #include "vehicle_gui.h"
#include "vehiclelist.h" #include "vehiclelist.h"
#include "command_func.h"
#include "vehicle_base.h"
#include "safeguards.h" #include "safeguards.h"
#include "table/strings.h"
/** All our depots tucked away in a pool. */ /** All our depots tucked away in a pool. */
DepotPool _depot_pool("Depot"); DepotPool _depot_pool("Depot");
INSTANTIATE_POOL_METHODS(Depot) INSTANTIATE_POOL_METHODS(Depot)
@ -29,21 +33,146 @@ Depot::~Depot()
{ {
if (CleaningPool()) return; if (CleaningPool()) return;
if (!IsDepotTile(this->xy) || GetDepotIndex(this->xy) != this->index) { if (this->owner == INVALID_OWNER) {
/* It can happen there is no depot here anymore (TTO/TTD savegames) */ /* Deleting depot remnants of TTD savegames while saveload conversion. */
assert(this->veh_type == VEH_INVALID);
return; return;
} }
/* Clear the order backup. */ /* Clear the order backup. */
OrderBackup::Reset(this->xy, false); 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 */ /* Clear the depot from all order-lists */
RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->index); RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->index);
/* Delete the depot-window */ /* Delete the depot-window */
CloseWindowById(WC_VEHICLE_DEPOT, this->xy); CloseWindowById(WC_VEHICLE_DEPOT, this->index);
/* Delete the depot list */ /* Delete the depot list */
VehicleType vt = GetDepotVehicleType(this->xy); CloseWindowById(GetWindowClassForVehicleType(this->veh_type),
CloseWindowById(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_DEPOT_LIST, vt, GetTileOwner(this->xy), this->index).Pack()); VehicleListIdentifier(VL_DEPOT_LIST,
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.
* @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));
}
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();
}
/**
* 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];
InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type);
} 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);
}
} }

View File

@ -13,10 +13,23 @@
#include "depot_map.h" #include "depot_map.h"
#include "core/pool_type.hpp" #include "core/pool_type.hpp"
#include "timer/timer_game_calendar.h" #include "timer/timer_game_calendar.h"
#include "rail_type.h"
#include "road_type.h"
typedef Pool<Depot, DepotID, 64, 64000> 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<Depot, DepotID, 64, MAX_DEPOTS> DepotPool;
extern DepotPool _depot_pool; extern DepotPool _depot_pool;
class CommandCost;
struct Vehicle;
struct Depot : DepotPool::PoolItem<&_depot_pool> { struct Depot : DepotPool::PoolItem<&_depot_pool> {
/* DepotID index member of DepotPool is 2 bytes. */ /* DepotID index member of DepotPool is 2 bytes. */
uint16_t town_cn; ///< The N-1th depot for this town (consecutive number) uint16_t town_cn; ///< The N-1th depot for this town (consecutive number)
@ -25,14 +38,35 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> {
std::string name; std::string name;
TimerGameCalendar::Date build_date; ///< Date of construction 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<TileIndex> 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(); ~Depot();
static inline Depot *GetByTile(TileIndex tile) static inline Depot *GetByTile(TileIndex tile)
{ {
assert(Depot::IsValidID(GetDepotIndex(tile)));
return Depot::Get(GetDepotIndex(tile)); return Depot::Get(GetDepotIndex(tile));
} }
TileIndex GetBestDepotTile(Vehicle *v) const;
/** /**
* Is the "type" of depot the same as the given depot, * Is the "type" of depot the same as the given depot,
* i.e. are both a rail, road or ship depots? * i.e. are both a rail, road or ship depots?
@ -43,6 +77,15 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> {
{ {
return GetTileType(d->xy) == GetTileType(this->xy); 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 */ #endif /* DEPOT_BASE_H */

View File

@ -48,7 +48,7 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str
Depot *d = Depot::GetIfValid(depot_id); Depot *d = Depot::GetIfValid(depot_id);
if (d == nullptr) return CMD_ERROR; if (d == nullptr) return CMD_ERROR;
CommandCost ret = CheckTileOwnership(d->xy); CommandCost ret = CheckOwnership(d->owner);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
bool reset = text.empty(); bool reset = text.empty();
@ -68,11 +68,76 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str
/* Update the orders and depot */ /* Update the orders and depot */
SetWindowClassesDirty(WC_VEHICLE_ORDERS); SetWindowClassesDirty(WC_VEHICLE_ORDERS);
SetWindowDirty(WC_VEHICLE_DEPOT, d->xy); SetWindowDirty(WC_VEHICLE_DEPOT, d->index);
/* Update the depot list */ /* Update the depot list */
VehicleType vt = GetDepotVehicleType(d->xy); SetWindowDirty(GetWindowClassForVehicleType(d->veh_type), VehicleListIdentifier(VL_DEPOT_LIST, d->veh_type, d->owner, d->index).Pack());
SetWindowDirty(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_DEPOT_LIST, vt, GetTileOwner(d->xy), d->index).Pack());
} }
return CommandCost(); 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();
}

View File

@ -10,10 +10,12 @@
#ifndef DEPOT_FUNC_H #ifndef DEPOT_FUNC_H
#define DEPOT_FUNC_H #define DEPOT_FUNC_H
#include "depot_type.h"
#include "vehicle_type.h" #include "vehicle_type.h"
#include "slope_func.h" #include "slope_func.h"
#include "command_type.h"
void ShowDepotWindow(TileIndex tile, VehicleType type); void ShowDepotWindow(DepotID depot_id);
void InitDepotWindowBlockSizes(); void InitDepotWindowBlockSizes();
void DeleteDepotHighlightOfVehicle(const Vehicle *v); void DeleteDepotHighlightOfVehicle(const Vehicle *v);
@ -33,4 +35,13 @@ inline bool CanBuildDepotByTileh(DiagDirection direction, Slope tileh)
return IsSteepSlope(tileh) ? (tileh & entrance_corners) == entrance_corners : (tileh & entrance_corners) != 0; 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<bool(DepotID join_to)>;
void ShowSelectDepotIfNeeded(TileArea ta, DepotPickerCmdProc proc, VehicleType veh_type);
struct Window;
void CheckRedrawDepotHighlight(const Window *w, VehicleType veh_type);
#endif /* DEPOT_FUNC_H */ #endif /* DEPOT_FUNC_H */

View File

@ -29,9 +29,11 @@
#include "zoom_func.h" #include "zoom_func.h"
#include "error.h" #include "error.h"
#include "depot_cmd.h" #include "depot_cmd.h"
#include "station_base.h"
#include "train_cmd.h" #include "train_cmd.h"
#include "vehicle_cmd.h" #include "vehicle_cmd.h"
#include "core/geometry_func.hpp" #include "core/geometry_func.hpp"
#include "depot_func.h"
#include "widgets/depot_widget.h" #include "widgets/depot_widget.h"
@ -78,6 +80,7 @@ static constexpr NWidgetPart _nested_train_depot_widgets[] = {
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), 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_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_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_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_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), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_START_ALL), SetDataTip(SPR_FLAG_VEH_RUNNING, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
@ -266,15 +269,17 @@ struct DepotWindow : Window {
Scrollbar *hscroll; ///< Only for trains. Scrollbar *hscroll; ///< Only for trains.
Scrollbar *vscroll; 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(depot->veh_type));
this->sel = INVALID_VEHICLE; this->sel = INVALID_VEHICLE;
this->vehicle_over = INVALID_VEHICLE; this->vehicle_over = INVALID_VEHICLE;
this->generate_list = true; this->generate_list = true;
this->hovered_widget = -1; this->hovered_widget = -1;
this->type = type; this->type = depot->veh_type;
this->num_columns = 1; // for non-trains this gets set in FinishInitNested() this->num_columns = 1; // for non-trains this gets set in FinishInitNested()
this->unitnumber_digits = 2; this->unitnumber_digits = 2;
@ -282,23 +287,24 @@ struct DepotWindow : Window {
this->hscroll = (this->type == VEH_TRAIN ? this->GetScrollbar(WID_D_H_SCROLL) : nullptr); this->hscroll = (this->type == VEH_TRAIN ? this->GetScrollbar(WID_D_H_SCROLL) : nullptr);
this->vscroll = this->GetScrollbar(WID_D_V_SCROLL); this->vscroll = this->GetScrollbar(WID_D_V_SCROLL);
/* Don't show 'rename button' of aircraft hangar */ /* Don't show 'rename button' of aircraft hangar */
this->GetWidget<NWidgetStacked>(WID_D_SHOW_RENAME)->SetDisplayedPlane(type == VEH_AIRCRAFT ? SZSP_NONE : 0); this->GetWidget<NWidgetStacked>(WID_D_SHOW_RENAME)->SetDisplayedPlane(this->type == VEH_AIRCRAFT ? SZSP_NONE : 0);
/* Only train depots have a horizontal scrollbar and a 'sell chain' button */ /* Only train depots have a horizontal scrollbar and a 'sell chain' button */
if (type == VEH_TRAIN) this->GetWidget<NWidgetCore>(WID_D_MATRIX)->widget_data = 1 << MAT_COL_START; if (this->type == VEH_TRAIN) this->GetWidget<NWidgetCore>(WID_D_MATRIX)->widget_data = 1 << MAT_COL_START;
this->GetWidget<NWidgetStacked>(WID_D_SHOW_H_SCROLL)->SetDisplayedPlane(type == VEH_TRAIN ? 0 : SZSP_HORIZONTAL); this->GetWidget<NWidgetStacked>(WID_D_SHOW_H_SCROLL)->SetDisplayedPlane(this->type == VEH_TRAIN ? 0 : SZSP_HORIZONTAL);
this->GetWidget<NWidgetStacked>(WID_D_SHOW_SELL_CHAIN)->SetDisplayedPlane(type == VEH_TRAIN ? 0 : SZSP_NONE); this->GetWidget<NWidgetStacked>(WID_D_SHOW_SELL_CHAIN)->SetDisplayedPlane(this->type == VEH_TRAIN ? 0 : SZSP_NONE);
this->SetupWidgetData(type); this->SetupWidgetData(this->type);
this->FinishInitNested(tile); this->FinishInitNested(depot_id);
this->owner = GetTileOwner(tile); this->owner = depot->owner;
OrderBackup::Reset(); OrderBackup::Reset();
} }
void Close([[maybe_unused]] int data = 0) override void Close([[maybe_unused]] int data = 0) override
{ {
CloseWindowById(WC_BUILD_VEHICLE, this->window_number); 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); OrderBackup::Reset(this->window_number);
SetViewportHighlightDepot(this->window_number, false);
this->Window::Close(); this->Window::Close();
} }
@ -426,7 +432,7 @@ struct DepotWindow : Window {
if (widget != WID_D_CAPTION) return; if (widget != WID_D_CAPTION) return;
SetDParam(0, this->type); SetDParam(0, this->type);
SetDParam(1, this->GetDepotIndex()); SetDParam(1, (this->type == VEH_AIRCRAFT) ? Depot::Get(this->window_number)->station->index : this->window_number);
} }
struct GetDepotVehiclePtData { struct GetDepotVehiclePtData {
@ -708,6 +714,9 @@ struct DepotWindow : Window {
void OnPaint() override void OnPaint() override
{ {
extern DepotID _viewport_highlight_depot;
this->SetWidgetLoweredState(WID_D_HIGHLIGHT, _viewport_highlight_depot == this->window_number);
if (this->generate_list) { if (this->generate_list) {
/* Generate the vehicle list /* Generate the vehicle list
* It's ok to use the wagon pointers for non-trains as they will be ignored */ * It's ok to use the wagon pointers for non-trains as they will be ignored */
@ -742,8 +751,7 @@ struct DepotWindow : Window {
} }
/* Setup disabled buttons. */ /* Setup disabled buttons. */
TileIndex tile = this->window_number; this->SetWidgetsDisabledState(this->owner != _local_company,
this->SetWidgetsDisabledState(!IsTileOwner(tile, _local_company),
WID_D_STOP_ALL, WID_D_STOP_ALL,
WID_D_START_ALL, WID_D_START_ALL,
WID_D_SELL, WID_D_SELL,
@ -759,6 +767,8 @@ struct DepotWindow : Window {
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
{ {
TileIndex tile = Depot::Get(this->window_number)->xy;
switch (widget) { switch (widget) {
case WID_D_MATRIX: // List case WID_D_MATRIX: // List
this->DepotClick(pt.x, pt.y); this->DepotClick(pt.x, pt.y);
@ -789,20 +799,25 @@ struct DepotWindow : Window {
if (_ctrl_pressed) { if (_ctrl_pressed) {
ShowExtraViewportWindow(this->window_number); ShowExtraViewportWindow(this->window_number);
} else { } else {
ScrollMainWindowToTile(this->window_number); ScrollMainWindowToTile(tile);
} }
break; break;
case WID_D_RENAME: // Rename button case WID_D_RENAME: // Rename button
SetDParam(0, this->type); 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); 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; 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_STOP_ALL:
case WID_D_START_ALL: { case WID_D_START_ALL: {
VehicleListIdentifier vli(VL_DEPOT_LIST, this->type, this->owner); VehicleListIdentifier vli(VL_DEPOT_LIST, this->type, this->owner);
Command<CMD_MASS_START_STOP>::Post(this->window_number, widget == WID_D_START_ALL, false, vli); Command<CMD_MASS_START_STOP>::Post(tile, widget == WID_D_START_ALL, false, vli);
break; break;
} }
@ -810,7 +825,7 @@ struct DepotWindow : Window {
/* Only open the confirmation window if there are anything to sell */ /* Only open the confirmation window if there are anything to sell */
if (!this->vehicle_list.empty() || !this->wagon_list.empty()) { if (!this->vehicle_list.empty() || !this->wagon_list.empty()) {
SetDParam(0, this->type); SetDParam(0, this->type);
SetDParam(1, this->GetDepotIndex()); SetDParam(1, this->window_number);
ShowQuery( ShowQuery(
STR_DEPOT_CAPTION, STR_DEPOT_CAPTION,
STR_DEPOT_SELL_CONFIRMATION_TEXT, STR_DEPOT_SELL_CONFIRMATION_TEXT,
@ -821,11 +836,11 @@ struct DepotWindow : Window {
break; break;
case WID_D_VEHICLE_LIST: case WID_D_VEHICLE_LIST:
ShowVehicleListWindow(GetTileOwner(this->window_number), this->type, (TileIndex)this->window_number); ShowVehicleListWindowDepot(this->owner, this->type, this->window_number);
break; break;
case WID_D_AUTOREPLACE: case WID_D_AUTOREPLACE:
Command<CMD_DEPOT_MASS_AUTOREPLACE>::Post(this->window_number, this->type); Command<CMD_DEPOT_MASS_AUTOREPLACE>::Post(tile, this->type);
break; break;
} }
@ -836,7 +851,7 @@ struct DepotWindow : Window {
if (!str.has_value()) return; if (!str.has_value()) return;
/* Do depot renaming */ /* Do depot renaming */
Command<CMD_RENAME_DEPOT>::Post(STR_ERROR_CAN_T_RENAME_DEPOT, this->GetDepotIndex(), *str); Command<CMD_RENAME_DEPOT>::Post(STR_ERROR_CAN_T_RENAME_DEPOT, this->window_number, *str);
} }
bool OnRightClick([[maybe_unused]] Point pt, WidgetID widget) override bool OnRightClick([[maybe_unused]] Point pt, WidgetID widget) override
@ -902,10 +917,10 @@ struct DepotWindow : Window {
{ {
if (_ctrl_pressed) { if (_ctrl_pressed) {
/* Share-clone, do not open new viewport, and keep tool active */ /* Share-clone, do not open new viewport, and keep tool active */
Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, this->window_number, v->index, true); Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, Depot::Get(this->window_number)->xy, v->index, true);
} else { } else {
/* Copy-clone, open viewport for new vehicle, and deselect the tool (assume player wants to change things on new vehicle) */ /* Copy-clone, open viewport for new vehicle, and deselect the tool (assume player wants to change things on new vehicle) */
if (Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, CcCloneVehicle, this->window_number, v->index, false)) { if (Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, CcCloneVehicle, Depot::Get(this->window_number)->xy, v->index, false)) {
ResetObjectToPlace(); ResetObjectToPlace();
} }
} }
@ -1111,43 +1126,33 @@ struct DepotWindow : Window {
return ES_NOT_HANDLED; 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) static void DepotSellAllConfirmationCallback(Window *win, bool confirmed)
{ {
if (confirmed) { if (confirmed) {
DepotWindow *w = (DepotWindow*)win; assert(Depot::IsValidID(win->window_number));
TileIndex tile = w->window_number; Depot *d = Depot::Get(win->window_number);
VehicleType vehtype = w->type; Command<CMD_DEPOT_SELL_ALL_VEHICLES>::Post(d->xy, d->veh_type);
Command<CMD_DEPOT_SELL_ALL_VEHICLES>::Post(tile, vehtype);
} }
} }
/** /**
* Opens a depot window * Opens a depot window.
* @param tile The tile where the depot/hangar is located * @param depot_id Index of the depot.
* @param type The type of vehicles in 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 (d->veh_type) {
default: NOT_REACHED(); default: NOT_REACHED();
case VEH_TRAIN: new DepotWindow(_train_depot_desc, tile, type); break; case VEH_TRAIN: new DepotWindow(_train_depot_desc, depot_id); break;
case VEH_ROAD: new DepotWindow(_road_depot_desc, tile, type); break; case VEH_ROAD: new DepotWindow(_road_depot_desc, depot_id); break;
case VEH_SHIP: new DepotWindow(_ship_depot_desc, tile, type); break; case VEH_SHIP: new DepotWindow(_ship_depot_desc, depot_id); break;
case VEH_AIRCRAFT: new DepotWindow(_aircraft_depot_desc, tile, type); break; case VEH_AIRCRAFT: new DepotWindow(_aircraft_depot_desc, depot_id); break;
} }
} }
@ -1164,8 +1169,347 @@ void DeleteDepotHighlightOfVehicle(const Vehicle *v)
*/ */
if (_special_mouse_mode != WSM_DRAGDROP) return; if (_special_mouse_mode != WSM_DRAGDROP) return;
w = dynamic_cast<DepotWindow*>(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<DepotWindow*>(FindWindowById(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)));
if (w != nullptr) { if (w != nullptr) {
if (w->sel == v->index) ResetObjectToPlace(); if (w->sel == v->index) ResetObjectToPlace();
} }
} }
static std::vector<DepotID> _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);
_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
{
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();
}
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[] = {
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);
}
}
/**
* 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<int>(0, x - max_c), std::max<int>(0, y - max_c)), TileXY(std::min<int>(Map::MaxX(), x + location.w + max_c), std::min<int>(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);
}
}
}

View File

@ -43,16 +43,21 @@ inline bool IsDepotTile(Tile tile)
return IsRailDepotTile(tile) || IsRoadDepotTile(tile) || IsShipDepotTile(tile) || IsHangarTile(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. * Get the index of which depot is attached to the tile.
* @param t the tile * @param t the tile
* @pre IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t) * @pre IsDepotTile(t)
* @return DepotID * @return DepotID
*/ */
inline DepotID GetDepotIndex(Tile t) inline DepotID GetDepotIndex(Tile t)
{ {
/* Hangars don't have a Depot class, thus store no DepotID. */ assert(IsDepotTile(t));
assert(IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t));
/* Hangars don't store depot id on m2. */
if (IsTileType(t, MP_STATION)) return GetHangarIndex(t);
return t.m2(); return t.m2();
} }

View File

@ -14,7 +14,10 @@ typedef uint16_t DepotID; ///< Type for the unique identifier of depots.
struct Depot; struct Depot;
static const DepotID INVALID_DEPOT = UINT16_MAX; 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' 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 */ #endif /* DEPOT_TYPE_H */

View File

@ -32,6 +32,7 @@
#include "waypoint_cmd.h" #include "waypoint_cmd.h"
#include "timer/timer.h" #include "timer/timer.h"
#include "timer/timer_game_calendar.h" #include "timer/timer_game_calendar.h"
#include "depot_func.h"
#include "widgets/dock_widget.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 (_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 (_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(); this->Window::Close();
} }
@ -164,7 +167,9 @@ struct BuildDocksToolbarWindow : Window {
break; break;
case WID_DT_DEPOT: // Build depot button 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; break;
case WID_DT_STATION: // Build station button case WID_DT_STATION: // Build station button
@ -204,9 +209,16 @@ struct BuildDocksToolbarWindow : Window {
PlaceProc_DemolishArea(tile); PlaceProc_DemolishArea(tile);
break; break;
case WID_DT_DEPOT: // Build depot button case WID_DT_DEPOT: { // Build depot button
Command<CMD_BUILD_SHIP_DEPOT>::Post(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, tile, _ship_depot_direction); 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; break;
}
case WID_DT_STATION: { // Build station button case WID_DT_STATION: { // Build station button
/* Determine the watery part of the dock. */ /* Determine the watery part of the dock. */
@ -260,6 +272,15 @@ struct BuildDocksToolbarWindow : Window {
case DDSP_CREATE_RIVER: case DDSP_CREATE_RIVER:
Command<CMD_BUILD_CANAL>::Post(STR_ERROR_CAN_T_PLACE_RIVERS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_RIVER, _ctrl_pressed); Command<CMD_BUILD_CANAL>::Post(STR_ERROR_CAN_T_PLACE_RIVERS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_RIVER, _ctrl_pressed);
break; break;
case DDSP_BUILD_DEPOT: {
bool adjacent = _ctrl_pressed;
auto proc = [=](DepotID join_to) -> bool {
return Command<CMD_BUILD_SHIP_DEPOT>::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; default: break;
} }
@ -270,11 +291,14 @@ 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_STATION)) SetViewportCatchmentStation(nullptr, true);
if (_game_mode != GM_EDITOR && this->IsWidgetLowered(WID_DT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true);
this->RaiseButtons(); this->RaiseButtons();
CloseWindowById(WC_BUILD_STATION, TRANSPORT_WATER); CloseWindowById(WC_BUILD_STATION, TRANSPORT_WATER);
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_WATER); CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_WATER);
CloseWindowById(WC_SELECT_STATION, 0); CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
CloseWindowByClass(WC_BUILD_BRIDGE); CloseWindowByClass(WC_BUILD_BRIDGE);
} }
@ -514,10 +538,13 @@ struct BuildDocksDepotWindow : public PickerWindowBase {
private: private:
static void UpdateDocksDirection() static void UpdateDocksDirection()
{ {
VpSetPlaceFixedSize(2);
if (_ship_depot_direction != AXIS_X) { if (_ship_depot_direction != AXIS_X) {
SetTileSelectSize(1, 2); SetTileSelectSize(1, 2);
_thd.select_method = VPM_LIMITED_X_FIXED_Y;
} else { } else {
SetTileSelectSize(2, 1); SetTileSelectSize(2, 1);
_thd.select_method = VPM_LIMITED_Y_FIXED_X;
} }
} }
@ -529,6 +556,13 @@ public:
UpdateDocksDirection(); UpdateDocksDirection();
} }
void Close([[maybe_unused]] int data = 0) override
{
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
VpResetFixedSize();
this->PickerWindowBase::Close();
}
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{ {
switch (widget) { switch (widget) {
@ -569,6 +603,7 @@ public:
switch (widget) { switch (widget) {
case WID_BDD_X: case WID_BDD_X:
case WID_BDD_Y: case WID_BDD_Y:
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
this->RaiseWidget(WID_BDD_X + _ship_depot_direction); this->RaiseWidget(WID_BDD_X + _ship_depot_direction);
_ship_depot_direction = (widget == WID_BDD_X ? AXIS_X : AXIS_Y); _ship_depot_direction = (widget == WID_BDD_X ? AXIS_X : AXIS_Y);
this->LowerWidget(WID_BDD_X + _ship_depot_direction); this->LowerWidget(WID_BDD_X + _ship_depot_direction);
@ -578,6 +613,11 @@ public:
break; break;
} }
} }
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
CheckRedrawDepotHighlight(this, VEH_SHIP);
}
}; };
static constexpr NWidgetPart _nested_build_docks_depot_widgets[] = { static constexpr NWidgetPart _nested_build_docks_depot_widgets[] = {

View File

@ -55,6 +55,7 @@
#include "timer/timer.h" #include "timer/timer.h"
#include "timer/timer_game_calendar.h" #include "timer/timer_game_calendar.h"
#include "timer/timer_game_economy.h" #include "timer/timer_game_economy.h"
#include "depot_base.h"
#include "table/strings.h" #include "table/strings.h"
#include "table/pricebase.h" #include "table/pricebase.h"
@ -374,6 +375,12 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
} }
if (new_owner == INVALID_OWNER) RebuildSubsidisedSourceAndDestinationCache(); 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 */ /* Take care of rating and transport rights in towns */
for (Town *t : Town::Iterate()) { for (Town *t : Town::Iterate()) {
/* If a company takes over, give the ratings to that company. */ /* If a company takes over, give the ratings to that company. */

View File

@ -19,6 +19,7 @@
#include "core/pool_func.hpp" #include "core/pool_func.hpp"
#include "order_backup.h" #include "order_backup.h"
#include "group_cmd.h" #include "group_cmd.h"
#include "depot_map.h"
#include "table/strings.h" #include "table/strings.h"
@ -579,7 +580,7 @@ std::tuple<CommandCost, GroupID> 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_VIEW, v->index);
SetWindowDirty(WC_VEHICLE_DETAILS, v->index); SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
InvalidateWindowData(WC_VEHICLE_VIEW, v->index); InvalidateWindowData(WC_VEHICLE_VIEW, v->index);

View File

@ -842,7 +842,7 @@ public:
break; break;
case WID_GL_AVAILABLE_VEHICLES: case WID_GL_AVAILABLE_VEHICLES:
ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype); ShowBuildVehicleWindow(INVALID_DEPOT, this->vli.vtype);
break; break;
case WID_GL_MANAGE_VEHICLES_DROPDOWN: { case WID_GL_MANAGE_VEHICLES_DROPDOWN: {

View File

@ -278,6 +278,8 @@ STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Select f
STR_BUTTON_SORT_BY :{BLACK}Sort by STR_BUTTON_SORT_BY :{BLACK}Sort by
STR_BUTTON_CATCHMENT :{BLACK}Coverage STR_BUTTON_CATCHMENT :{BLACK}Coverage
STR_TOOLTIP_CATCHMENT :{BLACK}Toggle coverage area display 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_CLOSE_WINDOW :{BLACK}Close window
STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Window title - drag this to move window STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Window title - drag this to move window
@ -1462,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 :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_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_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 :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 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
@ -1615,6 +1618,15 @@ 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_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_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_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 :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 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 +2149,7 @@ STR_CONFIG_SETTING_ENVIRONMENT_TREES :Trees
STR_CONFIG_SETTING_AI :Competitors STR_CONFIG_SETTING_AI :Competitors
STR_CONFIG_SETTING_AI_NPC :Computer players STR_CONFIG_SETTING_AI_NPC :Computer players
STR_CONFIG_SETTING_NETWORK :Network 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 :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 STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Allow trains to reverse on a signal, if they waited there a long time
@ -2763,6 +2776,11 @@ STR_JOIN_STATION_CREATE_SPLITTED_STATION :{YELLOW}Build a
STR_JOIN_WAYPOINT_CAPTION :{WHITE}Join waypoint STR_JOIN_WAYPOINT_CAPTION :{WHITE}Join waypoint
STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT :{YELLOW}Build a separate 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 # Generic toolbar
STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE :{BLACK}Disabled as currently no vehicles are available for this infrastructure STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE :{BLACK}Disabled as currently no vehicles are available for this infrastructure
@ -5110,10 +5128,12 @@ STR_ERROR_BUOY_IS_IN_USE :{WHITE}... buoy
# Depot related errors # Depot related errors
STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT :{WHITE}Can't build train depot here... 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_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_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_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_CAN_T_RENAME_DEPOT :{WHITE}Can't rename depot...
STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT :{WHITE}... must be stopped inside a depot STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT :{WHITE}... must be stopped inside a depot
@ -5123,6 +5143,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_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_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 :{WHITE}Can't reverse direction of vehicle...
STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS :{WHITE}... consists of multiple units STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS :{WHITE}... consists of multiple units
STR_ERROR_INCOMPATIBLE_RAIL_TYPES :Incompatible rail types STR_ERROR_INCOMPATIBLE_RAIL_TYPES :Incompatible rail types
@ -5131,7 +5152,9 @@ 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_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_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_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 STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Wrong depot type
# Depot unbunching related errors # Depot unbunching related errors

View File

@ -19,6 +19,8 @@
#include "order_cmd.h" #include "order_cmd.h"
#include "group_cmd.h" #include "group_cmd.h"
#include "vehicle_func.h" #include "vehicle_func.h"
#include "depot_map.h"
#include "depot_base.h"
#include "safeguards.h" #include "safeguards.h"
@ -45,8 +47,9 @@ OrderBackup::~OrderBackup()
*/ */
OrderBackup::OrderBackup(const Vehicle *v, uint32_t user) OrderBackup::OrderBackup(const Vehicle *v, uint32_t user)
{ {
assert(IsDepotTile(v->tile));
this->user = user; this->user = user;
this->tile = v->tile; this->depot_id = GetDepotIndex(v->tile);
this->group = v->group_id; this->group = v->group_id;
this->CopyConsistPropertiesFrom(v); this->CopyConsistPropertiesFrom(v);
@ -123,8 +126,10 @@ void OrderBackup::DoRestore(Vehicle *v)
*/ */
/* static */ void OrderBackup::Restore(Vehicle *v, uint32_t user) /* 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()) { 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); ob->DoRestore(v);
delete ob; delete ob;
@ -133,28 +138,28 @@ void OrderBackup::DoRestore(Vehicle *v)
/** /**
* Reset an OrderBackup given a tile and user. * 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. * @param user The user associated with the OrderBackup.
* @note Must not be used from the GUI! * @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()) { 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 * Clear an OrderBackup
* @param flags For command. * @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. * @param user_id User that had the OrderBackup.
* @return The cost of this operation or an error. * @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. */ assert(Depot::IsValidID(depot_id));
if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, user_id); if (flags & DC_EXEC) OrderBackup::ResetOfUser(depot_id, user_id);
return CommandCost(); 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 it's not a backup of us, ignore it. */
if (ob->user != user) continue; if (ob->user != user) continue;
Command<CMD_CLEAR_ORDER_BACKUP>::Post(0, static_cast<ClientID>(user)); Command<CMD_CLEAR_ORDER_BACKUP>::Post(ob->depot_id, static_cast<ClientID>(user));
return; return;
} }
} }
/** /**
* Reset the OrderBackups from GUI/game logic. * 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 * @param from_gui Whether the call came from the GUI, i.e. whether
* it must be synced over the network. * 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, /* 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 * 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()) { for (OrderBackup *ob : OrderBackup::Iterate()) {
/* If this is a GUI action, and it's not a backup of us, ignore it. */ /* If this is a GUI action, and it's not a backup of us, ignore it. */
if (from_gui && ob->user != user) continue; if (from_gui && ob->user != user) continue;
/* If it's not for our chosen tile either, ignore it. */ /* If it's not for our chosen depot either, ignore it. */
if (t != INVALID_TILE && t != ob->tile) continue; if (depot_id != INVALID_DEPOT && depot_id != ob->depot_id) continue;
if (from_gui) { if (from_gui) {
/* We need to circumvent the "prevention" from this command being executed /* 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 * 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. */ * this command to get its cost estimated when shift is pressed. */
Command<CMD_CLEAR_ORDER_BACKUP>::Unsafe<CommandCallback>(STR_NULL, nullptr, true, false, ob->tile, CommandTraits<CMD_CLEAR_ORDER_BACKUP>::Args{ ob->tile, static_cast<ClientID>(user) }); Command<CMD_CLEAR_ORDER_BACKUP>::Unsafe<CommandCallback>(STR_NULL, nullptr, true, false, ob->depot_id, CommandTraits<CMD_CLEAR_ORDER_BACKUP>::Args{ ob->depot_id, static_cast<ClientID>(user) });
} else { } 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. */ * In that case we have no need to actually sync this, just do it. */
delete ob; delete ob;
} }
@ -246,18 +251,14 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID us
* Removes an order from all vehicles. Triggers when, say, a station is removed. * 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 type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]).
* @param destination The destination. Can be a StationID, DepotID or WaypointID. * @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 (OrderBackup *ob : OrderBackup::Iterate()) {
for (Order *order = ob->orders; order != nullptr; order = order->next) { for (Order *order = ob->orders; order != nullptr; order = order->next) {
OrderType ot = order->GetType(); OrderType ot = order->GetType();
if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue; 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) ot = OT_GOTO_STATION;
if (ot == OT_IMPLICIT || (IsHangarTile(ob->tile) && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
if (ot == type && order->GetDestination() == destination) { 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. */ /* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */
delete ob; delete ob;

View File

@ -16,6 +16,7 @@
#include "vehicle_type.h" #include "vehicle_type.h"
#include "base_consist.h" #include "base_consist.h"
#include "saveload/saveload.h" #include "saveload/saveload.h"
#include "depot_type.h"
/** Unique identifier for an order backup. */ /** Unique identifier for an order backup. */
typedef uint8_t OrderBackupID; typedef uint8_t OrderBackupID;
@ -35,7 +36,7 @@ private:
friend SaveLoadTable GetOrderBackupDescription(); ///< Saving and loading of order backups. friend SaveLoadTable GetOrderBackupDescription(); ///< Saving and loading of order backups.
friend struct BKORChunkHandler; ///< Creating empty orders upon savegame loading. friend struct BKORChunkHandler; ///< Creating empty orders upon savegame loading.
uint32_t user; ///< The user that requested the backup. uint32_t user; ///< The user that requested the backup.
TileIndex tile; ///< Tile of the depot where the order was changed. DepotID depot_id; ///< Depot where the order was changed.
GroupID group; ///< The group the vehicle was part of. GroupID group; ///< The group the vehicle was part of.
const Vehicle *clone; ///< Vehicle this vehicle was a clone of. const Vehicle *clone; ///< Vehicle this vehicle was a clone of.
@ -53,13 +54,13 @@ public:
static void Backup(const Vehicle *v, uint32_t user); static void Backup(const Vehicle *v, uint32_t user);
static void Restore(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 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 ClearGroup(GroupID group);
static void ClearVehicle(const Vehicle *v); 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 */ #endif /* ORDER_BACKUP_H */

View File

@ -650,7 +650,7 @@ TileIndex Order::GetLocation(const Vehicle *v, bool airport) const
case OT_GOTO_DEPOT: case OT_GOTO_DEPOT:
if (this->GetDestination() == INVALID_DEPOT) return INVALID_TILE; 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: default:
return INVALID_TILE; 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); 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. * Add an order to the orderlist of a vehicle.
* @param flags operation to perform * @param flags operation to perform
@ -763,41 +785,15 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se
case OT_GOTO_DEPOT: { case OT_GOTO_DEPOT: {
if ((new_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0) { if ((new_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0) {
if (v->type == VEH_AIRCRAFT) {
const Station *st = Station::GetIfValid(new_order.GetDestination());
if (st == 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()); const Depot *dp = Depot::GetIfValid(new_order.GetDestination());
if (dp == nullptr) return CMD_ERROR; if (dp == nullptr) return CMD_ERROR;
ret = CheckOwnership(GetTileOwner(dp->xy)); ret = CheckOwnership(dp->owner);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
switch (v->type) { if (v->type != dp->veh_type) return CMD_ERROR;
case VEH_TRAIN: if (v->type == VEH_AIRCRAFT && !CanVehicleUseStation(v, Station::GetByTile(dp->xy))) return CMD_ERROR;
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;
default: return CMD_ERROR;
}
}
} }
if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) return CMD_ERROR; if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) return CMD_ERROR;
@ -1780,24 +1776,11 @@ void CheckOrders(const Vehicle *v)
* Removes an order from all vehicles. Triggers when, say, a station is removed. * 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 type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]).
* @param destination The destination. Can be a StationID, DepotID or WaypointID. * @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 */ /* Go through all vehicles */
for (Vehicle *v : Vehicle::Iterate()) { 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 */ /* Clear the order from the order-list */
int id = -1; int id = -1;
for (Order *order : v->Orders()) { for (Order *order : v->Orders()) {
@ -1806,8 +1789,7 @@ restart:
OrderType ot = order->GetType(); OrderType ot = order->GetType();
if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue; 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) ot = OT_GOTO_STATION;
if (ot == OT_IMPLICIT || (v->type == VEH_AIRCRAFT && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
if (ot == type && order->GetDestination() == destination) { if (ot == type && order->GetDestination() == destination) {
/* We want to clear implicit orders, but we don't want to make them /* 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 * dummy orders. They should just vanish. Also check the actual order
@ -1841,7 +1823,7 @@ restart:
} }
} }
OrderBackup::RemoveOrder(type, destination, hangar); OrderBackup::RemoveOrder(type, destination);
} }
/** /**
@ -2051,10 +2033,11 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool
v->IncrementRealOrderIndex(); v->IncrementRealOrderIndex();
} else { } else {
if (v->type != VEH_AIRCRAFT) { if (v->type != VEH_AIRCRAFT) {
v->SetDestTile(Depot::Get(order->GetDestination())->xy); v->SetDestTile(Depot::Get(order->GetDestination())->GetBestDepotTile(v));
} else { } else {
Aircraft *a = Aircraft::From(v); 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) { if (a->targetairport != destination) {
/* The aircraft is now heading for a different hangar than the next in the orders */ /* The aircraft is now heading for a different hangar than the next in the orders */
a->SetDestTile(a->GetOrderStationLocation(destination)); a->SetDestTile(a->GetOrderStationLocation(destination));

View File

@ -21,7 +21,7 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se
CommandCost CmdOrderRefit(DoCommandFlag flags, VehicleID veh, VehicleOrderID order_number, CargoID cargo); CommandCost CmdOrderRefit(DoCommandFlag flags, VehicleID veh, VehicleOrderID order_number, CargoID cargo);
CommandCost CmdCloneOrder(DoCommandFlag flags, CloneOptions action, VehicleID veh_dst, VehicleID veh_src); 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 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_MODIFY_ORDER, CmdModifyOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT)
DEF_CMD_TRAIT(CMD_SKIP_TO_ORDER, CmdSkipToOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) DEF_CMD_TRAIT(CMD_SKIP_TO_ORDER, CmdSkipToOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT)

View File

@ -15,7 +15,7 @@
#include "company_type.h" #include "company_type.h"
/* Functions */ /* Functions */
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar = false); void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination);
void InvalidateVehicleOrder(const Vehicle *v, int data); void InvalidateVehicleOrder(const Vehicle *v, int data);
void CheckOrders(const Vehicle*); void CheckOrders(const Vehicle*);
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false, bool reset_order_indices = true); 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); bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0, bool pbs_look_ahead = false);
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v); VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v);
uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int conditional_depth = 0); 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); void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable, int left, int middle, int right);

View File

@ -34,6 +34,7 @@
#include "error.h" #include "error.h"
#include "order_cmd.h" #include "order_cmd.h"
#include "company_cmd.h" #include "company_cmd.h"
#include "depot_base.h"
#include "widgets/order_widget.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. */ /* Going to a specific depot. */
SetDParam(0, STR_ORDER_GO_TO_DEPOT_FORMAT); SetDParam(0, STR_ORDER_GO_TO_DEPOT_FORMAT);
SetDParam(2, v->type); SetDParam(2, v->type);
SetDParam(3, order->GetDestination()); SetDParam(3, GetTargetDestination(*order, v->type == VEH_AIRCRAFT));
} }
if (order->GetDepotOrderType() & ODTFB_SERVICE) { if (order->GetDepotOrderType() & ODTFB_SERVICE) {
@ -384,8 +385,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
/* check depot first */ /* check depot first */
if (IsDepotTypeTile(tile, (TransportType)(uint)v->type) && IsTileOwner(tile, _local_company)) { if (IsDepotTypeTile(tile, (TransportType)(uint)v->type) && IsTileOwner(tile, _local_company)) {
order.MakeGoToDepot(v->type == VEH_AIRCRAFT ? GetStationIndex(tile) : GetDepotIndex(tile), order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS,
ODTFB_PART_OF_ORDERS,
(_settings_client.gui.new_nonstop && v->IsGroundVehicle()) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE); (_settings_client.gui.new_nonstop && v->IsGroundVehicle()) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
if (_ctrl_pressed) { if (_ctrl_pressed) {

View File

@ -12,6 +12,7 @@
#include "../tile_cmd.h" #include "../tile_cmd.h"
#include "../waypoint_base.h" #include "../waypoint_base.h"
#include "../depot_base.h"
/** /**
* Calculates the tile of given station that is closest to a given tile * 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); 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 * Wrapper around GetTileTrackStatus() and TrackStatusToTrackdirBits(), as for
* single tram bits GetTileTrackStatus() returns 0. The reason for this is * single tram bits GetTileTrackStatus() returns 0. The reason for this is

View File

@ -118,6 +118,7 @@ protected:
TileIndex m_destTile; TileIndex m_destTile;
TrackdirBits m_destTrackdirs; TrackdirBits m_destTrackdirs;
StationID m_dest_station_id; StationID m_dest_station_id;
DepotID m_dest_depot_id;
bool m_any_depot; bool m_any_depot;
/** to access inherited path finder */ /** to access inherited path finder */
@ -149,14 +150,25 @@ public:
break; break;
case OT_GOTO_DEPOT: case OT_GOTO_DEPOT:
m_dest_station_id = INVALID_STATION;
if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) { if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
m_any_depot = true; 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: default:
m_destTile = v->dest_tile; m_destTile = v->dest_tile;
m_dest_station_id = INVALID_STATION; m_dest_station_id = INVALID_STATION;
m_dest_depot_id = INVALID_DEPOT;
m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_RAIL, 0)); m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_RAIL, 0));
break; break;
} }
@ -176,6 +188,10 @@ public:
return HasStationTileRail(tile) return HasStationTileRail(tile)
&& (GetStationIndex(tile) == m_dest_station_id) && (GetStationIndex(tile) == m_dest_station_id)
&& (GetRailStationTrack(tile) == TrackdirToTrack(td)); && (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) { if (m_any_depot) {

View File

@ -234,7 +234,9 @@ protected:
TileIndex m_destTile; TileIndex m_destTile;
TrackdirBits m_destTrackdirs; TrackdirBits m_destTrackdirs;
StationID m_dest_station; StationID m_dest_station;
DepotID m_dest_depot;
StationType m_station_type; StationType m_station_type;
bool m_bus;
bool m_non_artic; bool m_non_artic;
public: public:
@ -252,8 +254,14 @@ public:
m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_station_type); m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_station_type);
m_non_artic = !v->HasArticulatedPart(); m_non_artic = !v->HasArticulatedPart();
m_destTrackdirs = INVALID_TRACKDIR_BIT; 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 { } else {
m_dest_station = INVALID_STATION; m_dest_station = INVALID_STATION;
m_dest_depot = INVALID_DEPOT;
m_destTile = v->dest_tile; m_destTile = v->dest_tile;
m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype))); m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype)));
} }
@ -287,6 +295,11 @@ public:
(m_non_artic || IsDriveThroughStopTile(tile)); (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); return tile == m_destTile && HasTrackdir(m_destTrackdirs, trackdir);
} }

View File

@ -337,6 +337,19 @@ inline bool HasPowerOnRail(RailType enginetype, RailType tiletype)
return HasBit(GetRailTypeInfo(enginetype)->powered_railtypes, 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. * Test if a RailType disallows build of level crossings.
* @param rt The RailType to check. * @param rt The RailType to check.

View File

@ -952,22 +952,32 @@ CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, Tile
/** /**
* Build a train depot * Build a train depot
* @param flags operation to perform * @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 railtype rail type
* @param dir entrance direction * @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 * @return the cost of this operation or an error
* *
* @todo When checking for the tile slope, * @todo When checking for the tile slope,
* distinguish between "Flat land required" and "land sloped in wrong direction" * 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 */ /* check railtype and valid direction for depot (0 through 3), 4 in total */
if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR; if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
Slope tileh = GetTileSlope(tile);
CommandCost cost(EXPENSES_CONSTRUCTION); 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 /* Prohibit construction if
* The tile is non-flat AND * 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 * 2) the tile is steep i.e. spans two height levels
* 3) the exit points in the wrong direction * 3) the exit points in the wrong direction
*/ */
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 (tileh != SLOPE_FLAT) {
if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) { if (!_settings_game.construction.build_on_slopes ||
!CanBuildDepotByTileh(dir, tileh)) {
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); 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 */ /* Check whether a depot tile exists and it needs to be rotated. */
bool rotate_existing_depot = false; if (IsRailDepotTile(t) && GetDepotIndex(t) == join_to && railtype == GetRailType(t)) {
if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) { if (dir == GetRailDepotDirection(t)) continue;
CommandCost ret = CheckTileOwnership(tile);
ret = EnsureNoVehicleOnGround(t);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
if (dir == GetRailDepotDirection(tile)) return CommandCost(); num_rotated_depot_tiles++;
if (flags & DC_EXEC) {
ret = EnsureNoVehicleOnGround(tile); SetRailDepotExitDirection(t, dir);
if (ret.Failed()) return ret;
rotate_existing_depot = true;
} }
} else {
if (!rotate_existing_depot) { cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, t));
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile));
if (cost.Failed()) return cost; 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) {
if (!Depot::CanAllocateItem()) return CMD_ERROR; MakeRailDepot(t, _current_company, depot->index, dir, railtype);
MarkTileDirtyByTile(t);
}
} }
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
if (rotate_existing_depot) { AddSideToSignalBuffer(t, INVALID_DIAGDIR, _current_company);
SetRailDepotExitDirection(tile, dir); YapfNotifyTrackLayoutChange(t, DiagDirToDiagTrack(dir));
} else { MarkTileDirtyByTile(t);
Depot *d = new Depot(tile); }
d->build_date = TimerGameCalendar::date; }
MakeRailDepot(tile, _current_company, d->index, dir, railtype); if (num_new_depot_tiles + num_rotated_depot_tiles == 0) return CommandCost();
MakeDefaultName(d);
Company::Get(_current_company)->infrastructure.rail[railtype]++; 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));
if (flags & DC_EXEC) {
Company::Get(_current_company)->infrastructure.rail[railtype] += num_new_depot_tiles;
DirtyCompanyInfrastructureWindows(_current_company); DirtyCompanyInfrastructureWindows(_current_company);
depot->AfterAddRemove(ta, true);
if (join_to == NEW_DEPOT) MakeDefaultName(depot);
} }
MarkTileDirtyByTile(tile);
AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_company);
YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
}
cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN]);
cost.AddCost(RailBuildCost(railtype));
return cost; return cost;
} }
@ -1558,6 +1569,7 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
if (area_start >= Map::Size()) return CMD_ERROR; if (area_start >= Map::Size()) return CMD_ERROR;
TrainList affected_trains; TrainList affected_trains;
std::vector<DepotID> affected_depots;
CommandCost cost(EXPENSES_CONSTRUCTION); CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert. CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
@ -1653,10 +1665,11 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
/* notify YAPF about the track layout change */ /* notify YAPF about the track layout change */
YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile)); YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile));
/* Update build vehicle window related to this depot */ if (find(affected_depots.begin(), affected_depots.end(), (tile)) == affected_depots.end()) {
InvalidateWindowData(WC_VEHICLE_DEPOT, tile); affected_depots.push_back(GetDepotIndex(tile));
InvalidateWindowData(WC_BUILD_VEHICLE, tile);
} }
}
found_convertible_track = true; found_convertible_track = true;
cost.AddCost(RailConvertCost(type, totype)); cost.AddCost(RailConvertCost(type, totype));
break; break;
@ -1754,6 +1767,13 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
} }
if (flags & DC_EXEC) { 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 */ /* Railtype changed, update trains as when entering different track */
for (Train *v : affected_trains) { for (Train *v : affected_trains) {
v->ConsistChanged(CCF_TRACK); v->ConsistChanged(CCF_TRACK);
@ -1765,6 +1785,8 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
{ {
assert(IsRailDepotTile(tile));
if (_current_company != OWNER_WATER) { if (_current_company != OWNER_WATER) {
CommandCost ret = CheckTileOwnership(tile); CommandCost ret = CheckTileOwnership(tile);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
@ -1774,9 +1796,11 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
if (flags & DC_EXEC) { 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); DiagDirection dir = GetRailDepotDirection(tile);
Owner owner = GetTileOwner(tile);
Train *v = nullptr; Train *v = nullptr;
if (HasDepotReservation(tile)) { if (HasDepotReservation(tile)) {
@ -1784,19 +1808,46 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
if (v != nullptr) FreeTrainTrackReservation(v); if (v != nullptr) FreeTrainTrackReservation(v);
} }
Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--; c->infrastructure.rail[GetRailType(tile)]--;
DirtyCompanyInfrastructureWindows(owner); DirtyCompanyInfrastructureWindows(c->index);
delete Depot::GetByTile(tile);
DoClearSquare(tile); DoClearSquare(tile);
AddSideToSignalBuffer(tile, dir, owner);
AddSideToSignalBuffer(tile, dir, c->index);
YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir)); YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
if (v != nullptr) TryPathReserve(v, true); if (v != nullptr) TryPathReserve(v, true);
depot->AfterAddRemove(TileArea(tile), false);
} }
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]); 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) static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
{ {
CommandCost cost(EXPENSES_CONSTRUCTION); CommandCost cost(EXPENSES_CONSTRUCTION);
@ -2774,7 +2825,7 @@ static bool ClickTile_Track(TileIndex tile)
{ {
if (!IsRailDepot(tile)) return false; if (!IsRailDepot(tile)) return false;
ShowDepotWindow(tile, VEH_TRAIN); ShowDepotWindow(GetDepotIndex(tile));
return true; return true;
} }
@ -2973,7 +3024,7 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int
if (v->Next() == nullptr) VehicleEnterDepot(v->First()); if (v->Next() == nullptr) VehicleEnterDepot(v->First());
v->tile = tile; v->tile = tile;
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
return VETSB_ENTERED_WORMHOLE; return VETSB_ENTERED_WORMHOLE;
} }

View File

@ -19,7 +19,8 @@ CommandCost CmdBuildRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileI
CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, Track track); 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 CmdBuildSingleRail(DoCommandFlag flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals);
CommandCost CmdRemoveSingleRail(DoCommandFlag flags, TileIndex tile, Track track); 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 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 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 CmdRemoveSingleSignal(DoCommandFlag flags, TileIndex tile, Track track);
CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal); 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_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_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_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_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_REMOVE_SINGLE_SIGNAL, CmdRemoveSingleSignal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_CONVERT_RAIL, CmdConvertRail, 0, CMDT_LANDSCAPE_CONSTRUCTION) DEF_CMD_TRAIT(CMD_CONVERT_RAIL, CmdConvertRail, 0, CMDT_LANDSCAPE_CONSTRUCTION)
@ -40,6 +42,6 @@ DEF_CMD_TRAIT(CMD_REMOVE_SIGNAL_TRACK, CmdRemoveSignalTrack, CMD_AUTO,
CommandCallback CcPlaySound_CONSTRUCTION_RAIL; CommandCallback CcPlaySound_CONSTRUCTION_RAIL;
CommandCallback CcStation; CommandCallback CcStation;
CommandCallback CcBuildRailTunnel; 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 */ #endif /* RAIL_CMD_H */

View File

@ -41,6 +41,7 @@
#include "timer/timer.h" #include "timer/timer.h"
#include "timer/timer_game_calendar.h" #include "timer/timer_game_calendar.h"
#include "picker_gui.h" #include "picker_gui.h"
#include "depot_func.h"
#include "station_map.h" #include "station_map.h"
#include "tunnelbridge_map.h" #include "tunnelbridge_map.h"
@ -116,7 +117,7 @@ static void GenericPlaceRail(TileIndex tile, Track track)
*/ */
static void PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, 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 (GetRailTileType(tile) == RAIL_TILE_SIGNALS && !_settings_client.gui.auto_remove_signals) return;
if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) return; if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) return;
@ -137,25 +138,27 @@ static const DiagDirection _place_depot_extra_dir[12] = {
DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_NW, DIAGDIR_NE, 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 (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(); 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)) { if (IsTileType(tile, MP_RAILWAY)) {
PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]); 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); Tile double_depot_tile = tile + TileOffsByDiagDir(dir);
bool is_double_depot = IsValidTile(double_depot_tile) && IsRailDepotTile(double_depot_tile); 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]); 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]);
} }
}
} }
/** /**
@ -317,6 +320,7 @@ void CcBuildRailTunnel(Commands, const CommandCost &result, TileIndex tile)
static void ToggleRailButton_Remove(Window *w) static void ToggleRailButton_Remove(Window *w)
{ {
CloseWindowById(WC_SELECT_STATION, 0); CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
w->ToggleWidgetLoweredState(WID_RAT_REMOVE); w->ToggleWidgetLoweredState(WID_RAT_REMOVE);
w->SetWidgetDirty(WID_RAT_REMOVE); w->SetWidgetDirty(WID_RAT_REMOVE);
_remove_button_clicked = w->IsWidgetLowered(WID_RAT_REMOVE); _remove_button_clicked = w->IsWidgetLowered(WID_RAT_REMOVE);
@ -333,8 +337,9 @@ static bool RailToolbar_CtrlChanged(Window *w)
if (w->IsWidgetDisabled(WID_RAT_REMOVE)) return false; if (w->IsWidgetDisabled(WID_RAT_REMOVE)) return false;
/* allow ctrl to switch remove mode only for these widgets */ /* allow ctrl to switch remove mode only for these widgets */
for (WidgetID i = WID_RAT_BUILD_NS; i <= WID_RAT_BUILD_STATION; i++) { 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); ToggleRailButton_Remove(w);
return true; return true;
} }
@ -454,6 +459,7 @@ struct BuildRailToolbarWindow : Window {
if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true); if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true);
if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
CloseWindowById(WC_SELECT_STATION, 0); CloseWindowById(WC_SELECT_STATION, 0);
if (this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true);
this->Window::Close(); this->Window::Close();
} }
@ -533,6 +539,7 @@ struct BuildRailToolbarWindow : Window {
case WID_RAT_BUILD_EW: case WID_RAT_BUILD_EW:
case WID_RAT_BUILD_Y: case WID_RAT_BUILD_Y:
case WID_RAT_AUTORAIL: case WID_RAT_AUTORAIL:
case WID_RAT_BUILD_DEPOT:
case WID_RAT_BUILD_WAYPOINT: case WID_RAT_BUILD_WAYPOINT:
case WID_RAT_BUILD_STATION: case WID_RAT_BUILD_STATION:
case WID_RAT_BUILD_SIGNALS: case WID_RAT_BUILD_SIGNALS:
@ -688,9 +695,14 @@ struct BuildRailToolbarWindow : Window {
PlaceProc_DemolishArea(tile); PlaceProc_DemolishArea(tile);
break; break;
case WID_RAT_BUILD_DEPOT: case WID_RAT_BUILD_DEPOT: {
Command<CMD_BUILD_TRAIN_DEPOT>::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, tile, _cur_railtype, _build_depot_direction); CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
ViewportPlaceMethod 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; break;
}
case WID_RAT_BUILD_WAYPOINT: case WID_RAT_BUILD_WAYPOINT:
PlaceRail_Waypoint(tile); PlaceRail_Waypoint(tile);
@ -786,6 +798,20 @@ struct BuildRailToolbarWindow : Window {
} }
} }
break; break;
case DDSP_BUILD_DEPOT:
if (_remove_button_clicked) {
Command<CMD_REMOVE_TRAIN_DEPOT>::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<CMD_BUILD_TRAIN_DEPOT>::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;
} }
} }
} }
@ -795,6 +821,8 @@ struct BuildRailToolbarWindow : Window {
if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) SetViewportCatchmentStation(nullptr, true); 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_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true);
if (this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true);
this->RaiseButtons(); this->RaiseButtons();
this->DisableWidget(WID_RAT_REMOVE); this->DisableWidget(WID_RAT_REMOVE);
this->SetWidgetDirty(WID_RAT_REMOVE); this->SetWidgetDirty(WID_RAT_REMOVE);
@ -804,6 +832,7 @@ struct BuildRailToolbarWindow : Window {
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_RAIL); CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_RAIL);
CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_RAIL); CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_RAIL);
CloseWindowById(WC_SELECT_STATION, 0); CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
CloseWindowByClass(WC_BUILD_BRIDGE); CloseWindowByClass(WC_BUILD_BRIDGE);
} }
@ -815,8 +844,11 @@ struct BuildRailToolbarWindow : Window {
EventState OnCTRLStateChange() override EventState OnCTRLStateChange() override
{ {
/* do not toggle Remove button by Ctrl when placing station */ /* 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) && RailToolbar_CtrlChanged(this)) return ES_HANDLED; 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; return ES_NOT_HANDLED;
} }
@ -1705,6 +1737,12 @@ struct BuildRailDepotWindow : public PickerWindowBase {
this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction); 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 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; if (!IsInsideMM(widget, WID_BRAD_DEPOT_NE, WID_BRAD_DEPOT_NW + 1)) return;
@ -1734,6 +1772,7 @@ struct BuildRailDepotWindow : public PickerWindowBase {
case WID_BRAD_DEPOT_SE: case WID_BRAD_DEPOT_SE:
case WID_BRAD_DEPOT_SW: case WID_BRAD_DEPOT_SW:
case WID_BRAD_DEPOT_NW: case WID_BRAD_DEPOT_NW:
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
this->RaiseWidget(WID_BRAD_DEPOT_NE + _build_depot_direction); this->RaiseWidget(WID_BRAD_DEPOT_NE + _build_depot_direction);
_build_depot_direction = (DiagDirection)(widget - WID_BRAD_DEPOT_NE); _build_depot_direction = (DiagDirection)(widget - WID_BRAD_DEPOT_NE);
this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction); this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction);
@ -1742,6 +1781,11 @@ struct BuildRailDepotWindow : public PickerWindowBase {
break; break;
} }
} }
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
CheckRedrawDepotHighlight(this, VEH_TRAIN);
}
}; };
/** Nested widget definition of the build rail depot window */ /** Nested widget definition of the build rail depot window */

View File

@ -244,6 +244,19 @@ inline bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype)
return HasBit(GetRoadTypeInfo(enginetype)->powered_roadtypes, 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. * Returns the cost of building the specified roadtype.
* @param roadtype The roadtype being built. * @param roadtype The roadtype being built.

View File

@ -1141,18 +1141,34 @@ std::tuple<CommandCost, Money> CmdRemoveLongRoad(DoCommandFlag flags, TileIndex
* @param flags operation to perform * @param flags operation to perform
* @param rt road type * @param rt road type
* @param dir entrance direction * @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 * @return the cost of this operation or an error
* *
* @todo When checking for the tile slope, * @todo When checking for the tile slope,
* distinguish between "Flat land required" and "land sloped in wrong direction" * 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; 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); /* 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;
uint8_t num_new_depot_tiles = 0;
uint8_t num_rotated_depot_tiles = 0;
CommandCost cost(EXPENSES_CONSTRUCTION);
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 (tileh != SLOPE_FLAT) {
if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) { if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) {
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
@ -1160,47 +1176,44 @@ CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt,
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 */ /* Check whether a depot tile exists and it needs to be rotated. */
bool rotate_existing_depot = false; if (IsRoadDepotTile(t) &&
if (IsRoadDepotTile(tile) && (HasRoadTypeTram(tile) ? rt == GetRoadTypeTram(tile) : rt == GetRoadTypeRoad(tile))) GetDepotIndex(t) == join_to &&
{ (HasRoadTypeTram(t) ? rt == GetRoadTypeTram(t) : rt == GetRoadTypeRoad(t))) {
CommandCost ret = CheckTileOwnership(tile); if (dir == GetRoadDepotDirection(t)) continue;
ret = EnsureNoVehicleOnGround(t);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
if (dir == GetRoadDepotDirection(tile)) return CommandCost(); num_rotated_depot_tiles++;
if (flags & DC_EXEC) {
ret = EnsureNoVehicleOnGround(tile); SetRoadDepotExitDirection(t, dir);
if (ret.Failed()) return ret; MarkTileDirtyByTile(t);
rotate_existing_depot = true;
} }
if (!rotate_existing_depot) { } else {
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile)); cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, t));
if (cost.Failed()) return cost; 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) {
if (!Depot::CanAllocateItem()) return CMD_ERROR; MakeRoadDepot(t, _current_company, depot->index, dir, rt);
MarkTileDirtyByTile(t);
} }
}
}
if (num_new_depot_tiles + num_rotated_depot_tiles == 0) return CommandCost();
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
if (rotate_existing_depot) {
SetRoadDepotExitDirection(tile, dir);
} else {
Depot *dep = new Depot(tile);
dep->build_date = TimerGameCalendar::date;
MakeRoadDepot(tile, _current_company, dep->index, dir, rt);
MakeDefaultName(dep);
/* A road depot has two road bits. */ /* A road depot has two road bits. */
UpdateCompanyRoadInfrastructure(rt, _current_company, ROAD_DEPOT_TRACKBIT_FACTOR); UpdateCompanyRoadInfrastructure(rt, _current_company, num_new_depot_tiles * ROAD_DEPOT_TRACKBIT_FACTOR);
depot->AfterAddRemove(ta, true);
if (join_to == NEW_DEPOT) MakeDefaultName(depot);
} }
MarkTileDirtyByTile(tile); cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * (num_new_depot_tiles + num_rotated_depot_tiles));
}
cost.AddCost(_price[PR_BUILD_DEPOT_ROAD]);
return cost; return cost;
} }
@ -1215,7 +1228,8 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags)
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
Company *c = Company::GetIfValid(GetTileOwner(tile)); Depot *depot = Depot::GetByTile(tile);
Company *c = Company::GetIfValid(depot->owner);
if (c != nullptr) { if (c != nullptr) {
/* A road depot has two road bits. */ /* A road depot has two road bits. */
RoadType rt = GetRoadTypeRoad(tile); RoadType rt = GetRoadTypeRoad(tile);
@ -1224,8 +1238,8 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags)
DirtyCompanyInfrastructureWindows(c->index); DirtyCompanyInfrastructureWindows(c->index);
} }
delete Depot::GetByTile(tile);
DoClearSquare(tile); DoClearSquare(tile);
depot->AfterAddRemove(TileArea(tile), false);
} }
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]);
@ -2081,7 +2095,7 @@ static bool ClickTile_Road(TileIndex tile)
{ {
if (!IsRoadDepot(tile)) return false; if (!IsRoadDepot(tile)) return false;
ShowDepotWindow(tile, VEH_ROAD); ShowDepotWindow(GetDepotIndex(tile));
return true; return true;
} }
@ -2269,7 +2283,7 @@ static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int,
if (rv->Next() == nullptr) VehicleEnterDepot(rv->First()); if (rv->Next() == nullptr) VehicleEnterDepot(rv->First());
rv->tile = tile; rv->tile = tile;
InvalidateWindowData(WC_VEHICLE_DEPOT, rv->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile));
return VETSB_ENTERED_WORMHOLE; return VETSB_ENTERED_WORMHOLE;
} }
break; break;
@ -2543,8 +2557,9 @@ CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_s
if (IsRoadDepotTile(tile)) { if (IsRoadDepotTile(tile)) {
/* Update build vehicle window related to this depot */ /* Update build vehicle window related to this depot */
InvalidateWindowData(WC_VEHICLE_DEPOT, tile); DepotID depot_id = GetDepotIndex(tile);
InvalidateWindowData(WC_BUILD_VEHICLE, tile); InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
InvalidateWindowData(WC_BUILD_VEHICLE, depot_id);
} }
} }
} else { } else {

View File

@ -13,6 +13,7 @@
#include "direction_type.h" #include "direction_type.h"
#include "road_type.h" #include "road_type.h"
#include "command_type.h" #include "command_type.h"
#include "depot_type.h"
enum RoadStopClassID : uint16_t; 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); 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<CommandCost, Money> CmdRemoveLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, bool start_half, bool end_half); std::tuple<CommandCost, Money> 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 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); 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) 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 CcPlaySound_CONSTRUCTION_OTHER;
CommandCallback CcBuildRoadTunnel; 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); 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 */ #endif /* ROAD_CMD_H */

View File

@ -43,6 +43,7 @@
#include "picker_gui.h" #include "picker_gui.h"
#include "timer/timer.h" #include "timer/timer.h"
#include "timer/timer_game_calendar.h" #include "timer/timer_game_calendar.h"
#include "depot_func.h"
#include "widgets/road_widget.h" #include "widgets/road_widget.h"
@ -170,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 (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(); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
TileArea ta(start_tile, end_tile);
for (TileIndex tile : ta) {
ConnectRoadToStructure(tile, dir); ConnectRoadToStructure(tile, dir);
}
} }
/** /**
@ -368,6 +373,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 (_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 (_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(); this->Window::Close();
} }
@ -636,8 +642,10 @@ struct BuildRoadToolbarWindow : Window {
break; break;
case WID_ROT_DEPOT: case WID_ROT_DEPOT:
Command<CMD_BUILD_ROAD_DEPOT>::Post(this->rti->strings.err_depot, CcRoadDepot, CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
tile, _cur_roadtype, _road_depot_orientation);
VpSetPlaceSizingLimit(_settings_game.depot.depot_spread);
VpStartPlaceSizing(tile, (DiagDirToAxis(_road_depot_orientation) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_DEPOT);
break; break;
case WID_ROT_BUILD_WAYPOINT: case WID_ROT_BUILD_WAYPOINT:
@ -673,6 +681,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_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->RaiseButtons();
this->SetWidgetDisabledState(WID_ROT_REMOVE, true); this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
this->SetWidgetDirty(WID_ROT_REMOVE); this->SetWidgetDirty(WID_ROT_REMOVE);
@ -687,6 +697,7 @@ struct BuildRoadToolbarWindow : Window {
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD); CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD);
CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_ROAD); CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_ROAD);
CloseWindowById(WC_SELECT_STATION, 0); CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
CloseWindowByClass(WC_BUILD_BRIDGE); CloseWindowByClass(WC_BUILD_BRIDGE);
} }
@ -805,6 +816,18 @@ struct BuildRoadToolbarWindow : Window {
} }
break; 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<CMD_BUILD_ROAD_DEPOT>::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: case DDSP_CONVERT_ROAD:
Command<CMD_CONVERT_ROAD>::Post(rti->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype); Command<CMD_CONVERT_ROAD>::Post(rti->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype);
break; break;
@ -1106,6 +1129,12 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
this->FinishInitNested(TRANSPORT_ROAD); 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 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; if (!IsInsideMM(widget, WID_BROD_DEPOT_NE, WID_BROD_DEPOT_NW + 1)) return;
@ -1135,6 +1164,7 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
case WID_BROD_DEPOT_NE: case WID_BROD_DEPOT_NE:
case WID_BROD_DEPOT_SW: case WID_BROD_DEPOT_SW:
case WID_BROD_DEPOT_SE: case WID_BROD_DEPOT_SE:
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
this->RaiseWidget(WID_BROD_DEPOT_NE + _road_depot_orientation); this->RaiseWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
_road_depot_orientation = (DiagDirection)(widget - WID_BROD_DEPOT_NE); _road_depot_orientation = (DiagDirection)(widget - WID_BROD_DEPOT_NE);
this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation); this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
@ -1146,6 +1176,11 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
break; break;
} }
} }
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
CheckRedrawDepotHighlight(this, VEH_ROAD);
}
}; };
static constexpr NWidgetPart _nested_build_road_depot_widgets[] = { static constexpr NWidgetPart _nested_build_road_depot_widgets[] = {

View File

@ -37,6 +37,7 @@
#include "framerate_type.h" #include "framerate_type.h"
#include "roadveh_cmd.h" #include "roadveh_cmd.h"
#include "road_cmd.h" #include "road_cmd.h"
#include "depot_base.h"
#include "table/strings.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; 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. * Build a road vehicle.
* @param flags type of operation. * @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) 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; RoadType rt = e->u.road.roadtype;
const RoadTypeInfo *rti = GetRoadTypeInfo(rt); 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) { if (flags & DC_EXEC) {
const RoadVehicleInfo *rvi = &e->u.road; const RoadVehicleInfo *rvi = &e->u.road;
@ -1018,7 +1056,9 @@ bool RoadVehLeaveDepot(RoadVehicle *v, bool first)
if (first) { if (first) {
/* We are leaving a depot, but have to go to the exact same one; re-enter */ /* 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); VehicleEnterDepot(v);
return true; return true;
} }
@ -1043,7 +1083,7 @@ bool RoadVehLeaveDepot(RoadVehicle *v, bool first)
v->UpdatePosition(); v->UpdatePosition();
v->UpdateInclination(true, true); v->UpdateInclination(true, true);
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
return true; return true;
} }

View File

@ -294,6 +294,10 @@ static void InitializeWindowsAndCaches()
} }
} }
for (Depot *dep : Depot::Iterate()) {
dep->RescanDepotTiles();
}
RecomputePrices(); RecomputePrices();
GroupStatistics::UpdateAfterLoad(); GroupStatistics::UpdateAfterLoad();
@ -795,6 +799,16 @@ bool AfterLoadGame()
_settings_game.linkgraph.recalc_time *= CalendarTime::SECONDS_PER_DAY; _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;
}
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 */ /* Load the sprites */
GfxLoadSprites(); GfxLoadSprites();
LoadStringWidthTable(); LoadStringWidthTable();
@ -2425,28 +2439,6 @@ bool AfterLoadGame()
for (Depot *d : Depot::Iterate()) d->build_date = TimerGameCalendar::date; 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. */ /* Move the animation frame to the same location (m7) for all objects. */
if (IsSavegameVersionBefore(SLV_147)) { if (IsSavegameVersionBefore(SLV_147)) {
for (auto t : Map::Iterate()) { for (auto t : Map::Iterate()) {
@ -2792,6 +2784,102 @@ 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);
}
}
}
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);
}
}
}
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.
* 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. */ /* 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) { 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; _settings_game.game_creation.snow_line_height /= TILE_HEIGHT;

View File

@ -108,6 +108,7 @@ const SaveLoadCompat _station_normal_sl_compat[] = {
SLC_VAR("airport.layout"), SLC_VAR("airport.layout"),
SLC_VAR("airport.flags"), SLC_VAR("airport.flags"),
SLC_VAR("airport.rotation"), SLC_VAR("airport.rotation"),
SLC_VAR("airport.hangar"),
SLC_VAR("storage"), SLC_VAR("storage"),
SLC_VAR("airport.psa"), SLC_VAR("airport.psa"),
SLC_VAR("indtype"), SLC_VAR("indtype"),

View File

@ -27,6 +27,12 @@ static const SaveLoad _depot_desc[] = {
SLE_CONDVAR(Depot, town_cn, SLE_UINT16, SLV_141, SL_MAX_VERSION), SLE_CONDVAR(Depot, town_cn, SLE_UINT16, SLV_141, SL_MAX_VERSION),
SLE_CONDSSTR(Depot, name, SLE_STR, 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, 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 { struct DEPTChunkHandler : ChunkHandler {

View File

@ -690,6 +690,8 @@ static bool LoadOldDepot(LoadgameState *ls, int num)
if (d->xy != 0) { if (d->xy != 0) {
d->town = RemapTown(d->xy); d->town = RemapTown(d->xy);
} else { } else {
d->owner = INVALID_OWNER;
d->veh_type = VEH_INVALID;
delete d; delete d;
} }

View File

@ -16,6 +16,7 @@
#include "../order_backup.h" #include "../order_backup.h"
#include "../settings_type.h" #include "../settings_type.h"
#include "../network/network.h" #include "../network/network.h"
#include "../depot_map.h"
#include "../safeguards.h" #include "../safeguards.h"
@ -243,11 +244,14 @@ struct ORDLChunkHandler : ChunkHandler {
} }
}; };
static TileIndex _tile;
SaveLoadTable GetOrderBackupDescription() SaveLoadTable GetOrderBackupDescription()
{ {
static const SaveLoad _order_backup_desc[] = { static const SaveLoad _order_backup_desc[] = {
SLE_VAR(OrderBackup, user, SLE_UINT32), 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_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_FILE_U32 | SLE_VAR_U16, SL_MIN_VERSION, SLV_192),
SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, SLV_192, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, SLV_192, SL_MAX_VERSION),
@ -297,6 +301,8 @@ struct BKORChunkHandler : ChunkHandler {
OrderBackup *ob = new (index) OrderBackup(); OrderBackup *ob = new (index) OrderBackup();
SlObject(ob, slt); SlObject(ob, slt);
} }
if (IsSavegameVersionBefore(SLV_DEPOTID_BACKUP_ORDERS)) _order_backup_pool.CleanPool();
} }
void FixPointers() const override void FixPointers() const override

View File

@ -23,6 +23,7 @@
#include "../stdafx.h" #include "../stdafx.h"
#include "../debug.h" #include "../debug.h"
#include "../station_base.h" #include "../station_base.h"
#include "../depot_base.h"
#include "../thread.h" #include "../thread.h"
#include "../town.h" #include "../town.h"
#include "../network/network.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_STORAGE: return ((const PersistentStorage*)obj)->index + 1;
case REF_LINK_GRAPH: return ((const LinkGraph*)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_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1;
case REF_DEPOT: return ((const Depot*)obj)->index + 1;
default: NOT_REACHED(); default: NOT_REACHED();
} }
} }
@ -1202,6 +1204,10 @@ static void *IntToReference(size_t index, SLRefType rt)
if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index); if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
SlErrorCorrupt("Referencing invalid LinkGraphJob"); SlErrorCorrupt("Referencing invalid LinkGraphJob");
case REF_DEPOT:
if (Depot::IsValidID(index)) return Depot::Get(index);
SlErrorCorrupt("Referencing invalid Depot");
default: NOT_REACHED(); default: NOT_REACHED();
} }
} }

View File

@ -383,6 +383,13 @@ enum SaveLoadVersion : uint16_t {
SLV_GROUP_NUMBERS, ///< 336 PR#12297 Add per-company group numbers. 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_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_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.
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 SL_MAX_VERSION, ///< Highest possible saveload version
}; };
@ -598,6 +605,7 @@ enum SLRefType {
REF_STORAGE = 9, ///< Load/save a reference to a persistent storage. 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 = 10, ///< Load/save a reference to a link graph.
REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job. REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job.
REF_DEPOT = 12, ///< Load/save a reference to a depot.
}; };
/** /**

View File

@ -610,6 +610,7 @@ public:
SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION), SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION),
SLE_VAR(Station, airport.flags, SLE_UINT64), SLE_VAR(Station, airport.flags, SLE_UINT64),
SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION), SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION),
SLE_CONDREF(Station, airport.hangar, REF_DEPOT, SLV_ADD_DEPOTS_TO_HANGARS, SL_MAX_VERSION),
SLEG_CONDARR("storage", _old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161), SLEG_CONDARR("storage", _old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161),
SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION),

View File

@ -17,33 +17,16 @@
ScriptDepotList::ScriptDepotList(ScriptTile::TransportType transport_type) ScriptDepotList::ScriptDepotList(ScriptTile::TransportType transport_type)
{ {
EnforceDeityOrCompanyModeValid_Void(); EnforceDeityOrCompanyModeValid_Void();
::TileType tile_type; static_assert(VEH_TRAIN == (int)ScriptTile::TRANSPORT_RAIL);
switch (transport_type) { static_assert(VEH_ROAD == (int)ScriptTile::TRANSPORT_ROAD);
default: return; 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(); bool is_deity = ScriptCompanyMode::IsDeity();
CompanyID owner = ScriptObject::GetCompany(); CompanyID owner = ScriptObject::GetCompany();
for (const Depot *depot : Depot::Iterate()) { 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());
} }
} }

View File

@ -83,7 +83,7 @@
EnforcePrecondition(false, ::IsValidTile(front)); EnforcePrecondition(false, ::IsValidTile(front));
EnforcePrecondition(false, (::TileX(front) == ::TileX(tile)) != (::TileY(front) == ::TileY(tile))); EnforcePrecondition(false, (::TileX(front) == ::TileX(tile)) != (::TileY(front) == ::TileY(tile)));
return ScriptObject::Command<CMD_BUILD_SHIP_DEPOT>::Do(tile, ::TileX(front) == ::TileX(tile) ? AXIS_Y : AXIS_X); return ScriptObject::Command<CMD_BUILD_SHIP_DEPOT>::Do(tile, ::TileX(front) == ::TileX(tile) ? AXIS_Y : AXIS_X, false, INVALID_DEPOT, tile);
} }
/* static */ bool ScriptMarine::BuildDock(TileIndex tile, StationID station_id) /* static */ bool ScriptMarine::BuildDock(TileIndex tile, StationID station_id)

View File

@ -245,18 +245,13 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
const Order *order = ::ResolveOrder(vehicle_id, order_position); const Order *order = ::ResolveOrder(vehicle_id, order_position);
if (order == nullptr || order->GetType() == OT_CONDITIONAL) return INVALID_TILE; if (order == nullptr || order->GetType() == OT_CONDITIONAL) return INVALID_TILE;
const Vehicle *v = ::Vehicle::Get(vehicle_id);
switch (order->GetType()) { switch (order->GetType()) {
case OT_GOTO_DEPOT: { case OT_GOTO_DEPOT: {
/* We don't know where the nearest depot is... (yet) */ /* We don't know where the nearest depot is... (yet) */
if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) return INVALID_TILE; if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) return INVALID_TILE;
if (v->type != VEH_AIRCRAFT) return ::Depot::Get(order->GetDestination())->xy; 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);
} }
case OT_GOTO_STATION: { case OT_GOTO_STATION: {

View File

@ -145,7 +145,16 @@
DiagDirection entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? DIAGDIR_SE : DIAGDIR_NW) : (::TileX(tile) < ::TileX(front) ? DIAGDIR_SW : DIAGDIR_NE); 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<CMD_BUILD_TRAIN_DEPOT>::Do(tile, (::RailType)ScriptObject::GetRailType(), entrance_dir); return ScriptObject::Command<CMD_BUILD_TRAIN_DEPOT>::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<CMD_REMOVE_TRAIN_DEPOT>::Do(start_tile, end_tile);
} }
/* static */ bool ScriptRail::BuildRailStation(TileIndex tile, RailTrack direction, SQInteger num_platforms, SQInteger platform_length, StationID station_id) /* static */ bool ScriptRail::BuildRailStation(TileIndex tile, RailTrack direction, SQInteger num_platforms, SQInteger platform_length, StationID station_id)

View File

@ -240,6 +240,18 @@ public:
*/ */
static bool BuildRailDepot(TileIndex tile, TileIndex front); 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. * Build a rail station.
* @param tile Place to build the station. * @param tile Place to build the station.

View File

@ -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); 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<CMD_BUILD_ROAD_DEPOT>::Do(tile, ScriptObject::GetRoadType(), entrance_dir); return ScriptObject::Command<CMD_BUILD_ROAD_DEPOT>::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) /* static */ bool ScriptRoad::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id)

View File

@ -2145,6 +2145,15 @@ static SettingsContainer &GetSettingsTree()
SettingsPage *limitations = main->Add(new SettingsPage(STR_CONFIG_SETTING_LIMITATIONS)); 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"));
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")); limitations->Add(new SettingEntry("construction.command_pause_level"));
limitations->Add(new SettingEntry("construction.autoslope")); limitations->Add(new SettingEntry("construction.autoslope"));
limitations->Add(new SettingEntry("construction.extra_dynamite")); limitations->Add(new SettingEntry("construction.extra_dynamite"));

View File

@ -380,6 +380,22 @@ static void SpriteZoomMinChanged(int32_t)
MarkWholeScreenDirty(); 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 * 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 * its widget parts might change. Reinit all windows as it allows access to the

View File

@ -570,6 +570,15 @@ struct StationSettings {
uint8_t station_spread; ///< amount a station may spread 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
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. */ /** Default settings for vehicles. */
struct VehicleDefaultSettings { struct VehicleDefaultSettings {
bool servint_ispercent; ///< service intervals are in percents bool servint_ispercent; ///< service intervals are in percents
@ -603,6 +612,7 @@ struct GameSettings {
EconomySettings economy; ///< settings to change the economy EconomySettings economy; ///< settings to change the economy
LinkGraphSettings linkgraph; ///< settings for link graph calculations LinkGraphSettings linkgraph; ///< settings for link graph calculations
StationSettings station; ///< settings related to station management 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 LocaleSettings locale; ///< settings related to used currency/unit system in the current game
}; };

View File

@ -187,8 +187,9 @@ static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
const Depot *best_depot = nullptr; const Depot *best_depot = nullptr;
uint best_dist_sq = std::numeric_limits<uint>::max(); uint best_dist_sq = std::numeric_limits<uint>::max();
for (const Depot *depot : Depot::Iterate()) { for (const Depot *depot : Depot::Iterate()) {
if (depot->veh_type != VEH_SHIP || depot->owner != v->owner) continue;
const TileIndex tile = depot->xy; const TileIndex tile = depot->xy;
if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
const uint dist_sq = DistanceSquare(tile, v->tile); const uint dist_sq = DistanceSquare(tile, v->tile);
if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance && if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance &&
visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) { visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) {
@ -196,7 +197,6 @@ static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
best_depot = depot; best_depot = depot;
} }
} }
}
return best_depot; return best_depot;
} }
@ -222,7 +222,7 @@ static void CheckIfShipNeedsService(Vehicle *v)
} }
v->current_order.MakeGoToDepot(depot->index, ODTFB_SERVICE); 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); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
} }
@ -424,12 +424,13 @@ static bool CheckShipLeaveDepot(Ship *v)
v->cur_speed = 0; v->cur_speed = 0;
v->UpdateViewport(true, true); v->UpdateViewport(true, true);
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); DepotID depot_id = GetDepotIndex(v->tile);
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
VehicleServiceInDepot(v); VehicleServiceInDepot(v);
v->LeaveUnbunchingDepot(); v->LeaveUnbunchingDepot();
v->PlayLeaveStationSound(); v->PlayLeaveStationSound();
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
SetWindowClassesDirty(WC_SHIPS_LIST); SetWindowClassesDirty(WC_SHIPS_LIST);
return false; return false;
@ -961,5 +962,5 @@ ClosestDepot Ship::FindClosestDepot()
const Depot *depot = FindClosestShipDepot(this, MAX_SHIP_DEPOT_SEARCH_DISTANCE); const Depot *depot = FindClosestShipDepot(this, MAX_SHIP_DEPOT_SEARCH_DISTANCE);
if (depot == nullptr) return ClosestDepot(); if (depot == nullptr) return ClosestDepot();
return ClosestDepot(depot->xy, depot->index); return ClosestDepot(depot->GetBestDepotTile(this), depot->index);
} }

View File

@ -26,6 +26,7 @@
#include "core/random_func.hpp" #include "core/random_func.hpp"
#include "linkgraph/linkgraph.h" #include "linkgraph/linkgraph.h"
#include "linkgraph/linkgraphschedule.h" #include "linkgraph/linkgraphschedule.h"
#include "depot_base.h"
#include "table/strings.h" #include "table/strings.h"
@ -727,6 +728,52 @@ Money AirportMaintenanceCost(Owner owner)
return total_cost >> 3; return total_cost >> 3;
} }
/**
* Create a hangar on the airport.
*/
void Airport::AddHangar()
{
assert(this->hangar == nullptr);
assert(Depot::CanAllocateItem());
assert(this->GetNumHangars() > 0);
Station *st = Station::GetByTile(this->GetHangarTile(0));
this->hangar = new Depot(this->GetHangarTile(0), 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));
}
}
/**
* Delete the hangar on the airport.
*/
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 bool StationCompare::operator() (const Station *lhs, const Station *rhs) const
{ {
return lhs->index < rhs->index; return lhs->index < rhs->index;

View File

@ -18,6 +18,7 @@
#include "linkgraph/linkgraph_type.h" #include "linkgraph/linkgraph_type.h"
#include "newgrf_storage.h" #include "newgrf_storage.h"
#include "bitmap_type.h" #include "bitmap_type.h"
#include "depot_type.h"
static const uint8_t INITIAL_STATION_RATING = 175; static const uint8_t INITIAL_STATION_RATING = 175;
static const uint8_t MAX_STATION_RATING = 255; 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 type; ///< Type of this airport, @see AirportTypes
uint8_t layout; ///< Airport layout number. uint8_t layout; ///< Airport layout number.
Direction rotation; ///< How this airport is rotated. Direction rotation; ///< How this airport is rotated.
Depot *hangar; ///< The corresponding hangar of this airport, if any.
PersistentStorage *psa; ///< Persistent storage for NewGRF airports. PersistentStorage *psa; ///< Persistent storage for NewGRF airports.
@ -404,6 +406,9 @@ struct Airport : public TileArea {
return num; return num;
} }
void AddHangar();
void RemoveHangar();
private: private:
/** /**
* Retrieve hangar information of a hangar at a given tile. * Retrieve hangar information of a hangar at a given tile.

View File

@ -67,6 +67,7 @@
#include "timer/timer_game_tick.h" #include "timer/timer_game_tick.h"
#include "cheat_type.h" #include "cheat_type.h"
#include "road_func.h" #include "road_func.h"
#include "depot_base.h"
#include "widgets/station_widget.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 */ /* Check if a valid, buildable airport was chosen for construction */
const AirportSpec *as = AirportSpec::Get(airport_type); 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->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
if (!as->IsWithinMapBounds(layout, tile)) 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); AirportTileAnimationTrigger(st, iter, AAT_BUILT);
} }
if (!as->depots.empty()) st->airport.AddHangar();
UpdateAirplanesOnNewStation(st); UpdateAirplanesOnNewStation(st);
Company::Get(st->owner)->infrastructure.airport++; Company::Get(st->owner)->infrastructure.airport++;
@ -2674,11 +2679,7 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags)
} }
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
for (uint i = 0; i < st->airport.GetNumHangars(); ++i) { st->airport.RemoveHangar();
TileIndex tile_cur = st->airport.GetHangarTile(i);
OrderBackup::Reset(tile_cur, false);
CloseWindowById(WC_VEHICLE_DEPOT, tile_cur);
}
/* The noise level is the noise from the airport and reduce it to account for the distance to the town center. /* 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 * And as for construction, always remove it, even if the setting is not set, in order to avoid the
@ -3692,8 +3693,8 @@ static bool ClickTile_Station(TileIndex tile)
if (bst->facilities & FACIL_WAYPOINT) { if (bst->facilities & FACIL_WAYPOINT) {
ShowWaypointWindow(Waypoint::From(bst)); ShowWaypointWindow(Waypoint::From(bst));
} else if (IsHangar(tile)) { } else if (IsHangar(tile)) {
const Station *st = Station::From(bst); assert(Station::From(bst)->airport.HasHangar());
ShowDepotWindow(st->airport.GetHangarTile(st->airport.GetHangarNum(tile)), VEH_AIRCRAFT); ShowDepotWindow(Station::From(bst)->airport.hangar->index);
} else { } else {
ShowStationViewWindow(bst->index); ShowStationViewWindow(bst->index);
} }

View File

@ -21,6 +21,8 @@ static bool CheckRoadSide(int32_t &new_value);
static bool CheckDynamicEngines(int32_t &new_value); static bool CheckDynamicEngines(int32_t &new_value);
static void StationCatchmentChanged(int32_t new_value); static void StationCatchmentChanged(int32_t new_value);
static void MaxVehiclesChanged(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[] = { static const SettingVariant _game_settings_table[] = {
[post-amble] [post-amble]
@ -145,6 +147,47 @@ str = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS
strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS_HELPTEXT strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS_HELPTEXT
post_cb = [](auto) { CloseWindowById(WC_SELECT_STATION, 0); } 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
post_cb = [](auto) { CloseWindowByClass(WC_SELECT_DEPOT); }
[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
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] [SDT_OMANY]
var = vehicle.road_side var = vehicle.road_side
type = SLE_UINT8 type = SLE_UINT8

View File

@ -26,6 +26,8 @@ void VpStartDragging(ViewportDragDropSelectionProcess process);
void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process); void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process);
void VpSetPresizeRange(TileIndex from, TileIndex to); void VpSetPresizeRange(TileIndex from, TileIndex to);
void VpSetPlaceSizingLimit(int limit); void VpSetPlaceSizingLimit(int limit);
void VpSetPlaceFixedSize(uint8_t fixed_size);
void VpResetFixedSize();
void UpdateTileSelection(); void UpdateTileSelection();

View File

@ -60,6 +60,7 @@ struct TileHighlightData {
Point selstart; ///< The location where the dragging started. Point selstart; ///< The location where the dragging started.
Point selend; ///< The location where the drag currently ends. 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 drawstyle; ///< Lower bits 0-3 are reserved for detailed highlight information.
HighLightStyle next_drawstyle; ///< Queued, but not yet drawn style. HighLightStyle next_drawstyle; ///< Queued, but not yet drawn style.

View File

@ -353,4 +353,6 @@ protected: // These functions should not be called outside acceleration code.
} }
}; };
bool HasCompatibleDepotTile(TileIndex tile, const Train *t);
#endif /* TRAIN_H */ #endif /* TRAIN_H */

View File

@ -7,6 +7,7 @@
/** @file train_cmd.cpp Handling of trains. */ /** @file train_cmd.cpp Handling of trains. */
#include "depot_map.h"
#include "stdafx.h" #include "stdafx.h"
#include "error.h" #include "error.h"
#include "articulated_vehicles.h" #include "articulated_vehicles.h"
@ -38,6 +39,7 @@
#include "misc_cmd.h" #include "misc_cmd.h"
#include "timer/timer_game_calendar.h" #include "timer/timer_game_calendar.h"
#include "timer/timer_game_economy.h" #include "timer/timer_game_economy.h"
#include "depot_base.h"
#include "table/strings.h" #include "table/strings.h"
#include "table/train_sprites.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. * Build a railroad wagon.
* @param flags type of operation. * @param flags type of operation.
@ -615,9 +676,12 @@ void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs,
static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret) static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{ {
const RailVehicleInfo *rvi = &e->u.rail; 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 */ /* Find a good tile to place the wagon. */
if (!IsCompatibleRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR; tile = FindCompatibleDepotTile(depot_id, rvi->railtype, false);
if (tile == INVALID_TILE) return CMD_ERROR;
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
Train *v = new Train(); Train *v = new Train();
@ -645,7 +709,7 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const
v->SetWagon(); v->SetWagon();
v->SetFreeWagon(); v->SetFreeWagon();
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
v->cargo_type = e->GetDefaultCargoType(); v->cargo_type = e->GetDefaultCargoType();
assert(IsValidCargoID(v->cargo_type)); assert(IsValidCargoID(v->cargo_type));
@ -673,7 +737,8 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const
/* Try to connect the vehicle to one of free chains of wagons. */ /* Try to connect the vehicle to one of free chains of wagons. */
for (Train *w : Train::Iterate()) { 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->IsFreeWagon() && ///< A free wagon chain
w->engine_type == e->index && ///< Same type w->engine_type == e->index && ///< Same type
w->First() != v && ///< Don't connect to ourself w->First() != v && ///< Don't connect to ourself
@ -692,9 +757,10 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const
void NormalizeTrainVehInDepot(const Train *u) void NormalizeTrainVehInDepot(const Train *u)
{ {
assert(u->IsEngine()); assert(u->IsEngine());
DepotID dep_id = GetDepotIndex(u->tile);
for (const Train *v : Train::Iterate()) { for (const Train *v : Train::Iterate()) {
if (v->IsFreeWagon() && v->tile == u->tile && if (v->IsFreeWagon() && v->IsInDepot() &&
v->track == TRACK_BIT_DEPOT) { GetDepotIndex(v->tile) == dep_id) {
if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(DC_EXEC, v->index, u->index, true).Failed()) { if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(DC_EXEC, v->index, u->index, true).Failed()) {
break; break;
} }
@ -748,13 +814,14 @@ static void AddRearEngineToMultiheadedTrain(Train *v)
*/ */
CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret) CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{ {
assert(IsRailDepotTile(tile));
const RailVehicleInfo *rvi = &e->u.rail; const RailVehicleInfo *rvi = &e->u.rail;
if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret); if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret);
/* Check if depot and new engine uses the same kind of tracks * /* Find a good tile to place the engine and get power on it. */
* We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */ tile = FindCompatibleDepotTile(GetDepotIndex(tile), rvi->railtype, true);
if (!HasPowerOnRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR; if (tile == INVALID_TILE) return CMD_ERROR;
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
DiagDirection dir = GetRailDepotDirection(tile); DiagDirection dir = GetRailDepotDirection(tile);
@ -824,10 +891,10 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin
static Train *FindGoodVehiclePos(const Train *src) static Train *FindGoodVehiclePos(const Train *src)
{ {
EngineID eng = src->engine_type; EngineID eng = src->engine_type;
TileIndex tile = src->tile; DepotID dep_id = GetDepotIndex(src->tile);
for (Train *dst : Train::Iterate()) { 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. */ /* check so all vehicles in the line have the same engine. */
Train *t = dst; Train *t = dst;
while (t->engine_type == eng) { while (t->engine_type == eng) {
@ -984,6 +1051,82 @@ static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *origina
return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME); 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<TileIndex>::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. * Check whether the train parts can be attached.
* @param t the train to check * @param t the train to check
@ -994,6 +1137,8 @@ static CommandCost CheckTrainAttachment(Train *t)
/* No multi-part train, no need to check. */ /* No multi-part train, no need to check. */
if (t == nullptr || t->Next() == nullptr) return CommandCost(); 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 /* 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. */ * 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; int allowed_len = _settings_game.vehicle.max_train_length * TILE_SIZE - t->gcache.cached_veh_length;
@ -1229,7 +1374,7 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
Train *dst_head; Train *dst_head;
if (dst != nullptr) { if (dst != nullptr) {
dst_head = dst->First(); 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 */ /* Now deal with articulated part of destination wagon */
dst = dst->GetLastEnginePart(); dst = dst->GetLastEnginePart();
} else { } else {
@ -1359,11 +1504,18 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
CheckCargoCapacity(dst_head); CheckCargoCapacity(dst_head);
} }
if (src_head != nullptr) src_head->First()->MarkDirty(); if (src_head != nullptr) {
if (dst_head != nullptr) dst_head->First()->MarkDirty(); 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. */ /* 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); InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
} else { } else {
/* We don't want to execute what we're just tried. */ /* We don't want to execute what we're just tried. */
@ -1447,7 +1599,7 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b
NormaliseTrainHead(new_head); NormaliseTrainHead(new_head);
/* We are undoubtedly changing something in the depot and train list. */ /* 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); InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
/* Actually delete the sold 'goods' */ /* Actually delete the sold 'goods' */
@ -1966,7 +2118,7 @@ void ReverseTrainDirection(Train *v)
{ {
if (IsRailDepotTile(v->tile)) { if (IsRailDepotTile(v->tile)) {
if (IsWholeTrainInsideDepot(v)) return; 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. */ /* Clear path reservation in front if train is not stuck. */
@ -1989,7 +2141,7 @@ void ReverseTrainDirection(Train *v)
AdvanceWagonsAfterSwap(v); AdvanceWagonsAfterSwap(v);
if (IsRailDepotTile(v->tile)) { if (IsRailDepotTile(v->tile)) {
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
} }
ToggleBit(v->flags, VRF_TOGGLE_REVERSE); ToggleBit(v->flags, VRF_TOGGLE_REVERSE);
@ -2079,7 +2231,7 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool
ToggleBit(v->flags, VRF_REVERSE_DIRECTION); ToggleBit(v->flags, VRF_REVERSE_DIRECTION);
front->ConsistChanged(CCF_ARRANGE); 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_DETAILS, front->index);
SetWindowDirty(WC_VEHICLE_VIEW, front->index); SetWindowDirty(WC_VEHICLE_VIEW, front->index);
SetWindowClassesDirty(WC_TRAINS_LIST); SetWindowClassesDirty(WC_TRAINS_LIST);
@ -2273,7 +2425,7 @@ static bool CheckTrainStayInDepot(Train *v)
/* if the train got no power, then keep it in the depot */ /* if the train got no power, then keep it in the depot */
if (v->gcache.cached_power == 0) { if (v->gcache.cached_power == 0) {
v->vehstatus |= VS_STOPPED; v->vehstatus |= VS_STOPPED;
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
return true; return true;
} }
@ -2302,7 +2454,9 @@ static bool CheckTrainStayInDepot(Train *v)
} }
/* We are leaving a depot, but have to go to the exact same one; re-enter. */ /* 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. */ /* Service when depot has no reservation. */
if (!HasDepotReservation(v->tile)) VehicleEnterDepot(v); if (!HasDepotReservation(v->tile)) VehicleEnterDepot(v);
return true; return true;
@ -2334,7 +2488,7 @@ static bool CheckTrainStayInDepot(Train *v)
v->UpdatePosition(); v->UpdatePosition();
UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner); UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
v->UpdateAcceleration(); v->UpdateAcceleration();
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
return false; return false;
} }
@ -3654,7 +3808,7 @@ static void DeleteLastWagon(Train *v)
/* Update the depot window if the first vehicle is in depot - /* Update the depot window if the first vehicle is in depot -
* if v == first, then it is updated in PreDestructor() */ * if v == first, then it is updated in PreDestructor() */
if (first->track == TRACK_BIT_DEPOT) { 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 v->last_station_visited = first->last_station_visited; // for PreDestructor
} }

View File

@ -15,6 +15,7 @@
#include "vehicle_func.h" #include "vehicle_func.h"
#include "zoom_func.h" #include "zoom_func.h"
#include "train_cmd.h" #include "train_cmd.h"
#include "depot_map.h"
#include "table/strings.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) void CcBuildWagon(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray, TileIndex tile, EngineID, bool, CargoID, ClientID)
{ {
if (result.Failed()) return; if (result.Failed()) return;
DepotID depot_id = GetDepotIndex(tile);
/* find a locomotive in the depot. */ /* find a locomotive in the depot. */
const Vehicle *found = nullptr; const Vehicle *found = nullptr;
for (const Train *t : Train::Iterate()) { 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. if (found != nullptr) return; // must be exactly one.
found = t; found = t;
} }

View File

@ -297,7 +297,7 @@ uint Vehicle::Crash(bool)
InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0); InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP); SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
SetWindowDirty(WC_VEHICLE_DETAILS, this->index); 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; delete this->cargo_payment;
assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment 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 (v->disaster_vehicle != INVALID_VEHICLE) ReleaseDisasterVehicle(v->disaster_vehicle);
} }
if (this->Previous() == nullptr) { if (this->Previous() == nullptr && IsDepotTile(this->tile)) {
InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile));
} }
if (this->IsPrimaryVehicle()) { if (this->IsPrimaryVehicle()) {
@ -1554,6 +1554,7 @@ void VehicleEnterDepot(Vehicle *v)
/* Always work with the front of the vehicle */ /* Always work with the front of the vehicle */
assert(v == v->First()); assert(v == v->First());
DepotID depot_id = GetDepotIndex(v->tile);
switch (v->type) { switch (v->type) {
case VEH_TRAIN: { case VEH_TRAIN: {
Train *t = Train::From(v); Train *t = Train::From(v);
@ -1580,7 +1581,7 @@ void VehicleEnterDepot(Vehicle *v)
ship->state = TRACK_BIT_DEPOT; ship->state = TRACK_BIT_DEPOT;
ship->UpdateCache(); ship->UpdateCache();
ship->UpdateViewport(true, true); ship->UpdateViewport(true, true);
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
break; break;
} }
@ -1595,9 +1596,9 @@ void VehicleEnterDepot(Vehicle *v)
if (v->type != VEH_TRAIN) { 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. /* 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 */ * 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->vehstatus |= VS_HIDDEN;
v->cur_speed = 0; v->cur_speed = 0;
@ -1619,7 +1620,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. */ * 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) && if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) && 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. */ /* We are heading for another depot, keep driving. */
return; return;
} }

View File

@ -37,6 +37,7 @@
#include "roadveh_cmd.h" #include "roadveh_cmd.h"
#include "train_cmd.h" #include "train_cmd.h"
#include "ship_cmd.h" #include "ship_cmd.h"
#include "depot_base.h"
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
@ -178,7 +179,7 @@ std::tuple<CommandCost, VehicleID, uint, uint16_t, CargoArray> CmdBuildVehicle(D
NormalizeTrainVehInDepot(Train::From(v)); NormalizeTrainVehInDepot(Train::From(v));
} }
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
InvalidateWindowClassesData(GetWindowClassForVehicleType(type), 0); InvalidateWindowClassesData(GetWindowClassForVehicleType(type), 0);
SetWindowDirty(WC_COMPANY, _current_company); SetWindowDirty(WC_COMPANY, _current_company);
if (IsLocalCompany()) { if (IsLocalCompany()) {
@ -553,7 +554,7 @@ std::tuple<CommandCost, uint, uint16_t, CargoArray> CmdRefitVehicle(DoCommandFla
InvalidateWindowData(WC_VEHICLE_DETAILS, front->index); InvalidateWindowData(WC_VEHICLE_DETAILS, front->index);
InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
} }
SetWindowDirty(WC_VEHICLE_DEPOT, front->tile); if (IsDepotTile(front->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile));
} else { } else {
/* Always invalidate the cache; querycost might have filled it. */ /* Always invalidate the cache; querycost might have filled it. */
v->InvalidateNewGRFCacheOfChain(); v->InvalidateNewGRFCacheOfChain();
@ -639,7 +640,7 @@ CommandCost CmdStartStopVehicle(DoCommandFlag flags, VehicleID veh_id, bool eval
v->MarkDirty(); v->MarkDirty();
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); 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)); SetWindowClassesDirty(GetWindowClassForVehicleType(v->type));
InvalidateWindowData(WC_VEHICLE_VIEW, v->index); InvalidateWindowData(WC_VEHICLE_VIEW, v->index);
} }
@ -667,7 +668,7 @@ CommandCost CmdMassStartStopVehicle(DoCommandFlag flags, TileIndex tile, bool do
} else { } else {
if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR; if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
/* Get the list of vehicles in the depot */ /* 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) { for (const Vehicle *v : list) {
@ -699,7 +700,7 @@ CommandCost CmdDepotSellAllVehicles(DoCommandFlag flags, TileIndex tile, Vehicle
if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR; if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
/* Get the list of vehicles in the depot */ /* 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; CommandCost last_error = CMD_ERROR;
bool had_success = false; bool had_success = false;
@ -732,7 +733,7 @@ CommandCost CmdDepotMassAutoReplace(DoCommandFlag flags, TileIndex tile, Vehicle
if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR; if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
/* Get the list of vehicles in the depot */ /* 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) { for (const Vehicle *v : list) {
/* Ensure that the vehicle completely in the depot */ /* Ensure that the vehicle completely in the depot */

View File

@ -45,6 +45,7 @@
#include "train_cmd.h" #include "train_cmd.h"
#include "hotkeys.h" #include "hotkeys.h"
#include "group_cmd.h" #include "group_cmd.h"
#include "depot_base.h"
#include "safeguards.h" #include "safeguards.h"
@ -2122,7 +2123,7 @@ public:
} }
case WID_VL_AVAILABLE_VEHICLES: case WID_VL_AVAILABLE_VEHICLES:
ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype); ShowBuildVehicleWindow(INVALID_DEPOT, this->vli.vtype);
break; break;
case WID_VL_MANAGE_VEHICLES_DROPDOWN: { case WID_VL_MANAGE_VEHICLES_DROPDOWN: {
@ -2267,16 +2268,9 @@ void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationI
ShowVehicleListWindowLocal(company, VL_STATION_LIST, vehicle_type, station); 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; ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, depot_id);
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);
} }
@ -3141,7 +3135,7 @@ public:
case OT_GOTO_DEPOT: { case OT_GOTO_DEPOT: {
SetDParam(0, v->type); 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)); SetDParam(2, PackVelocity(v->GetDisplaySpeed(), v->type));
if (v->current_order.GetDestination() == INVALID_DEPOT) { if (v->current_order.GetDestination() == INVALID_DEPOT) {
/* This case *only* happens when multiple nearest depot orders /* This case *only* happens when multiple nearest depot orders

View File

@ -18,6 +18,7 @@
#include "station_type.h" #include "station_type.h"
#include "engine_type.h" #include "engine_type.h"
#include "company_type.h" #include "company_type.h"
#include "depot_type.h"
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit = false); 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 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 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); uint ShowRefitOptionsList(int left, int right, int y, EngineID engine);
StringID GetCargoSubtypeText(const Vehicle *v); StringID GetCargoSubtypeText(const Vehicle *v);
@ -64,7 +65,7 @@ StringID GetCargoSubtypeText(const Vehicle *v);
void ShowVehicleListWindow(const Vehicle *v); void ShowVehicleListWindow(const Vehicle *v);
void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type); void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type);
void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station); 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. * Get the height of a single vehicle in the GUIs.

View File

@ -13,6 +13,7 @@
#include "vehiclelist.h" #include "vehiclelist.h"
#include "vehiclelist_func.h" #include "vehiclelist_func.h"
#include "group.h" #include "group.h"
#include "depot_base.h"
#include "safeguards.h" #include "safeguards.h"
@ -96,18 +97,23 @@ static Vehicle *BuildDepotVehicleListProc(Vehicle *v, void *data)
/** /**
* Generate a list of vehicles inside a depot. * Generate a list of vehicles inside a depot.
* @param type Type of vehicle * @param type Type of vehicle
* @param tile The tile the depot is located on * @param depot_id The id of the depot
* @param engines Pointer to list to add vehicles to * @param engines Pointer to list to add vehicles to
* @param wagons Pointer to list to add wagons to (can be nullptr) * @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. * @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(); engines->clear();
if (wagons != nullptr && wagons != engines) wagons->clear(); if (wagons != nullptr && wagons != engines) wagons->clear();
BuildDepotVehicleListData bdvld{engines, wagons, type, individual_wagons}; BuildDepotVehicleListData bdvld{engines, wagons, type, individual_wagons};
for (TileIndex tile : dep->depot_tiles) {
FindVehicleOnPos(tile, &bdvld, BuildDepotVehicleListProc); FindVehicleOnPos(tile, &bdvld, BuildDepotVehicleListProc);
}
} }
/** /**

View File

@ -12,6 +12,7 @@
#include "vehicle_type.h" #include "vehicle_type.h"
#include "company_type.h" #include "company_type.h"
#include "depot_type.h"
#include "tile_type.h" #include "tile_type.h"
/** Vehicle List type flags */ /** Vehicle List type flags */
@ -54,7 +55,7 @@ struct VehicleListIdentifier {
typedef std::vector<const Vehicle *> VehicleList; typedef std::vector<const Vehicle *> VehicleList;
bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &identifier); 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); uint GetUnitNumberDigits(VehicleList &vehicles);
#endif /* VEHICLELIST_H */ #endif /* VEHICLELIST_H */

View File

@ -90,6 +90,7 @@
#include "network/network_func.h" #include "network/network_func.h"
#include "framerate_type.h" #include "framerate_type.h"
#include "viewport_cmd.h" #include "viewport_cmd.h"
#include "depot_map.h"
#include <forward_list> #include <forward_list>
#include <stack> #include <stack>
@ -1002,6 +1003,7 @@ enum TileHighlightType {
const Station *_viewport_highlight_station; ///< Currently selected station for coverage area highlight 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 Waypoint *_viewport_highlight_waypoint; ///< Currently selected waypoint for coverage area highlight
const Town *_viewport_highlight_town; ///< Currently selected town 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. * 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) static TileHighlightType GetTileHighlightType(TileIndex t)
{ {
if (_viewport_highlight_depot != INVALID_DEPOT) {
if (IsDepotTile(t) && GetDepotIndex(t) == _viewport_highlight_depot) return THT_BLUE;
}
if (_viewport_highlight_station != nullptr) { if (_viewport_highlight_station != nullptr) {
if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station->index) return THT_WHITE; if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station->index) return THT_WHITE;
if (_viewport_highlight_station->TileIsInCatchment(t)) return THT_BLUE; if (_viewport_highlight_station->TileIsInCatchment(t)) return THT_BLUE;
@ -2674,6 +2680,8 @@ void UpdateTileSelection()
} }
_thd.new_pos.x = x1 & ~TILE_UNIT_MASK; _thd.new_pos.x = x1 & ~TILE_UNIT_MASK;
_thd.new_pos.y = y1 & ~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;
} }
} }
@ -2766,6 +2774,15 @@ void VpSetPlaceSizingLimit(int limit)
_thd.sizelimit = 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 * Highlights all tiles between a set of two tiles. Used in dock and tunnel placement
* @param from TileIndex of the first tile to highlight * @param from TileIndex of the first tile to highlight
@ -3237,7 +3254,7 @@ void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
sx = _thd.selstart.x; sx = _thd.selstart.x;
sy = _thd.selstart.y; sy = _thd.selstart.y;
int limit = 0; int limit = -1;
switch (method) { switch (method) {
case VPM_X_OR_Y: // drag in X or Y direction case VPM_X_OR_Y: // drag in X or Y direction
@ -3250,28 +3267,33 @@ void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
} }
goto calc_heightdiff_single_direction; goto calc_heightdiff_single_direction;
case VPM_LIMITED_Y_FIXED_X:
case VPM_X_LIMITED: // Drag in X direction (limited size). case VPM_X_LIMITED: // Drag in X direction (limited size).
limit = (_thd.sizelimit - 1) * TILE_SIZE; limit = (_thd.sizelimit - 1) * TILE_SIZE;
[[fallthrough]]; [[fallthrough]];
case VPM_FIX_X: // drag in Y direction 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; style = HT_DIR_Y;
goto calc_heightdiff_single_direction; goto calc_heightdiff_single_direction;
case VPM_LIMITED_X_FIXED_Y:
case VPM_Y_LIMITED: // Drag in Y direction (limited size). case VPM_Y_LIMITED: // Drag in Y direction (limited size).
limit = (_thd.sizelimit - 1) * TILE_SIZE; limit = (_thd.sizelimit - 1) * TILE_SIZE;
[[fallthrough]]; [[fallthrough]];
case VPM_FIX_Y: // drag in X direction 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; style = HT_DIR_X;
calc_heightdiff_single_direction:; calc_heightdiff_single_direction:;
if (limit > 0) { if (limit >= 0) {
x = sx + Clamp(x - sx, -limit, limit); if (method != VPM_LIMITED_X_FIXED_Y) y = sy + Clamp(y - sy, -limit, limit);
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) { if (_settings_client.gui.measure_tooltip) {
TileIndex t0 = TileVirtXY(sx, sy); TileIndex t0 = TileVirtXY(sx, sy);
TileIndex t1 = TileVirtXY(x, y); TileIndex t1 = TileVirtXY(x, y);
@ -3301,6 +3323,7 @@ calc_heightdiff_single_direction:;
[[fallthrough]]; [[fallthrough]];
case VPM_X_AND_Y: // drag an X by Y area case VPM_X_AND_Y: // drag an X by Y area
measure_area:
if (_settings_client.gui.measure_tooltip) { if (_settings_client.gui.measure_tooltip) {
static const StringID measure_strings_area[] = { static const StringID measure_strings_area[] = {
STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
@ -3589,6 +3612,9 @@ void MarkCatchmentTilesDirty()
} }
MarkWholeScreenDirty(); MarkWholeScreenDirty();
} }
if (_viewport_highlight_depot != INVALID_DEPOT) {
MarkWholeScreenDirty();
}
} }
static void SetWindowDirtyForViewportCatchment() static void SetWindowDirtyForViewportCatchment()
@ -3596,6 +3622,7 @@ static void SetWindowDirtyForViewportCatchment()
if (_viewport_highlight_station != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station->index); 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_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_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() static void ClearViewportCatchment()
@ -3604,6 +3631,7 @@ static void ClearViewportCatchment()
_viewport_highlight_station = nullptr; _viewport_highlight_station = nullptr;
_viewport_highlight_waypoint = nullptr; _viewport_highlight_waypoint = nullptr;
_viewport_highlight_town = nullptr; _viewport_highlight_town = nullptr;
_viewport_highlight_depot = INVALID_DEPOT;
} }
/** /**
@ -3665,3 +3693,30 @@ void SetViewportCatchmentTown(const Town *t, bool sel)
} }
if (_viewport_highlight_town != nullptr) SetWindowDirty(WC_TOWN_VIEW, _viewport_highlight_town->index); 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);
}

View File

@ -16,6 +16,7 @@
#include "tile_map.h" #include "tile_map.h"
#include "station_type.h" #include "station_type.h"
#include "vehicle_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. 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 SetViewportCatchmentStation(const Station *st, bool sel);
void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel); void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel);
void SetViewportCatchmentTown(const Town *t, bool sel); void SetViewportCatchmentTown(const Town *t, bool sel);
void SetViewportHighlightDepot(const DepotID dep, bool sel);
void MarkCatchmentTilesDirty(); void MarkCatchmentTilesDirty();
template<class T> template<class T>

View File

@ -99,6 +99,8 @@ enum ViewportPlaceMethod {
VPM_FIX_VERTICAL = 6, ///< drag only in vertical direction VPM_FIX_VERTICAL = 6, ///< drag only in vertical direction
VPM_X_LIMITED = 7, ///< Drag only in X axis with limited size 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_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_RAILDIRS = 0x40, ///< all rail directions
VPM_SIGNALDIRS = 0x80, ///< similar to VMP_RAILDIRS, but with different cursor VPM_SIGNALDIRS = 0x80, ///< similar to VMP_RAILDIRS, but with different cursor
}; };
@ -120,6 +122,8 @@ enum ViewportDragDropSelectionProcess {
DDSP_PLANT_TREES, ///< Plant trees DDSP_PLANT_TREES, ///< Plant trees
DDSP_BUILD_BRIDGE, ///< Bridge placement DDSP_BUILD_BRIDGE, ///< Bridge placement
DDSP_BUILD_OBJECT, ///< Build an object DDSP_BUILD_OBJECT, ///< Build an object
DDSP_BUILD_DEPOT, ///< Depot placement
DDSP_REMOVE_DEPOT, ///< Depot removal
/* Rail specific actions */ /* Rail specific actions */
DDSP_PLACE_RAIL, ///< Rail placement DDSP_PLACE_RAIL, ///< Rail placement

View File

@ -94,66 +94,75 @@ static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
/** /**
* Build a ship depot. * Build a ship depot.
* @param flags type of operation * @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 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 * @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; if (!IsValidAxis(axis)) return CMD_ERROR;
TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) { TileArea complete_area(tile, end_tile);
return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); 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)) { /* Create a new depot or find a depot to join to. */
/* Prevent depots on rapids */ Depot *depot = nullptr;
return_cmd_error(STR_ERROR_SITE_UNSUITABLE); CommandCost ret = FindJoiningDepot(complete_area, VEH_SHIP, join_to, depot, adjacent, flags);
}
if (!Depot::CanAllocateItem()) return CMD_ERROR;
WaterClass wc1 = GetWaterClass(tile);
WaterClass wc2 = GetWaterClass(tile2);
CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
bool add_cost = !IsWaterTile(tile);
CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags | DC_AUTO, tile);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
if (add_cost) {
cost.AddCost(ret);
}
add_cost = !IsWaterTile(tile2);
ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags | DC_AUTO, tile2);
if (ret.Failed()) return ret;
if (add_cost) {
cost.AddCost(ret);
}
if (flags & DC_EXEC) { /* Get the cost of building all the ship depots. */
Depot *depot = new Depot(tile); CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP] * northern_tiles.w * northern_tiles.h);
depot->build_date = TimerGameCalendar::date;
uint new_water_infra = 2 * LOCK_DEPOT_TILE_FACTOR;
/* Update infrastructure counts after the tile clears earlier. /* 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. * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
* See: MakeWaterKeepingClass() */ * See: MakeWaterKeepingClass() */
if (wc1 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile) && GetWaterClass(tile) == WATER_CLASS_CANAL && IsTileOwner(tile, _current_company))) new_water_infra++; uint new_water_infra = 0;
if (wc2 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile2) && GetWaterClass(tile2) == WATER_CLASS_CANAL && IsTileOwner(tile2, _current_company))) new_water_infra++;
Company::Get(_current_company)->infrastructure.water += new_water_infra; 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);
/* ... 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);
/* Keep original water class before clearing tile. */
WaterClass wc = GetWaterClass(t);
/* Clear the tile. */
bool add_cost = !IsWaterTile(t);
CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::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); 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); MakeDefaultName(depot);
depot->AfterAddRemove(complete_area, true);
if (join_to == NEW_DEPOT) MakeDefaultName(depot);
} }
return cost; return cost;
@ -275,9 +284,8 @@ static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
bool do_clear = (flags & DC_FORCE_CLEAR_TILE) != 0; bool do_clear = (flags & DC_FORCE_CLEAR_TILE) != 0;
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
delete Depot::GetByTile(tile); Depot *depot = Depot::GetByTile(tile);
Company *c = Company::GetIfValid(depot->owner);
Company *c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) { if (c != nullptr) {
c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR; c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR;
if (do_clear && GetWaterClass(tile) == WATER_CLASS_CANAL) c->infrastructure.water--; 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)); if (!do_clear) MakeWaterKeepingClass(tile, GetTileOwner(tile));
MakeWaterKeepingClass(tile2, GetTileOwner(tile2)); MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
depot->AfterAddRemove(TileArea(tile, tile2), false);
} }
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]); return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
@ -1334,7 +1344,7 @@ static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode,
static bool ClickTile_Water(TileIndex tile) static bool ClickTile_Water(TileIndex tile)
{ {
if (GetWaterTileType(tile) == WATER_TILE_DEPOT) { if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP); ShowDepotWindow(GetDepotIndex(tile));
return true; return true;
} }
return false; return false;

View File

@ -13,7 +13,7 @@
#include "command_type.h" #include "command_type.h"
#include "water_map.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 CmdBuildCanal(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, WaterClass wc, bool diagonal);
CommandCost CmdBuildLock(DoCommandFlag flags, TileIndex tile); CommandCost CmdBuildLock(DoCommandFlag flags, TileIndex tile);

View File

@ -27,9 +27,17 @@ enum DepotWidgets : WidgetID {
WID_D_LOCATION, ///< Location button. WID_D_LOCATION, ///< Location button.
WID_D_SHOW_RENAME, ///< Show rename panel. WID_D_SHOW_RENAME, ///< Show rename panel.
WID_D_RENAME, ///< Rename button. WID_D_RENAME, ///< Rename button.
WID_D_HIGHLIGHT, ///< Highlight button.
WID_D_VEHICLE_LIST, ///< List of vehicles. WID_D_VEHICLE_LIST, ///< List of vehicles.
WID_D_STOP_ALL, ///< Stop all button. WID_D_STOP_ALL, ///< Stop all button.
WID_D_START_ALL, ///< Start all button. 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 */ #endif /* WIDGETS_DEPOT_WIDGET_H */

View File

@ -241,6 +241,12 @@ enum WindowClass {
*/ */
WC_SELECT_STATION, WC_SELECT_STATION,
/**
* Select depot (when joining depots); %Window numbers:
* - #Vehicle type = #JoinDepotWidgets
*/
WC_SELECT_DEPOT,
/** /**
* News window; %Window numbers: * News window; %Window numbers:
* - 0 = #NewsWidgets * - 0 = #NewsWidgets
@ -346,7 +352,7 @@ enum WindowClass {
/** /**
* Depot view; %Window numbers: * Depot view; %Window numbers:
* - #TileIndex = #DepotWidgets * - #DepotID = #DepotWidgets
*/ */
WC_VEHICLE_DEPOT, WC_VEHICLE_DEPOT,
@ -383,8 +389,8 @@ enum WindowClass {
/** /**
* Build vehicle; %Window numbers: * Build vehicle; %Window numbers:
* - #VehicleType = #BuildVehicleWidgets * - #DepotID = #BuildVehicleWidgets, for existing depots
* - #TileIndex = #BuildVehicleWidgets * - #MAX_DEPOTS + VehicleType = #BuildVehicleWidgets, for "Available Trains"...
*/ */
WC_BUILD_VEHICLE, WC_BUILD_VEHICLE,