mirror of https://github.com/OpenTTD/OpenTTD
Merge d82d36da97
into 3176de3f1d
commit
b88e6c0cd8
|
@ -41,6 +41,7 @@
|
|||
#include "framerate_type.h"
|
||||
#include "aircraft_cmd.h"
|
||||
#include "vehicle_cmd.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -136,7 +137,7 @@ static StationID FindNearestHangar(const Aircraft *v)
|
|||
if (v->current_order.IsType(OT_GOTO_STATION) ||
|
||||
(v->current_order.IsType(OT_GOTO_DEPOT) && (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0)) {
|
||||
last_dest = Station::GetIfValid(v->last_station_visited);
|
||||
next_dest = Station::GetIfValid(v->current_order.GetDestination());
|
||||
next_dest = Station::GetIfValid(GetTargetDestination(v->current_order, true));
|
||||
} else {
|
||||
last_dest = GetTargetAirportIfValid(v);
|
||||
next_dest = Station::GetIfValid(v->GetNextStoppingStation().value);
|
||||
|
@ -407,9 +408,10 @@ ClosestDepot Aircraft::FindClosestDepot()
|
|||
if (station == INVALID_STATION) return ClosestDepot();
|
||||
|
||||
st = Station::Get(station);
|
||||
assert(st->airport.hangar != nullptr);
|
||||
}
|
||||
|
||||
return ClosestDepot(st->xy, st->index);
|
||||
return ClosestDepot(st->xy, st->airport.hangar->index);
|
||||
}
|
||||
|
||||
static void CheckIfAircraftNeedsService(Aircraft *v)
|
||||
|
@ -424,13 +426,13 @@ static void CheckIfAircraftNeedsService(Aircraft *v)
|
|||
* we don't want to consider going to a depot too. */
|
||||
if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
|
||||
|
||||
const Station *st = Station::Get(v->current_order.GetDestination());
|
||||
const Station *st = Station::Get(GetTargetDestination(v->current_order, true));
|
||||
|
||||
assert(st != nullptr);
|
||||
|
||||
/* only goto depot if the target airport has a depot */
|
||||
if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
|
||||
v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
|
||||
v->current_order.MakeGoToDepot(st->airport.hangar->index, ODTFB_SERVICE);
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
|
||||
} else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
|
||||
v->current_order.MakeDummy();
|
||||
|
@ -892,7 +894,7 @@ static bool AircraftController(Aircraft *v)
|
|||
/* Jump into our "holding pattern" state machine if possible */
|
||||
if (v->pos >= afc->nofelements) {
|
||||
v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
|
||||
} else if (v->targetairport != v->current_order.GetDestination()) {
|
||||
} else if (v->targetairport != GetTargetDestination(v->current_order, true)) {
|
||||
/* If not possible, just get out of here fast */
|
||||
v->state = FLYING;
|
||||
UpdateAircraftCache(v);
|
||||
|
@ -1449,7 +1451,7 @@ static void AircraftLandAirplane(Aircraft *v)
|
|||
void AircraftNextAirportPos_and_Order(Aircraft *v)
|
||||
{
|
||||
if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
|
||||
v->targetairport = v->current_order.GetDestination();
|
||||
v->targetairport = GetTargetDestination(v->current_order, true);
|
||||
}
|
||||
|
||||
const Station *st = GetTargetAirportIfValid(v);
|
||||
|
@ -1488,7 +1490,9 @@ void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
|
|||
VehicleServiceInDepot(v);
|
||||
v->LeaveUnbunchingDepot();
|
||||
SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
|
||||
/* When called from UpdateOldAircraft(), tile may not be a hangar. */
|
||||
if (IsHangarTile(v->tile)) InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
SetWindowClassesDirty(WC_AIRCRAFT_LIST);
|
||||
}
|
||||
|
||||
|
@ -1539,7 +1543,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap
|
|||
return;
|
||||
|
||||
/* We are leaving a hangar, but have to go to the exact same one; re-enter */
|
||||
if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
|
||||
if (v->current_order.IsType(OT_GOTO_DEPOT) && GetTargetDestination(v->current_order, true) == v->targetairport) {
|
||||
VehicleEnterDepot(v);
|
||||
return;
|
||||
}
|
||||
|
@ -1548,7 +1552,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap
|
|||
if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
|
||||
|
||||
/* We are already at the target airport, we need to find a terminal */
|
||||
if (v->current_order.GetDestination() == v->targetairport) {
|
||||
if (GetTargetDestination(v->current_order, true) == v->targetairport) {
|
||||
/* FindFreeTerminal:
|
||||
* 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
|
||||
if (v->subtype == AIR_HELICOPTER) {
|
||||
|
@ -1599,7 +1603,7 @@ static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *
|
|||
case OT_GOTO_STATION: // ready to fly to another airport
|
||||
break;
|
||||
case OT_GOTO_DEPOT: // visit hangar for servicing, sale, etc.
|
||||
go_to_hangar = v->current_order.GetDestination() == v->targetairport;
|
||||
go_to_hangar = GetTargetDestination(v->current_order, true) == v->targetairport;
|
||||
break;
|
||||
case OT_CONDITIONAL:
|
||||
/* In case of a conditional order we just have to wait a tick
|
||||
|
@ -2103,7 +2107,7 @@ static bool AircraftEventHandler(Aircraft *v, int loop)
|
|||
/* Check the distance to the next destination. This code works because the target
|
||||
* airport is only updated after take off and not on the ground. */
|
||||
Station *cur_st = Station::GetIfValid(v->targetairport);
|
||||
Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : nullptr;
|
||||
Station *next_st = Station::GetIfValid(GetTargetDestination(v->current_order, true));
|
||||
|
||||
if (cur_st != nullptr && cur_st->airport.tile != INVALID_TILE && next_st != nullptr && next_st->airport.tile != INVALID_TILE) {
|
||||
uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
|
||||
|
@ -2169,18 +2173,7 @@ void UpdateAirplanesOnNewStation(const Station *st)
|
|||
if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
|
||||
assert(v->state == FLYING);
|
||||
|
||||
Order *o = &v->current_order;
|
||||
/* The aircraft is heading to a hangar, but the new station doesn't have one,
|
||||
* or the aircraft can't land on the new station. Cancel current order. */
|
||||
if (o->IsType(OT_GOTO_DEPOT) && !(o->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) && o->GetDestination() == st->index &&
|
||||
(!st->airport.HasHangar() || !CanVehicleUseStation(v, st))) {
|
||||
o->MakeDummy();
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
|
||||
}
|
||||
v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
|
||||
UpdateAircraftCache(v);
|
||||
}
|
||||
|
||||
/* Heliports don't have a hangar. Invalidate all go to hangar orders from all aircraft. */
|
||||
if (!st->airport.HasHangar()) RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, st->index, true);
|
||||
}
|
||||
|
|
|
@ -71,7 +71,10 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
|
|||
switch (type) {
|
||||
case VEH_TRAIN: {
|
||||
/* make sure the railtypes are compatible */
|
||||
if ((GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) return false;
|
||||
if (!_settings_game.depot.allow_no_comp_railtype_replacements &&
|
||||
(GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* make sure we do not replace wagons with engines or vice versa */
|
||||
if ((e_from->u.rail.railveh_type == RAILVEH_WAGON) != (e_to->u.rail.railveh_type == RAILVEH_WAGON)) return false;
|
||||
|
@ -79,11 +82,15 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
|
|||
}
|
||||
|
||||
case VEH_ROAD:
|
||||
/* make sure the roadtypes are compatible */
|
||||
if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) return false;
|
||||
if (!_settings_game.depot.allow_no_comp_roadtype_replacements) {
|
||||
/* make sure the roadtypes are compatible */
|
||||
if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* make sure that we do not replace a tram with a normal road vehicles or vice versa */
|
||||
if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false;
|
||||
/* make sure that we do not replace a tram with a normal road vehicles or vice versa */
|
||||
if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case VEH_AIRCRAFT:
|
||||
|
@ -519,6 +526,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
|
|||
{
|
||||
Vehicle *old_head = *chain;
|
||||
assert(old_head->IsPrimaryVehicle());
|
||||
TileIndex tile = old_head->tile;
|
||||
|
||||
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0);
|
||||
|
||||
|
@ -661,6 +669,9 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
|
|||
if ((flags & DC_EXEC) != 0) CheckCargoCapacity(new_head);
|
||||
}
|
||||
|
||||
assert(IsValidTile(tile));
|
||||
if (!HasCompatibleDepotTile(tile, Train::From(new_head))) cost.MakeError(STR_ERROR_UNABLE_TO_FIND_APPROPRIATE_DEPOT_TILE);
|
||||
|
||||
/* If we are not in DC_EXEC undo everything, i.e. rearrange old vehicles.
|
||||
* We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
|
||||
* Note: The vehicle attach callback is disabled here :) */
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "querystring_gui.h"
|
||||
#include "stringfilter_type.h"
|
||||
#include "hotkeys.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "widgets/build_vehicle_widget.h"
|
||||
|
||||
|
@ -1163,12 +1164,31 @@ enum BuildVehicleHotkeys {
|
|||
BVHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a unique window number for the BuildVehicleWindow.
|
||||
* The BuildVehicleWindow can be opened for a valid depot or
|
||||
* for a specific vehicle type ("Available Trains", "Available Ships"...).
|
||||
* The corresponding unique window number is chosen as:
|
||||
* - For existing depots, the depot id.
|
||||
* - For vehicle types, it is MAX_DEPOTS + vehicle_type
|
||||
* @param depot_id the depot id
|
||||
* @param type the vehicle type
|
||||
* @return the depot id for valid depots or MAX_DEPOTS + vehicle_type otherwise.
|
||||
*/
|
||||
DepotID GetBuildVehicleWindowNumber(DepotID depot_id, VehicleType type)
|
||||
{
|
||||
assert(depot_id == INVALID_DEPOT || Depot::IsValidID(depot_id));
|
||||
assert(IsCompanyBuildableVehicleType(type));
|
||||
if (depot_id != INVALID_DEPOT) return depot_id;
|
||||
return MAX_DEPOTS + type;
|
||||
}
|
||||
|
||||
/** GUI for building vehicles. */
|
||||
struct BuildVehicleWindow : Window {
|
||||
VehicleType vehicle_type; ///< Type of vehicles shown in the window.
|
||||
union {
|
||||
RailType railtype; ///< Rail type to show, or #INVALID_RAILTYPE.
|
||||
RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE.
|
||||
RailTypes railtypes; ///< Rail types to show, or #INVALID_RAILTYPES.
|
||||
RoadTypes roadtypes; ///< Road types to show, or #INVALID_ROADTYPES.
|
||||
} filter; ///< Filter to apply.
|
||||
bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction
|
||||
uint8_t sort_criteria; ///< Current sort criterium.
|
||||
|
@ -1201,11 +1221,11 @@ struct BuildVehicleWindow : Window {
|
|||
}
|
||||
}
|
||||
|
||||
BuildVehicleWindow(WindowDesc &desc, TileIndex tile, VehicleType type) : Window(desc), vehicle_editbox(MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS)
|
||||
BuildVehicleWindow(WindowDesc &desc, DepotID depot_id, VehicleType type) : Window(desc), vehicle_editbox(MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS)
|
||||
{
|
||||
this->vehicle_type = type;
|
||||
this->listview_mode = tile == INVALID_TILE;
|
||||
this->window_number = this->listview_mode ? (int)type : tile.base();
|
||||
this->listview_mode = depot_id == INVALID_DEPOT;
|
||||
this->window_number = GetBuildVehicleWindowNumber(depot_id, type);
|
||||
|
||||
this->sel_engine = INVALID_ENGINE;
|
||||
|
||||
|
@ -1240,16 +1260,13 @@ struct BuildVehicleWindow : Window {
|
|||
|
||||
this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9);
|
||||
|
||||
if (tile == INVALID_TILE) {
|
||||
this->FinishInitNested(type);
|
||||
} else {
|
||||
this->FinishInitNested(tile);
|
||||
}
|
||||
this->FinishInitNested(this->window_number);
|
||||
|
||||
this->querystrings[WID_BV_FILTER] = &this->vehicle_editbox;
|
||||
this->vehicle_editbox.cancel_button = QueryString::ACTION_CLEAR;
|
||||
|
||||
this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
|
||||
Depot *depot = Depot::GetIfValid(depot_id);
|
||||
this->owner = depot != nullptr ? depot->owner : _local_company;
|
||||
|
||||
this->eng_list.ForceRebuild();
|
||||
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 */
|
||||
void UpdateFilterByTile()
|
||||
{
|
||||
Depot *depot = this->listview_mode ? nullptr : Depot::Get(this->window_number);
|
||||
|
||||
switch (this->vehicle_type) {
|
||||
default: NOT_REACHED();
|
||||
case VEH_TRAIN:
|
||||
if (this->listview_mode) {
|
||||
this->filter.railtype = INVALID_RAILTYPE;
|
||||
} else {
|
||||
this->filter.railtype = GetRailType(this->window_number);
|
||||
}
|
||||
this->filter.railtypes = this->listview_mode ? INVALID_RAILTYPES : depot->r_types.rail_types;
|
||||
break;
|
||||
|
||||
case VEH_ROAD:
|
||||
if (this->listview_mode) {
|
||||
this->filter.roadtype = INVALID_ROADTYPE;
|
||||
} else {
|
||||
this->filter.roadtype = GetRoadTypeRoad(this->window_number);
|
||||
if (this->filter.roadtype == INVALID_ROADTYPE) {
|
||||
this->filter.roadtype = GetRoadTypeTram(this->window_number);
|
||||
}
|
||||
}
|
||||
this->filter.roadtypes = this->listview_mode ? INVALID_ROADTYPES : depot->r_types.road_types;
|
||||
break;
|
||||
|
||||
case VEH_SHIP:
|
||||
|
@ -1326,7 +1334,7 @@ struct BuildVehicleWindow : Window {
|
|||
|
||||
if (!this->listview_mode) {
|
||||
/* Query for cost and refitted capacity */
|
||||
auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command<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()) {
|
||||
this->te.cost = ret.GetCost() - e->GetCost();
|
||||
this->te.capacity = refit_capacity;
|
||||
|
@ -1401,7 +1409,7 @@ struct BuildVehicleWindow : Window {
|
|||
EngineID eid = e->index;
|
||||
const RailVehicleInfo *rvi = &e->u.rail;
|
||||
|
||||
if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
|
||||
if (!this->listview_mode && !HasPowerOnRails(rvi->railtype, this->filter.railtypes)) continue;
|
||||
if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
|
||||
|
||||
/* Filter now! So num_engines and num_wagons is valid */
|
||||
|
@ -1461,7 +1469,7 @@ struct BuildVehicleWindow : Window {
|
|||
if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
|
||||
EngineID eid = e->index;
|
||||
if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
|
||||
if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue;
|
||||
if (!this->listview_mode && !HasPowerOnRoads(e->u.road.roadtype, this->filter.roadtypes)) continue;
|
||||
|
||||
/* Filter by name or NewGRF extra text */
|
||||
if (!FilterByText(e)) continue;
|
||||
|
@ -1501,7 +1509,7 @@ struct BuildVehicleWindow : Window {
|
|||
|
||||
this->eng_list.clear();
|
||||
|
||||
const Station *st = this->listview_mode ? nullptr : Station::GetByTile(this->window_number);
|
||||
const Station *st = this->listview_mode ? nullptr : Depot::Get(this->window_number)->station;
|
||||
|
||||
/* Make list of all available planes.
|
||||
* Also check to see if the previously selected plane is still available,
|
||||
|
@ -1613,10 +1621,15 @@ struct BuildVehicleWindow : Window {
|
|||
|
||||
CargoID cargo = this->cargo_filter_criteria;
|
||||
if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO;
|
||||
|
||||
assert(Depot::IsValidID(this->window_number));
|
||||
Depot *depot = Depot::Get(this->window_number);
|
||||
assert(depot->xy != INVALID_TILE);
|
||||
|
||||
if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) {
|
||||
Command<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 {
|
||||
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. */
|
||||
|
@ -1736,11 +1749,21 @@ struct BuildVehicleWindow : Window {
|
|||
switch (widget) {
|
||||
case WID_BV_CAPTION:
|
||||
if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
|
||||
const RailTypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
|
||||
SetDParam(0, rti->strings.build_caption);
|
||||
uint num_railtypes = CountBits(this->filter.railtypes);
|
||||
if (num_railtypes != 1) {
|
||||
SetDParam(0, STR_BUY_VEHICLE_TRAIN_ALL_CAPTION);
|
||||
} else {
|
||||
const RailTypeInfo *rti = GetRailTypeInfo((RailType)FindFirstBit(this->filter.railtypes));
|
||||
SetDParam(0, rti->strings.build_caption);
|
||||
}
|
||||
} else if (this->vehicle_type == VEH_ROAD && !this->listview_mode) {
|
||||
const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype);
|
||||
SetDParam(0, rti->strings.build_caption);
|
||||
uint num_roadtypes = CountBits(this->filter.roadtypes);
|
||||
if (num_roadtypes != 1) {
|
||||
SetDParam(0, STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION);
|
||||
} else {
|
||||
const RoadTypeInfo *rti = GetRoadTypeInfo((RoadType)FindFirstBit(this->filter.roadtypes));
|
||||
SetDParam(0, rti->strings.build_caption);
|
||||
}
|
||||
} else {
|
||||
SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
|
||||
}
|
||||
|
@ -1930,17 +1953,11 @@ static WindowDesc _build_vehicle_desc(
|
|||
&BuildVehicleWindow::hotkeys
|
||||
);
|
||||
|
||||
void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
|
||||
void ShowBuildVehicleWindow(DepotID depot_id, VehicleType type)
|
||||
{
|
||||
/* We want to be able to open both Available Train as Available Ships,
|
||||
* so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
|
||||
* As it always is a low value, it won't collide with any real tile
|
||||
* number. */
|
||||
uint num = (tile == INVALID_TILE) ? (int)type : tile.base();
|
||||
|
||||
assert(IsCompanyBuildableVehicleType(type));
|
||||
|
||||
CloseWindowById(WC_BUILD_VEHICLE, num);
|
||||
CloseWindowById(WC_BUILD_VEHICLE, GetBuildVehicleWindowNumber(depot_id, type));
|
||||
|
||||
new BuildVehicleWindow(_build_vehicle_desc, tile, type);
|
||||
new BuildVehicleWindow(_build_vehicle_desc, depot_id, type);
|
||||
}
|
||||
|
|
|
@ -193,6 +193,7 @@ enum Commands : uint16_t {
|
|||
CMD_BUILD_BRIDGE, ///< build a bridge
|
||||
CMD_BUILD_RAIL_STATION, ///< build a rail station
|
||||
CMD_BUILD_TRAIN_DEPOT, ///< build a train depot
|
||||
CMD_REMOVE_TRAIN_DEPOT, ///< remove a train depot
|
||||
CMD_BUILD_SINGLE_SIGNAL, ///< build a signal
|
||||
CMD_REMOVE_SINGLE_SIGNAL, ///< remove a signal
|
||||
CMD_TERRAFORM_LAND, ///< terraform a tile
|
||||
|
|
141
src/depot.cpp
141
src/depot.cpp
|
@ -15,9 +15,13 @@
|
|||
#include "core/pool_func.hpp"
|
||||
#include "vehicle_gui.h"
|
||||
#include "vehiclelist.h"
|
||||
#include "command_func.h"
|
||||
#include "vehicle_base.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
/** All our depots tucked away in a pool. */
|
||||
DepotPool _depot_pool("Depot");
|
||||
INSTANTIATE_POOL_METHODS(Depot)
|
||||
|
@ -29,21 +33,146 @@ Depot::~Depot()
|
|||
{
|
||||
if (CleaningPool()) return;
|
||||
|
||||
if (!IsDepotTile(this->xy) || GetDepotIndex(this->xy) != this->index) {
|
||||
/* It can happen there is no depot here anymore (TTO/TTD savegames) */
|
||||
if (this->owner == INVALID_OWNER) {
|
||||
/* Deleting depot remnants of TTD savegames while saveload conversion. */
|
||||
assert(this->veh_type == VEH_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->index);
|
||||
|
||||
/* Delete the depot-window */
|
||||
CloseWindowById(WC_VEHICLE_DEPOT, this->xy);
|
||||
CloseWindowById(WC_VEHICLE_DEPOT, this->index);
|
||||
|
||||
/* Delete the depot list */
|
||||
VehicleType vt = GetDepotVehicleType(this->xy);
|
||||
CloseWindowById(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_DEPOT_LIST, vt, GetTileOwner(this->xy), this->index).Pack());
|
||||
CloseWindowById(GetWindowClassForVehicleType(this->veh_type),
|
||||
VehicleListIdentifier(VL_DEPOT_LIST,
|
||||
this->veh_type, this->owner, this->index).Pack());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,23 @@
|
|||
#include "depot_map.h"
|
||||
#include "core/pool_type.hpp"
|
||||
#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;
|
||||
|
||||
class CommandCost;
|
||||
struct Vehicle;
|
||||
|
||||
struct Depot : DepotPool::PoolItem<&_depot_pool> {
|
||||
/* DepotID index member of DepotPool is 2 bytes. */
|
||||
uint16_t town_cn; ///< The N-1th depot for this town (consecutive number)
|
||||
|
@ -25,14 +38,35 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> {
|
|||
std::string name;
|
||||
TimerGameCalendar::Date build_date; ///< Date of construction
|
||||
|
||||
Depot(TileIndex xy = INVALID_TILE) : xy(xy) {}
|
||||
VehicleType veh_type; ///< Vehicle type of the depot.
|
||||
Owner owner; ///< Owner of the depot.
|
||||
Station *station; ///< For aircraft, station associated with this hangar.
|
||||
|
||||
union {
|
||||
RoadTypes road_types;
|
||||
RailTypes rail_types;
|
||||
} r_types;
|
||||
|
||||
TileArea ta;
|
||||
std::vector<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();
|
||||
|
||||
static inline Depot *GetByTile(TileIndex tile)
|
||||
{
|
||||
assert(Depot::IsValidID(GetDepotIndex(tile)));
|
||||
return Depot::Get(GetDepotIndex(tile));
|
||||
}
|
||||
|
||||
TileIndex GetBestDepotTile(Vehicle *v) const;
|
||||
|
||||
/**
|
||||
* Is the "type" of depot the same as the given depot,
|
||||
* i.e. are both a rail, road or ship depots?
|
||||
|
@ -43,6 +77,15 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> {
|
|||
{
|
||||
return GetTileType(d->xy) == GetTileType(this->xy);
|
||||
}
|
||||
|
||||
/* Check we can add some tiles to this depot. */
|
||||
CommandCost BeforeAddTiles(TileArea ta);
|
||||
|
||||
/* Add some tiles to this depot and rescan area for depot_tiles. */
|
||||
void AfterAddRemove(TileArea ta, bool adding);
|
||||
|
||||
/* Rescan depot_tiles. Done after AfterAddRemove and SaveLoad. */
|
||||
void RescanDepotTiles();
|
||||
};
|
||||
|
||||
#endif /* DEPOT_BASE_H */
|
||||
|
|
|
@ -48,7 +48,7 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str
|
|||
Depot *d = Depot::GetIfValid(depot_id);
|
||||
if (d == nullptr) return CMD_ERROR;
|
||||
|
||||
CommandCost ret = CheckTileOwnership(d->xy);
|
||||
CommandCost ret = CheckOwnership(d->owner);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
bool reset = text.empty();
|
||||
|
@ -68,11 +68,76 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str
|
|||
|
||||
/* Update the orders and depot */
|
||||
SetWindowClassesDirty(WC_VEHICLE_ORDERS);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, d->xy);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, d->index);
|
||||
|
||||
/* Update the depot list */
|
||||
VehicleType vt = GetDepotVehicleType(d->xy);
|
||||
SetWindowDirty(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_DEPOT_LIST, vt, GetTileOwner(d->xy), d->index).Pack());
|
||||
SetWindowDirty(GetWindowClassForVehicleType(d->veh_type), VehicleListIdentifier(VL_DEPOT_LIST, d->veh_type, d->owner, d->index).Pack());
|
||||
}
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
#ifndef DEPOT_FUNC_H
|
||||
#define DEPOT_FUNC_H
|
||||
|
||||
#include "depot_type.h"
|
||||
#include "vehicle_type.h"
|
||||
#include "slope_func.h"
|
||||
#include "command_type.h"
|
||||
|
||||
void ShowDepotWindow(TileIndex tile, VehicleType type);
|
||||
void ShowDepotWindow(DepotID depot_id);
|
||||
void InitDepotWindowBlockSizes();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
|
|
@ -29,9 +29,11 @@
|
|||
#include "zoom_func.h"
|
||||
#include "error.h"
|
||||
#include "depot_cmd.h"
|
||||
#include "station_base.h"
|
||||
#include "train_cmd.h"
|
||||
#include "vehicle_cmd.h"
|
||||
#include "core/geometry_func.hpp"
|
||||
#include "depot_func.h"
|
||||
|
||||
#include "widgets/depot_widget.h"
|
||||
|
||||
|
@ -78,6 +80,7 @@ static constexpr NWidgetPart _nested_train_depot_widgets[] = {
|
|||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_D_BUILD), SetDataTip(0x0, STR_NULL), SetFill(1, 1), SetResize(1, 0),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_D_CLONE), SetDataTip(0x0, STR_NULL), SetFill(1, 1), SetResize(1, 0),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_D_HIGHLIGHT), SetDataTip(STR_BUTTON_HIGHLIGHT_DEPOT, STR_TOOLTIP_HIGHLIGHT_DEPOT), SetFill(1, 1), SetResize(1, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_D_VEHICLE_LIST), SetDataTip(0x0, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetFill(0, 1),
|
||||
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_STOP_ALL), SetDataTip(SPR_FLAG_VEH_STOPPED, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
|
||||
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_START_ALL), SetDataTip(SPR_FLAG_VEH_RUNNING, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
|
||||
|
@ -266,15 +269,17 @@ struct DepotWindow : Window {
|
|||
Scrollbar *hscroll; ///< Only for trains.
|
||||
Scrollbar *vscroll;
|
||||
|
||||
DepotWindow(WindowDesc &desc, TileIndex tile, VehicleType type) : Window(desc)
|
||||
DepotWindow(WindowDesc &desc, DepotID depot_id) : Window(desc)
|
||||
{
|
||||
assert(IsCompanyBuildableVehicleType(type)); // ensure that we make the call with a valid type
|
||||
assert(Depot::IsValidID(depot_id));
|
||||
Depot *depot = Depot::Get(depot_id);
|
||||
assert(IsCompanyBuildableVehicleType(depot->veh_type));
|
||||
|
||||
this->sel = INVALID_VEHICLE;
|
||||
this->vehicle_over = INVALID_VEHICLE;
|
||||
this->generate_list = true;
|
||||
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->unitnumber_digits = 2;
|
||||
|
||||
|
@ -282,23 +287,24 @@ struct DepotWindow : Window {
|
|||
this->hscroll = (this->type == VEH_TRAIN ? this->GetScrollbar(WID_D_H_SCROLL) : nullptr);
|
||||
this->vscroll = this->GetScrollbar(WID_D_V_SCROLL);
|
||||
/* Don't show 'rename button' of aircraft hangar */
|
||||
this->GetWidget<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 */
|
||||
if (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_SELL_CHAIN)->SetDisplayedPlane(type == VEH_TRAIN ? 0 : SZSP_NONE);
|
||||
this->SetupWidgetData(type);
|
||||
this->FinishInitNested(tile);
|
||||
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(this->type == VEH_TRAIN ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_D_SHOW_SELL_CHAIN)->SetDisplayedPlane(this->type == VEH_TRAIN ? 0 : SZSP_NONE);
|
||||
this->SetupWidgetData(this->type);
|
||||
this->FinishInitNested(depot_id);
|
||||
|
||||
this->owner = GetTileOwner(tile);
|
||||
this->owner = depot->owner;
|
||||
OrderBackup::Reset();
|
||||
}
|
||||
|
||||
void Close([[maybe_unused]] int data = 0) override
|
||||
{
|
||||
CloseWindowById(WC_BUILD_VEHICLE, this->window_number);
|
||||
CloseWindowById(GetWindowClassForVehicleType(this->type), VehicleListIdentifier(VL_DEPOT_LIST, this->type, this->owner, this->GetDepotIndex()).Pack(), false);
|
||||
CloseWindowById(GetWindowClassForVehicleType(this->type), VehicleListIdentifier(VL_DEPOT_LIST, this->type, this->owner, this->window_number).Pack(), false);
|
||||
OrderBackup::Reset(this->window_number);
|
||||
SetViewportHighlightDepot(this->window_number, false);
|
||||
this->Window::Close();
|
||||
}
|
||||
|
||||
|
@ -426,7 +432,7 @@ struct DepotWindow : Window {
|
|||
if (widget != WID_D_CAPTION) return;
|
||||
|
||||
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 {
|
||||
|
@ -708,6 +714,9 @@ struct DepotWindow : Window {
|
|||
|
||||
void OnPaint() override
|
||||
{
|
||||
extern DepotID _viewport_highlight_depot;
|
||||
this->SetWidgetLoweredState(WID_D_HIGHLIGHT, _viewport_highlight_depot == this->window_number);
|
||||
|
||||
if (this->generate_list) {
|
||||
/* Generate the vehicle list
|
||||
* It's ok to use the wagon pointers for non-trains as they will be ignored */
|
||||
|
@ -742,8 +751,7 @@ struct DepotWindow : Window {
|
|||
}
|
||||
|
||||
/* Setup disabled buttons. */
|
||||
TileIndex tile = this->window_number;
|
||||
this->SetWidgetsDisabledState(!IsTileOwner(tile, _local_company),
|
||||
this->SetWidgetsDisabledState(this->owner != _local_company,
|
||||
WID_D_STOP_ALL,
|
||||
WID_D_START_ALL,
|
||||
WID_D_SELL,
|
||||
|
@ -759,6 +767,8 @@ struct DepotWindow : Window {
|
|||
|
||||
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
|
||||
{
|
||||
TileIndex tile = Depot::Get(this->window_number)->xy;
|
||||
|
||||
switch (widget) {
|
||||
case WID_D_MATRIX: // List
|
||||
this->DepotClick(pt.x, pt.y);
|
||||
|
@ -789,20 +799,25 @@ struct DepotWindow : Window {
|
|||
if (_ctrl_pressed) {
|
||||
ShowExtraViewportWindow(this->window_number);
|
||||
} else {
|
||||
ScrollMainWindowToTile(this->window_number);
|
||||
ScrollMainWindowToTile(tile);
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_D_RENAME: // Rename button
|
||||
SetDParam(0, this->type);
|
||||
SetDParam(1, Depot::GetByTile((TileIndex)this->window_number)->index);
|
||||
SetDParam(1, this->window_number);
|
||||
ShowQueryString(STR_DEPOT_NAME, STR_DEPOT_RENAME_DEPOT_CAPTION, MAX_LENGTH_DEPOT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
|
||||
break;
|
||||
|
||||
case WID_D_HIGHLIGHT:
|
||||
this->SetWidgetDirty(WID_D_HIGHLIGHT);
|
||||
SetViewportHighlightDepot(this->window_number, !this->IsWidgetLowered(WID_D_HIGHLIGHT));
|
||||
break;
|
||||
|
||||
case WID_D_STOP_ALL:
|
||||
case WID_D_START_ALL: {
|
||||
VehicleListIdentifier vli(VL_DEPOT_LIST, this->type, this->owner);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -810,7 +825,7 @@ struct DepotWindow : Window {
|
|||
/* Only open the confirmation window if there are anything to sell */
|
||||
if (!this->vehicle_list.empty() || !this->wagon_list.empty()) {
|
||||
SetDParam(0, this->type);
|
||||
SetDParam(1, this->GetDepotIndex());
|
||||
SetDParam(1, this->window_number);
|
||||
ShowQuery(
|
||||
STR_DEPOT_CAPTION,
|
||||
STR_DEPOT_SELL_CONFIRMATION_TEXT,
|
||||
|
@ -821,11 +836,11 @@ struct DepotWindow : Window {
|
|||
break;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
@ -836,7 +851,7 @@ struct DepotWindow : Window {
|
|||
if (!str.has_value()) return;
|
||||
|
||||
/* 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
|
||||
|
@ -902,10 +917,10 @@ struct DepotWindow : Window {
|
|||
{
|
||||
if (_ctrl_pressed) {
|
||||
/* 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 {
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
|
@ -1111,43 +1126,33 @@ struct DepotWindow : Window {
|
|||
|
||||
return ES_NOT_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DepotID of the current window.
|
||||
* In the case of airports, this is the station ID.
|
||||
* @return Depot or station ID of this window.
|
||||
*/
|
||||
inline uint16_t GetDepotIndex() const
|
||||
{
|
||||
return (this->type == VEH_AIRCRAFT) ? ::GetStationIndex(this->window_number) : ::GetDepotIndex(this->window_number);
|
||||
}
|
||||
};
|
||||
|
||||
static void DepotSellAllConfirmationCallback(Window *win, bool confirmed)
|
||||
{
|
||||
if (confirmed) {
|
||||
DepotWindow *w = (DepotWindow*)win;
|
||||
TileIndex tile = w->window_number;
|
||||
VehicleType vehtype = w->type;
|
||||
Command<CMD_DEPOT_SELL_ALL_VEHICLES>::Post(tile, vehtype);
|
||||
assert(Depot::IsValidID(win->window_number));
|
||||
Depot *d = Depot::Get(win->window_number);
|
||||
Command<CMD_DEPOT_SELL_ALL_VEHICLES>::Post(d->xy, d->veh_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a depot window
|
||||
* @param tile The tile where the depot/hangar is located
|
||||
* @param type The type of vehicles in the depot
|
||||
* Opens a depot window.
|
||||
* @param depot_id Index of the depot.
|
||||
*/
|
||||
void ShowDepotWindow(TileIndex tile, VehicleType type)
|
||||
void ShowDepotWindow(DepotID depot_id)
|
||||
{
|
||||
if (BringWindowToFrontById(WC_VEHICLE_DEPOT, tile) != nullptr) return;
|
||||
assert(Depot::IsValidID(depot_id));
|
||||
if (BringWindowToFrontById(WC_VEHICLE_DEPOT, depot_id) != nullptr) return;
|
||||
|
||||
switch (type) {
|
||||
Depot *d = Depot::Get(depot_id);
|
||||
switch (d->veh_type) {
|
||||
default: NOT_REACHED();
|
||||
case VEH_TRAIN: new DepotWindow(_train_depot_desc, tile, type); break;
|
||||
case VEH_ROAD: new DepotWindow(_road_depot_desc, tile, type); break;
|
||||
case VEH_SHIP: new DepotWindow(_ship_depot_desc, tile, type); break;
|
||||
case VEH_AIRCRAFT: new DepotWindow(_aircraft_depot_desc, tile, type); break;
|
||||
case VEH_TRAIN: new DepotWindow(_train_depot_desc, depot_id); break;
|
||||
case VEH_ROAD: new DepotWindow(_road_depot_desc, depot_id); break;
|
||||
case VEH_SHIP: new DepotWindow(_ship_depot_desc, depot_id); break;
|
||||
case VEH_AIRCRAFT: new DepotWindow(_aircraft_depot_desc, depot_id); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1164,8 +1169,347 @@ void DeleteDepotHighlightOfVehicle(const Vehicle *v)
|
|||
*/
|
||||
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->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,16 +43,21 @@ inline bool IsDepotTile(Tile tile)
|
|||
return IsRailDepotTile(tile) || IsRoadDepotTile(tile) || IsShipDepotTile(tile) || IsHangarTile(tile);
|
||||
}
|
||||
|
||||
extern DepotID GetHangarIndex(TileIndex t);
|
||||
|
||||
/**
|
||||
* Get the index of which depot is attached to the tile.
|
||||
* @param t the tile
|
||||
* @pre IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t)
|
||||
* @pre IsDepotTile(t)
|
||||
* @return DepotID
|
||||
*/
|
||||
inline DepotID GetDepotIndex(Tile t)
|
||||
{
|
||||
/* Hangars don't have a Depot class, thus store no DepotID. */
|
||||
assert(IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t));
|
||||
assert(IsDepotTile(t));
|
||||
|
||||
/* Hangars don't store depot id on m2. */
|
||||
if (IsTileType(t, MP_STATION)) return GetHangarIndex(t);
|
||||
|
||||
return t.m2();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,10 @@ typedef uint16_t DepotID; ///< Type for the unique identifier of depots.
|
|||
struct Depot;
|
||||
|
||||
static const DepotID INVALID_DEPOT = UINT16_MAX;
|
||||
static const DepotID NEW_DEPOT = INVALID_DEPOT - 1;
|
||||
|
||||
static const uint MAX_LENGTH_DEPOT_NAME_CHARS = 32; ///< The maximum length of a depot name in characters including '\0'
|
||||
|
||||
static const uint DEF_MAX_DEPOT_SPREAD = 12;
|
||||
|
||||
#endif /* DEPOT_TYPE_H */
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "waypoint_cmd.h"
|
||||
#include "timer/timer.h"
|
||||
#include "timer/timer_game_calendar.h"
|
||||
#include "depot_func.h"
|
||||
|
||||
#include "widgets/dock_widget.h"
|
||||
|
||||
|
@ -112,6 +113,8 @@ struct BuildDocksToolbarWindow : Window {
|
|||
{
|
||||
if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_DT_STATION)) SetViewportCatchmentStation(nullptr, true);
|
||||
if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
|
||||
if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_DT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true);
|
||||
|
||||
this->Window::Close();
|
||||
}
|
||||
|
||||
|
@ -164,7 +167,9 @@ struct BuildDocksToolbarWindow : Window {
|
|||
break;
|
||||
|
||||
case WID_DT_DEPOT: // Build depot button
|
||||
if (HandlePlacePushButton(this, WID_DT_DEPOT, SPR_CURSOR_SHIP_DEPOT, HT_RECT)) ShowBuildDocksDepotPicker(this);
|
||||
if (HandlePlacePushButton(this, widget, SPR_CURSOR_SHIP_DEPOT, HT_RECT)) {
|
||||
ShowBuildDocksDepotPicker(this);
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_DT_STATION: // Build station button
|
||||
|
@ -204,9 +209,16 @@ struct BuildDocksToolbarWindow : Window {
|
|||
PlaceProc_DemolishArea(tile);
|
||||
break;
|
||||
|
||||
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);
|
||||
case WID_DT_DEPOT: { // Build depot button
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
|
||||
|
||||
ViewportPlaceMethod vpm = _ship_depot_direction != AXIS_X ? VPM_LIMITED_X_FIXED_Y : VPM_LIMITED_Y_FIXED_X;
|
||||
VpSetPlaceSizingLimit(_settings_game.depot.depot_spread);
|
||||
VpStartPlaceSizing(tile, vpm, DDSP_BUILD_DEPOT);
|
||||
/* Select tiles now to prevent selection from flickering. */
|
||||
VpSelectTilesWithMethod(pt.x, pt.y, vpm);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_DT_STATION: { // Build station button
|
||||
/* Determine the watery part of the dock. */
|
||||
|
@ -260,6 +272,15 @@ struct BuildDocksToolbarWindow : Window {
|
|||
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);
|
||||
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;
|
||||
}
|
||||
|
@ -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_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true);
|
||||
|
||||
this->RaiseButtons();
|
||||
|
||||
CloseWindowById(WC_BUILD_STATION, TRANSPORT_WATER);
|
||||
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_WATER);
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
|
||||
CloseWindowByClass(WC_BUILD_BRIDGE);
|
||||
}
|
||||
|
||||
|
@ -514,10 +538,13 @@ struct BuildDocksDepotWindow : public PickerWindowBase {
|
|||
private:
|
||||
static void UpdateDocksDirection()
|
||||
{
|
||||
VpSetPlaceFixedSize(2);
|
||||
if (_ship_depot_direction != AXIS_X) {
|
||||
SetTileSelectSize(1, 2);
|
||||
_thd.select_method = VPM_LIMITED_X_FIXED_Y;
|
||||
} else {
|
||||
SetTileSelectSize(2, 1);
|
||||
_thd.select_method = VPM_LIMITED_Y_FIXED_X;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -529,6 +556,13 @@ public:
|
|||
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
|
||||
{
|
||||
switch (widget) {
|
||||
|
@ -569,6 +603,7 @@ public:
|
|||
switch (widget) {
|
||||
case WID_BDD_X:
|
||||
case WID_BDD_Y:
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
|
||||
this->RaiseWidget(WID_BDD_X + _ship_depot_direction);
|
||||
_ship_depot_direction = (widget == WID_BDD_X ? AXIS_X : AXIS_Y);
|
||||
this->LowerWidget(WID_BDD_X + _ship_depot_direction);
|
||||
|
@ -578,6 +613,11 @@ public:
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
|
||||
{
|
||||
CheckRedrawDepotHighlight(this, VEH_SHIP);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr NWidgetPart _nested_build_docks_depot_widgets[] = {
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "timer/timer.h"
|
||||
#include "timer/timer_game_calendar.h"
|
||||
#include "timer/timer_game_economy.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "table/pricebase.h"
|
||||
|
@ -374,6 +375,12 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
|
|||
}
|
||||
if (new_owner == INVALID_OWNER) RebuildSubsidisedSourceAndDestinationCache();
|
||||
|
||||
for (Depot *dep : Depot::Iterate()) {
|
||||
if (dep->owner == old_owner && new_owner != INVALID_OWNER) {
|
||||
dep->owner = new_owner;
|
||||
}
|
||||
}
|
||||
|
||||
/* Take care of rating and transport rights in towns */
|
||||
for (Town *t : Town::Iterate()) {
|
||||
/* If a company takes over, give the ratings to that company. */
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "core/pool_func.hpp"
|
||||
#include "order_backup.h"
|
||||
#include "group_cmd.h"
|
||||
#include "depot_map.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -579,7 +580,7 @@ std::tuple<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_DETAILS, v->index);
|
||||
InvalidateWindowData(WC_VEHICLE_VIEW, v->index);
|
||||
|
|
|
@ -842,7 +842,7 @@ public:
|
|||
break;
|
||||
|
||||
case WID_GL_AVAILABLE_VEHICLES:
|
||||
ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
|
||||
ShowBuildVehicleWindow(INVALID_DEPOT, this->vli.vtype);
|
||||
break;
|
||||
|
||||
case WID_GL_MANAGE_VEHICLES_DROPDOWN: {
|
||||
|
|
|
@ -278,6 +278,8 @@ STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Select f
|
|||
STR_BUTTON_SORT_BY :{BLACK}Sort by
|
||||
STR_BUTTON_CATCHMENT :{BLACK}Coverage
|
||||
STR_TOOLTIP_CATCHMENT :{BLACK}Toggle coverage area display
|
||||
STR_BUTTON_HIGHLIGHT_DEPOT :{BLACK}Highlight
|
||||
STR_TOOLTIP_HIGHLIGHT_DEPOT :{BLACK}Toggle highlight on viewport for this depot
|
||||
|
||||
STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Close window
|
||||
STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Window title - drag this to move window
|
||||
|
@ -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_HELPTEXT :Allow construction of drive-through road stops on roads owned by other companies
|
||||
STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Changing this setting is not possible when there are vehicles
|
||||
STR_CONFIG_SETTING_REPLACEMENTS_DIFF_TYPE :{WHITE}Disabling this setting is not possible during a game
|
||||
|
||||
STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :Infrastructure maintenance: {STRING2}
|
||||
STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :When enabled, infrastructure causes maintenance costs. The cost grows over-proportional with the network size, thus affecting bigger companies more than smaller ones
|
||||
|
@ -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_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_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_NPC :Computer players
|
||||
STR_CONFIG_SETTING_NETWORK :Network
|
||||
STR_CONFIG_SETTING_DEPOTS :Depots
|
||||
|
||||
STR_CONFIG_SETTING_REVERSE_AT_SIGNALS :Automatic reversing at signals: {STRING2}
|
||||
STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Allow trains to reverse on a signal, if they waited there a long time
|
||||
|
@ -2763,6 +2776,11 @@ STR_JOIN_STATION_CREATE_SPLITTED_STATION :{YELLOW}Build a
|
|||
STR_JOIN_WAYPOINT_CAPTION :{WHITE}Join waypoint
|
||||
STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT :{YELLOW}Build a separate waypoint
|
||||
|
||||
# Join depot window
|
||||
STR_JOIN_DEPOT_CAPTION :{WHITE}Join depot
|
||||
STR_JOIN_DEPOT_CREATE_SPLITTED_DEPOT :{YELLOW}Build a separate depot
|
||||
STR_DEPOT_LIST_DEPOT :{YELLOW}{DEPOT}
|
||||
|
||||
# Generic toolbar
|
||||
STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE :{BLACK}Disabled as currently no vehicles are available for this infrastructure
|
||||
|
||||
|
@ -5110,10 +5128,12 @@ STR_ERROR_BUOY_IS_IN_USE :{WHITE}... buoy
|
|||
|
||||
# Depot related errors
|
||||
STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT :{WHITE}Can't build train depot here...
|
||||
STR_ERROR_CAN_T_REMOVE_TRAIN_DEPOT :{WHITE}Can't remove train depot here...
|
||||
STR_ERROR_CAN_T_BUILD_ROAD_DEPOT :{WHITE}Can't build road vehicle depot here...
|
||||
STR_ERROR_CAN_T_BUILD_TRAM_DEPOT :{WHITE}Can't build tram vehicle depot here...
|
||||
STR_ERROR_CAN_T_BUILD_SHIP_DEPOT :{WHITE}Can't build ship depot here...
|
||||
|
||||
STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING_DEPOT :{WHITE}... adjoins more than one existing depot
|
||||
STR_ERROR_CAN_T_RENAME_DEPOT :{WHITE}Can't rename depot...
|
||||
|
||||
STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT :{WHITE}... must be stopped inside a depot
|
||||
|
@ -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_TRAIN_TOO_LONG :{WHITE}Train too long
|
||||
STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT :{WHITE}Train chain is incompatible with any tile of this depot
|
||||
STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE :{WHITE}Can't reverse direction of vehicle...
|
||||
STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS :{WHITE}... consists of multiple units
|
||||
STR_ERROR_INCOMPATIBLE_RAIL_TYPES :Incompatible rail types
|
||||
|
@ -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_UNABLE_TO_FIND_ROUTE_TO :{WHITE}Unable to find route to local depot
|
||||
STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT :{WHITE}Unable to find local depot
|
||||
STR_ERROR_UNABLE_TO_FIND_APPROPRIATE_DEPOT_TILE :{WHITE}Unable to find appropriate depot tile
|
||||
|
||||
STR_ERROR_DEPOT_TOO_SPREAD_OUT :{WHITE}... depot too spread out
|
||||
STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Wrong depot type
|
||||
|
||||
# Depot unbunching related errors
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "order_cmd.h"
|
||||
#include "group_cmd.h"
|
||||
#include "vehicle_func.h"
|
||||
#include "depot_map.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
@ -45,8 +47,9 @@ OrderBackup::~OrderBackup()
|
|||
*/
|
||||
OrderBackup::OrderBackup(const Vehicle *v, uint32_t user)
|
||||
{
|
||||
assert(IsDepotTile(v->tile));
|
||||
this->user = user;
|
||||
this->tile = v->tile;
|
||||
this->depot_id = GetDepotIndex(v->tile);
|
||||
this->group = v->group_id;
|
||||
|
||||
this->CopyConsistPropertiesFrom(v);
|
||||
|
@ -123,8 +126,10 @@ void OrderBackup::DoRestore(Vehicle *v)
|
|||
*/
|
||||
/* static */ void OrderBackup::Restore(Vehicle *v, uint32_t user)
|
||||
{
|
||||
assert(IsDepotTile(v->tile));
|
||||
DepotID depot_id_veh = GetDepotIndex(v->tile);
|
||||
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
||||
if (v->tile != ob->tile || ob->user != user) continue;
|
||||
if (depot_id_veh != ob->depot_id || ob->user != user) continue;
|
||||
|
||||
ob->DoRestore(v);
|
||||
delete ob;
|
||||
|
@ -133,28 +138,28 @@ void OrderBackup::DoRestore(Vehicle *v)
|
|||
|
||||
/**
|
||||
* Reset an OrderBackup given a tile and user.
|
||||
* @param tile The tile associated with the OrderBackup.
|
||||
* @param depot_id The depot associated with the OrderBackup.
|
||||
* @param user The user associated with the OrderBackup.
|
||||
* @note Must not be used from the GUI!
|
||||
*/
|
||||
/* static */ void OrderBackup::ResetOfUser(TileIndex tile, uint32_t user)
|
||||
/* static */ void OrderBackup::ResetOfUser(DepotID depot_id, uint32_t user)
|
||||
{
|
||||
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
||||
if (ob->user == user && (ob->tile == tile || tile == INVALID_TILE)) delete ob;
|
||||
if (ob->user == user && (ob->depot_id == depot_id || depot_id == INVALID_DEPOT)) delete ob;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear an OrderBackup
|
||||
* @param flags For command.
|
||||
* @param tile Tile related to the to-be-cleared OrderBackup.
|
||||
* @param depot_id Tile related to the to-be-cleared OrderBackup.
|
||||
* @param user_id User that had the OrderBackup.
|
||||
* @return The cost of this operation or an error.
|
||||
*/
|
||||
CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id)
|
||||
CommandCost CmdClearOrderBackup(DoCommandFlag flags, DepotID depot_id, ClientID user_id)
|
||||
{
|
||||
/* No need to check anything. If the tile or user don't exist we just ignore it. */
|
||||
if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, user_id);
|
||||
assert(Depot::IsValidID(depot_id));
|
||||
if (flags & DC_EXEC) OrderBackup::ResetOfUser(depot_id, user_id);
|
||||
|
||||
return CommandCost();
|
||||
}
|
||||
|
@ -173,18 +178,18 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID us
|
|||
/* If it's not a backup of us, ignore it. */
|
||||
if (ob->user != user) continue;
|
||||
|
||||
Command<CMD_CLEAR_ORDER_BACKUP>::Post(0, static_cast<ClientID>(user));
|
||||
Command<CMD_CLEAR_ORDER_BACKUP>::Post(ob->depot_id, static_cast<ClientID>(user));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the OrderBackups from GUI/game logic.
|
||||
* @param t The tile of the order backup.
|
||||
* @param depot_id The index of the depot associated to the order backups.
|
||||
* @param from_gui Whether the call came from the GUI, i.e. whether
|
||||
* it must be synced over the network.
|
||||
*/
|
||||
/* static */ void OrderBackup::Reset(TileIndex t, bool from_gui)
|
||||
/* static */ void OrderBackup::Reset(DepotID depot_id, bool from_gui)
|
||||
{
|
||||
/* The user has CLIENT_ID_SERVER as default when network play is not active,
|
||||
* but compiled it. A network client has its own variable for the unique
|
||||
|
@ -195,16 +200,16 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID us
|
|||
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
||||
/* If this is a GUI action, and it's not a backup of us, ignore it. */
|
||||
if (from_gui && ob->user != user) continue;
|
||||
/* If it's not for our chosen tile either, ignore it. */
|
||||
if (t != INVALID_TILE && t != ob->tile) continue;
|
||||
/* If it's not for our chosen depot either, ignore it. */
|
||||
if (depot_id != INVALID_DEPOT && depot_id != ob->depot_id) continue;
|
||||
|
||||
if (from_gui) {
|
||||
/* We need to circumvent the "prevention" from this command being executed
|
||||
* while the game is paused, so use the internal method. Nor do we want
|
||||
* this command to get its cost estimated when shift is pressed. */
|
||||
Command<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 {
|
||||
/* The command came from the game logic, i.e. the clearing of a tile.
|
||||
/* The command came from the game logic, i.e. the clearing of a depot.
|
||||
* In that case we have no need to actually sync this, just do it. */
|
||||
delete ob;
|
||||
}
|
||||
|
@ -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.
|
||||
* @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]).
|
||||
* @param destination The destination. Can be a StationID, DepotID or WaypointID.
|
||||
* @param hangar Only used for airports in the destination.
|
||||
* When false, remove airport and hangar orders.
|
||||
* When true, remove either airport or hangar order.
|
||||
*/
|
||||
/* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination, bool hangar)
|
||||
/* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination)
|
||||
{
|
||||
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
||||
for (Order *order = ob->orders; order != nullptr; order = order->next) {
|
||||
OrderType ot = order->GetType();
|
||||
if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
|
||||
if (ot == OT_GOTO_DEPOT && hangar && !IsHangarTile(ob->tile)) continue; // Not an aircraft? Can't have a hangar order.
|
||||
if (ot == OT_IMPLICIT || (IsHangarTile(ob->tile) && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
|
||||
if (ot == OT_IMPLICIT) ot = OT_GOTO_STATION;
|
||||
if (ot == type && order->GetDestination() == destination) {
|
||||
/* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */
|
||||
delete ob;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "vehicle_type.h"
|
||||
#include "base_consist.h"
|
||||
#include "saveload/saveload.h"
|
||||
#include "depot_type.h"
|
||||
|
||||
/** Unique identifier for an order backup. */
|
||||
typedef uint8_t OrderBackupID;
|
||||
|
@ -34,8 +35,8 @@ struct OrderBackup : OrderBackupPool::PoolItem<&_order_backup_pool>, BaseConsist
|
|||
private:
|
||||
friend SaveLoadTable GetOrderBackupDescription(); ///< Saving and loading of order backups.
|
||||
friend struct BKORChunkHandler; ///< Creating empty orders upon savegame loading.
|
||||
uint32_t user; ///< The user that requested the backup.
|
||||
TileIndex tile; ///< Tile of the depot where the order was changed.
|
||||
uint32_t user; ///< The user that requested the backup.
|
||||
DepotID depot_id; ///< Depot where the order was changed.
|
||||
GroupID group; ///< The group the vehicle was part of.
|
||||
|
||||
const Vehicle *clone; ///< Vehicle this vehicle was a clone of.
|
||||
|
@ -53,13 +54,13 @@ public:
|
|||
static void Backup(const Vehicle *v, uint32_t user);
|
||||
static void Restore(Vehicle *v, uint32_t user);
|
||||
|
||||
static void ResetOfUser(TileIndex tile, uint32_t user);
|
||||
static void ResetOfUser(DepotID depot_id, uint32_t user);
|
||||
static void ResetUser(uint32_t user);
|
||||
static void Reset(TileIndex tile = INVALID_TILE, bool from_gui = true);
|
||||
static void Reset(DepotID depot = INVALID_DEPOT, bool from_gui = true);
|
||||
|
||||
static void ClearGroup(GroupID group);
|
||||
static void ClearVehicle(const Vehicle *v);
|
||||
static void RemoveOrder(OrderType type, DestinationID destination, bool hangar);
|
||||
static void RemoveOrder(OrderType type, DestinationID destination);
|
||||
};
|
||||
|
||||
#endif /* ORDER_BACKUP_H */
|
||||
|
|
|
@ -650,7 +650,7 @@ TileIndex Order::GetLocation(const Vehicle *v, bool airport) const
|
|||
|
||||
case OT_GOTO_DEPOT:
|
||||
if (this->GetDestination() == INVALID_DEPOT) return INVALID_TILE;
|
||||
return (v->type == VEH_AIRCRAFT) ? Station::Get(this->GetDestination())->xy : Depot::Get(this->GetDestination())->xy;
|
||||
return Depot::Get(this->GetDestination())->xy;
|
||||
|
||||
default:
|
||||
return INVALID_TILE;
|
||||
|
@ -684,6 +684,28 @@ uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int
|
|||
return v->type == VEH_AIRCRAFT ? DistanceSquare(prev_tile, cur_tile) : DistanceManhattan(prev_tile, cur_tile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the station or depot index associated to an order of a vehicle.
|
||||
* For aircraft, it will return the index of the associated station, even for go to hangar orders.
|
||||
* @param o Order to check.
|
||||
* @param is_aircraft Whether the order is of an aircraft vehicle.
|
||||
* @return index associated to a station or depot, or INVALID_STATION.
|
||||
*/
|
||||
DestinationID GetTargetDestination(const Order &o, bool is_aircraft)
|
||||
{
|
||||
DestinationID destination_id = o.GetDestination();
|
||||
switch (o.GetType()) {
|
||||
case OT_GOTO_STATION:
|
||||
return destination_id;
|
||||
case OT_GOTO_DEPOT:
|
||||
assert(Depot::IsValidID(destination_id));
|
||||
return is_aircraft ? GetStationIndex(Depot::Get(destination_id)->xy) : destination_id;
|
||||
default:
|
||||
return INVALID_STATION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an order to the orderlist of a vehicle.
|
||||
* @param flags operation to perform
|
||||
|
@ -763,41 +785,15 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se
|
|||
|
||||
case OT_GOTO_DEPOT: {
|
||||
if ((new_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0) {
|
||||
if (v->type == VEH_AIRCRAFT) {
|
||||
const Station *st = Station::GetIfValid(new_order.GetDestination());
|
||||
const Depot *dp = Depot::GetIfValid(new_order.GetDestination());
|
||||
|
||||
if (st == nullptr) return CMD_ERROR;
|
||||
if (dp == nullptr) return CMD_ERROR;
|
||||
|
||||
ret = CheckOwnership(st->owner);
|
||||
if (ret.Failed()) return ret;
|
||||
ret = CheckOwnership(dp->owner);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
if (!CanVehicleUseStation(v, st) || !st->airport.HasHangar()) {
|
||||
return CMD_ERROR;
|
||||
}
|
||||
} else {
|
||||
const Depot *dp = Depot::GetIfValid(new_order.GetDestination());
|
||||
|
||||
if (dp == nullptr) return CMD_ERROR;
|
||||
|
||||
ret = CheckOwnership(GetTileOwner(dp->xy));
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
switch (v->type) {
|
||||
case VEH_TRAIN:
|
||||
if (!IsRailDepotTile(dp->xy)) return CMD_ERROR;
|
||||
break;
|
||||
|
||||
case VEH_ROAD:
|
||||
if (!IsRoadDepotTile(dp->xy)) return CMD_ERROR;
|
||||
break;
|
||||
|
||||
case VEH_SHIP:
|
||||
if (!IsShipDepotTile(dp->xy)) return CMD_ERROR;
|
||||
break;
|
||||
|
||||
default: return CMD_ERROR;
|
||||
}
|
||||
}
|
||||
if (v->type != dp->veh_type) return CMD_ERROR;
|
||||
if (v->type == VEH_AIRCRAFT && !CanVehicleUseStation(v, Station::GetByTile(dp->xy))) return CMD_ERROR;
|
||||
}
|
||||
|
||||
if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) return CMD_ERROR;
|
||||
|
@ -1780,24 +1776,11 @@ void CheckOrders(const Vehicle *v)
|
|||
* Removes an order from all vehicles. Triggers when, say, a station is removed.
|
||||
* @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]).
|
||||
* @param destination The destination. Can be a StationID, DepotID or WaypointID.
|
||||
* @param hangar Only used for airports in the destination.
|
||||
* When false, remove airport and hangar orders.
|
||||
* When true, remove either airport or hangar order.
|
||||
*/
|
||||
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar)
|
||||
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination)
|
||||
{
|
||||
/* Aircraft have StationIDs for depot orders and never use DepotIDs
|
||||
* This fact is handled specially below
|
||||
*/
|
||||
|
||||
/* Go through all vehicles */
|
||||
for (Vehicle *v : Vehicle::Iterate()) {
|
||||
if ((v->type == VEH_AIRCRAFT && v->current_order.IsType(OT_GOTO_DEPOT) && !hangar ? OT_GOTO_STATION : v->current_order.GetType()) == type &&
|
||||
(!hangar || v->type == VEH_AIRCRAFT) && v->current_order.GetDestination() == destination) {
|
||||
v->current_order.MakeDummy();
|
||||
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
|
||||
}
|
||||
|
||||
/* Clear the order from the order-list */
|
||||
int id = -1;
|
||||
for (Order *order : v->Orders()) {
|
||||
|
@ -1806,8 +1789,7 @@ restart:
|
|||
|
||||
OrderType ot = order->GetType();
|
||||
if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
|
||||
if (ot == OT_GOTO_DEPOT && hangar && v->type != VEH_AIRCRAFT) continue; // Not an aircraft? Can't have a hangar order.
|
||||
if (ot == OT_IMPLICIT || (v->type == VEH_AIRCRAFT && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
|
||||
if (ot == OT_IMPLICIT) ot = OT_GOTO_STATION;
|
||||
if (ot == type && order->GetDestination() == destination) {
|
||||
/* We want to clear implicit orders, but we don't want to make them
|
||||
* dummy orders. They should just vanish. Also check the actual order
|
||||
|
@ -1841,7 +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();
|
||||
} else {
|
||||
if (v->type != VEH_AIRCRAFT) {
|
||||
v->SetDestTile(Depot::Get(order->GetDestination())->xy);
|
||||
v->SetDestTile(Depot::Get(order->GetDestination())->GetBestDepotTile(v));
|
||||
} else {
|
||||
Aircraft *a = Aircraft::From(v);
|
||||
DestinationID destination = a->current_order.GetDestination();
|
||||
DestinationID destination_depot = a->current_order.GetDestination();
|
||||
StationID destination = GetStationIndex(Depot::Get(destination_depot)->xy);
|
||||
if (a->targetairport != destination) {
|
||||
/* The aircraft is now heading for a different hangar than the next in the orders */
|
||||
a->SetDestTile(a->GetOrderStationLocation(destination));
|
||||
|
|
|
@ -21,7 +21,7 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se
|
|||
CommandCost CmdOrderRefit(DoCommandFlag flags, VehicleID veh, VehicleOrderID order_number, CargoID cargo);
|
||||
CommandCost CmdCloneOrder(DoCommandFlag flags, CloneOptions action, VehicleID veh_dst, VehicleID veh_src);
|
||||
CommandCost CmdMoveOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID moving_order, VehicleOrderID target_order);
|
||||
CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id);
|
||||
CommandCost CmdClearOrderBackup(DoCommandFlag flags, DepotID depot_id, ClientID user_id);
|
||||
|
||||
DEF_CMD_TRAIT(CMD_MODIFY_ORDER, CmdModifyOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT)
|
||||
DEF_CMD_TRAIT(CMD_SKIP_TO_ORDER, CmdSkipToOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT)
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "company_type.h"
|
||||
|
||||
/* Functions */
|
||||
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar = false);
|
||||
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination);
|
||||
void InvalidateVehicleOrder(const Vehicle *v, int data);
|
||||
void CheckOrders(const Vehicle*);
|
||||
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false, bool reset_order_indices = true);
|
||||
|
@ -23,6 +23,7 @@ bool ProcessOrders(Vehicle *v);
|
|||
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0, bool pbs_look_ahead = false);
|
||||
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v);
|
||||
uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int conditional_depth = 0);
|
||||
DestinationID GetTargetDestination(const Order &o, bool is_aircraft);
|
||||
|
||||
void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable, int left, int middle, int right);
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "error.h"
|
||||
#include "order_cmd.h"
|
||||
#include "company_cmd.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "widgets/order_widget.h"
|
||||
|
||||
|
@ -309,7 +310,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
|
|||
/* Going to a specific depot. */
|
||||
SetDParam(0, STR_ORDER_GO_TO_DEPOT_FORMAT);
|
||||
SetDParam(2, v->type);
|
||||
SetDParam(3, order->GetDestination());
|
||||
SetDParam(3, GetTargetDestination(*order, v->type == VEH_AIRCRAFT));
|
||||
}
|
||||
|
||||
if (order->GetDepotOrderType() & ODTFB_SERVICE) {
|
||||
|
@ -384,8 +385,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
|
|||
|
||||
/* check depot first */
|
||||
if (IsDepotTypeTile(tile, (TransportType)(uint)v->type) && IsTileOwner(tile, _local_company)) {
|
||||
order.MakeGoToDepot(v->type == VEH_AIRCRAFT ? GetStationIndex(tile) : GetDepotIndex(tile),
|
||||
ODTFB_PART_OF_ORDERS,
|
||||
order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS,
|
||||
(_settings_client.gui.new_nonstop && v->IsGroundVehicle()) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
|
||||
|
||||
if (_ctrl_pressed) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "../tile_cmd.h"
|
||||
#include "../waypoint_base.h"
|
||||
#include "../depot_base.h"
|
||||
|
||||
/**
|
||||
* Calculates the tile of given station that is closest to a given tile
|
||||
|
@ -47,6 +48,37 @@ inline TileIndex CalcClosestStationTile(StationID station, TileIndex tile, Stati
|
|||
return TileXY(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the tile of a depot that is closest to a given tile.
|
||||
* @param depot_id The depot to calculate the distance to.
|
||||
* @param tile The tile from where to calculate the distance.
|
||||
* @return The closest depot tile to the given tile.
|
||||
*/
|
||||
static inline TileIndex CalcClosestDepotTile(DepotID depot_id, TileIndex tile)
|
||||
{
|
||||
assert(Depot::IsValidID(depot_id));
|
||||
const Depot *dep = Depot::Get(depot_id);
|
||||
|
||||
/* If tile area is empty, use the xy tile. */
|
||||
if (dep->ta.tile == INVALID_TILE) {
|
||||
assert(dep->xy != INVALID_TILE);
|
||||
return dep->xy;
|
||||
}
|
||||
|
||||
TileIndex best_tile = INVALID_TILE;
|
||||
uint best_distance = UINT_MAX;
|
||||
|
||||
for (auto const &depot_tile : dep->depot_tiles) {
|
||||
uint new_distance = DistanceManhattan(depot_tile, tile);
|
||||
if (new_distance < best_distance) {
|
||||
best_tile = depot_tile;
|
||||
best_distance = new_distance;
|
||||
}
|
||||
}
|
||||
|
||||
return best_tile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around GetTileTrackStatus() and TrackStatusToTrackdirBits(), as for
|
||||
* single tram bits GetTileTrackStatus() returns 0. The reason for this is
|
||||
|
|
|
@ -118,6 +118,7 @@ protected:
|
|||
TileIndex m_destTile;
|
||||
TrackdirBits m_destTrackdirs;
|
||||
StationID m_dest_station_id;
|
||||
DepotID m_dest_depot_id;
|
||||
bool m_any_depot;
|
||||
|
||||
/** to access inherited path finder */
|
||||
|
@ -149,14 +150,25 @@ public:
|
|||
break;
|
||||
|
||||
case OT_GOTO_DEPOT:
|
||||
m_dest_station_id = INVALID_STATION;
|
||||
|
||||
if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
|
||||
m_any_depot = true;
|
||||
m_dest_depot_id = INVALID_DEPOT;
|
||||
m_destTile = v->dest_tile;
|
||||
m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_RAIL, 0));
|
||||
} else {
|
||||
m_dest_depot_id = v->current_order.GetDestination();
|
||||
assert(Depot::IsValidID(m_dest_depot_id));
|
||||
m_destTile = CalcClosestDepotTile(m_dest_depot_id, v->tile);
|
||||
m_destTrackdirs = INVALID_TRACKDIR_BIT;
|
||||
}
|
||||
[[fallthrough]];
|
||||
break;
|
||||
|
||||
default:
|
||||
m_destTile = v->dest_tile;
|
||||
m_dest_station_id = INVALID_STATION;
|
||||
m_dest_depot_id = INVALID_DEPOT;
|
||||
m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_RAIL, 0));
|
||||
break;
|
||||
}
|
||||
|
@ -176,6 +188,10 @@ public:
|
|||
return HasStationTileRail(tile)
|
||||
&& (GetStationIndex(tile) == m_dest_station_id)
|
||||
&& (GetRailStationTrack(tile) == TrackdirToTrack(td));
|
||||
} else if (m_dest_depot_id != INVALID_DEPOT) {
|
||||
return IsRailDepotTile(tile)
|
||||
&& (GetDepotIndex(tile) == m_dest_depot_id)
|
||||
&& (GetRailDepotTrack(tile) == TrackdirToTrack(td));
|
||||
}
|
||||
|
||||
if (m_any_depot) {
|
||||
|
|
|
@ -234,7 +234,9 @@ protected:
|
|||
TileIndex m_destTile;
|
||||
TrackdirBits m_destTrackdirs;
|
||||
StationID m_dest_station;
|
||||
DepotID m_dest_depot;
|
||||
StationType m_station_type;
|
||||
bool m_bus;
|
||||
bool m_non_artic;
|
||||
|
||||
public:
|
||||
|
@ -252,8 +254,14 @@ public:
|
|||
m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_station_type);
|
||||
m_non_artic = !v->HasArticulatedPart();
|
||||
m_destTrackdirs = INVALID_TRACKDIR_BIT;
|
||||
} else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
|
||||
m_dest_station = INVALID_STATION;
|
||||
m_dest_depot = v->current_order.GetDestination();
|
||||
m_destTile = CalcClosestDepotTile(m_dest_depot, v->tile);
|
||||
m_destTrackdirs = INVALID_TRACKDIR_BIT;
|
||||
} else {
|
||||
m_dest_station = INVALID_STATION;
|
||||
m_dest_depot = INVALID_DEPOT;
|
||||
m_destTile = v->dest_tile;
|
||||
m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype)));
|
||||
}
|
||||
|
@ -287,6 +295,11 @@ public:
|
|||
(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);
|
||||
}
|
||||
|
||||
|
|
13
src/rail.h
13
src/rail.h
|
@ -337,6 +337,19 @@ inline bool HasPowerOnRail(RailType enginetype, RailType tiletype)
|
|||
return HasBit(GetRailTypeInfo(enginetype)->powered_railtypes, tiletype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an engine with a given \a enginetype is powered on \a rail_types.
|
||||
* This would normally just be an equality check,
|
||||
* but for electric rails (which also support non-electric vehicles).
|
||||
* @param enginetype The RailType of the engine we are considering.
|
||||
* @param rail_types The RailTypes we are considering.
|
||||
* @return Whether the engine got power on this tile.
|
||||
*/
|
||||
static inline bool HasPowerOnRails(RailType enginetype, RailTypes rail_types)
|
||||
{
|
||||
return (GetRailTypeInfo(enginetype)->powered_railtypes & rail_types) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a RailType disallows build of level crossings.
|
||||
* @param rt The RailType to check.
|
||||
|
|
167
src/rail_cmd.cpp
167
src/rail_cmd.cpp
|
@ -952,22 +952,32 @@ CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, Tile
|
|||
/**
|
||||
* Build a train depot
|
||||
* @param flags operation to perform
|
||||
* @param tile position of the train depot
|
||||
* @param tile first position of the train depot
|
||||
* @param railtype rail type
|
||||
* @param dir entrance direction
|
||||
* @param adjacent allow adjacent depots
|
||||
* @param join_to depot to join to
|
||||
* @param end_tile end tile of the area to be built
|
||||
* @return the cost of this operation or an error
|
||||
*
|
||||
* @todo When checking for the tile slope,
|
||||
* distinguish between "Flat land required" and "land sloped in wrong direction"
|
||||
*/
|
||||
CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir)
|
||||
CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile)
|
||||
{
|
||||
/* check railtype and valid direction for depot (0 through 3), 4 in total */
|
||||
if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
|
||||
|
||||
Slope tileh = GetTileSlope(tile);
|
||||
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||
TileArea ta(tile, end_tile);
|
||||
Depot *depot = nullptr;
|
||||
|
||||
/* Create a new depot or find a depot to join to. */
|
||||
CommandCost ret = FindJoiningDepot(ta, VEH_TRAIN, join_to, depot, adjacent, flags);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
uint8_t num_new_depot_tiles = 0;
|
||||
uint8_t num_rotated_depot_tiles = 0;
|
||||
|
||||
/* Prohibit construction if
|
||||
* The tile is non-flat AND
|
||||
|
@ -975,58 +985,59 @@ CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType rai
|
|||
* 2) the tile is steep i.e. spans two height levels
|
||||
* 3) the exit points in the wrong direction
|
||||
*/
|
||||
for (Tile t : ta) {
|
||||
if (IsBridgeAbove(t)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
|
||||
|
||||
if (tileh != SLOPE_FLAT) {
|
||||
if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) {
|
||||
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
|
||||
Slope tileh = GetTileSlope(t);
|
||||
if (tileh != SLOPE_FLAT) {
|
||||
if (!_settings_game.construction.build_on_slopes ||
|
||||
!CanBuildDepotByTileh(dir, tileh)) {
|
||||
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
|
||||
}
|
||||
cost.AddCost(_price[PR_BUILD_FOUNDATION]);
|
||||
}
|
||||
|
||||
/* Check whether a depot tile exists and it needs to be rotated. */
|
||||
if (IsRailDepotTile(t) && GetDepotIndex(t) == join_to && railtype == GetRailType(t)) {
|
||||
if (dir == GetRailDepotDirection(t)) continue;
|
||||
|
||||
ret = EnsureNoVehicleOnGround(t);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
num_rotated_depot_tiles++;
|
||||
if (flags & DC_EXEC) {
|
||||
SetRailDepotExitDirection(t, dir);
|
||||
}
|
||||
} else {
|
||||
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, t));
|
||||
if (cost.Failed()) return cost;
|
||||
|
||||
num_new_depot_tiles++;
|
||||
if (flags & DC_EXEC) {
|
||||
MakeRailDepot(t, _current_company, depot->index, dir, railtype);
|
||||
MarkTileDirtyByTile(t);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
AddSideToSignalBuffer(t, INVALID_DIAGDIR, _current_company);
|
||||
YapfNotifyTrackLayoutChange(t, DiagDirToDiagTrack(dir));
|
||||
MarkTileDirtyByTile(t);
|
||||
}
|
||||
cost.AddCost(_price[PR_BUILD_FOUNDATION]);
|
||||
}
|
||||
|
||||
/* Allow the user to rotate the depot instead of having to destroy it and build it again */
|
||||
bool rotate_existing_depot = false;
|
||||
if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) {
|
||||
CommandCost ret = CheckTileOwnership(tile);
|
||||
if (ret.Failed()) return ret;
|
||||
if (num_new_depot_tiles + num_rotated_depot_tiles == 0) return CommandCost();
|
||||
|
||||
if (dir == GetRailDepotDirection(tile)) return CommandCost();
|
||||
|
||||
ret = EnsureNoVehicleOnGround(tile);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
rotate_existing_depot = true;
|
||||
}
|
||||
|
||||
if (!rotate_existing_depot) {
|
||||
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile));
|
||||
if (cost.Failed()) return cost;
|
||||
|
||||
if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
|
||||
|
||||
if (!Depot::CanAllocateItem()) return CMD_ERROR;
|
||||
}
|
||||
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) {
|
||||
if (rotate_existing_depot) {
|
||||
SetRailDepotExitDirection(tile, dir);
|
||||
} else {
|
||||
Depot *d = new Depot(tile);
|
||||
d->build_date = TimerGameCalendar::date;
|
||||
|
||||
MakeRailDepot(tile, _current_company, d->index, dir, railtype);
|
||||
MakeDefaultName(d);
|
||||
|
||||
Company::Get(_current_company)->infrastructure.rail[railtype]++;
|
||||
DirtyCompanyInfrastructureWindows(_current_company);
|
||||
}
|
||||
|
||||
MarkTileDirtyByTile(tile);
|
||||
AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_company);
|
||||
YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
|
||||
Company::Get(_current_company)->infrastructure.rail[railtype] += num_new_depot_tiles;
|
||||
DirtyCompanyInfrastructureWindows(_current_company);
|
||||
depot->AfterAddRemove(ta, true);
|
||||
if (join_to == NEW_DEPOT) MakeDefaultName(depot);
|
||||
}
|
||||
|
||||
cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN]);
|
||||
cost.AddCost(RailBuildCost(railtype));
|
||||
return cost;
|
||||
}
|
||||
|
||||
|
@ -1558,6 +1569,7 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
|
|||
if (area_start >= Map::Size()) return CMD_ERROR;
|
||||
|
||||
TrainList affected_trains;
|
||||
std::vector<DepotID> affected_depots;
|
||||
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||
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 */
|
||||
YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile));
|
||||
|
||||
/* Update build vehicle window related to this depot */
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, tile);
|
||||
InvalidateWindowData(WC_BUILD_VEHICLE, tile);
|
||||
if (find(affected_depots.begin(), affected_depots.end(), (tile)) == affected_depots.end()) {
|
||||
affected_depots.push_back(GetDepotIndex(tile));
|
||||
}
|
||||
}
|
||||
|
||||
found_convertible_track = true;
|
||||
cost.AddCost(RailConvertCost(type, totype));
|
||||
break;
|
||||
|
@ -1754,6 +1767,13 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
|
|||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
/* Update affected depots. */
|
||||
for (auto &depot_tile : affected_depots) {
|
||||
Depot *dep = Depot::Get(depot_tile);
|
||||
dep->RescanDepotTiles();
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, dep->index);
|
||||
}
|
||||
|
||||
/* Railtype changed, update trains as when entering different track */
|
||||
for (Train *v : affected_trains) {
|
||||
v->ConsistChanged(CCF_TRACK);
|
||||
|
@ -1765,6 +1785,8 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
|
|||
|
||||
static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
|
||||
{
|
||||
assert(IsRailDepotTile(tile));
|
||||
|
||||
if (_current_company != OWNER_WATER) {
|
||||
CommandCost ret = CheckTileOwnership(tile);
|
||||
if (ret.Failed()) return ret;
|
||||
|
@ -1774,9 +1796,11 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
|
|||
if (ret.Failed()) return ret;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
/* read variables before the depot is removed */
|
||||
Depot *depot = Depot::GetByTile(tile);
|
||||
Company *c = Company::GetIfValid(depot->owner);
|
||||
assert(c != nullptr);
|
||||
|
||||
DiagDirection dir = GetRailDepotDirection(tile);
|
||||
Owner owner = GetTileOwner(tile);
|
||||
Train *v = nullptr;
|
||||
|
||||
if (HasDepotReservation(tile)) {
|
||||
|
@ -1784,19 +1808,46 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
|
|||
if (v != nullptr) FreeTrainTrackReservation(v);
|
||||
}
|
||||
|
||||
Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--;
|
||||
DirtyCompanyInfrastructureWindows(owner);
|
||||
c->infrastructure.rail[GetRailType(tile)]--;
|
||||
DirtyCompanyInfrastructureWindows(c->index);
|
||||
|
||||
delete Depot::GetByTile(tile);
|
||||
DoClearSquare(tile);
|
||||
AddSideToSignalBuffer(tile, dir, owner);
|
||||
|
||||
AddSideToSignalBuffer(tile, dir, c->index);
|
||||
|
||||
YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
|
||||
if (v != nullptr) TryPathReserve(v, true);
|
||||
|
||||
depot->AfterAddRemove(TileArea(tile), false);
|
||||
}
|
||||
|
||||
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove train depots from an area
|
||||
* @param flags operation to perform
|
||||
* @param start_tile start tile of the area
|
||||
* @param end_tile end tile of the area
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdRemoveTrainDepot(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile)
|
||||
{
|
||||
assert(IsValidTile(start_tile));
|
||||
assert(IsValidTile(end_tile));
|
||||
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||
TileArea ta(start_tile, end_tile);
|
||||
for (TileIndex t : ta) {
|
||||
if (!IsRailDepotTile(t)) continue;
|
||||
CommandCost ret = RemoveTrainDepot(t, flags);
|
||||
if (ret.Failed()) return ret;
|
||||
cost.AddCost(ret);
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
|
||||
{
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||
|
@ -2774,7 +2825,7 @@ static bool ClickTile_Track(TileIndex tile)
|
|||
{
|
||||
if (!IsRailDepot(tile)) return false;
|
||||
|
||||
ShowDepotWindow(tile, VEH_TRAIN);
|
||||
ShowDepotWindow(GetDepotIndex(tile));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2973,7 +3024,7 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int
|
|||
if (v->Next() == nullptr) VehicleEnterDepot(v->First());
|
||||
v->tile = tile;
|
||||
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
return VETSB_ENTERED_WORMHOLE;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 CmdBuildSingleRail(DoCommandFlag flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals);
|
||||
CommandCost CmdRemoveSingleRail(DoCommandFlag flags, TileIndex tile, Track track);
|
||||
CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir);
|
||||
CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, DepotID depot_id, TileIndex end_tile);
|
||||
CommandCost CmdRemoveTrainDepot(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile);
|
||||
CommandCost CmdBuildSingleSignal(DoCommandFlag flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy);
|
||||
CommandCost CmdRemoveSingleSignal(DoCommandFlag flags, TileIndex tile, Track track);
|
||||
CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal);
|
||||
|
@ -31,6 +32,7 @@ DEF_CMD_TRAIT(CMD_REMOVE_RAILROAD_TRACK, CmdRemoveRailroadTrack, CMD_AUTO,
|
|||
DEF_CMD_TRAIT(CMD_BUILD_SINGLE_RAIL, CmdBuildSingleRail, CMD_AUTO | CMD_NO_WATER, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
DEF_CMD_TRAIT(CMD_REMOVE_SINGLE_RAIL, CmdRemoveSingleRail, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
DEF_CMD_TRAIT(CMD_BUILD_TRAIN_DEPOT, CmdBuildTrainDepot, CMD_AUTO | CMD_NO_WATER, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
DEF_CMD_TRAIT(CMD_REMOVE_TRAIN_DEPOT, CmdRemoveTrainDepot, CMD_AUTO | CMD_NO_WATER, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
DEF_CMD_TRAIT(CMD_BUILD_SINGLE_SIGNAL, CmdBuildSingleSignal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
DEF_CMD_TRAIT(CMD_REMOVE_SINGLE_SIGNAL, CmdRemoveSingleSignal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
DEF_CMD_TRAIT(CMD_CONVERT_RAIL, CmdConvertRail, 0, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
|
@ -40,6 +42,6 @@ DEF_CMD_TRAIT(CMD_REMOVE_SIGNAL_TRACK, CmdRemoveSignalTrack, CMD_AUTO,
|
|||
CommandCallback CcPlaySound_CONSTRUCTION_RAIL;
|
||||
CommandCallback CcStation;
|
||||
CommandCallback CcBuildRailTunnel;
|
||||
void CcRailDepot(Commands cmd, const CommandCost &result, TileIndex tile, RailType rt, DiagDirection dir);
|
||||
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 */
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "timer/timer.h"
|
||||
#include "timer/timer_game_calendar.h"
|
||||
#include "picker_gui.h"
|
||||
#include "depot_func.h"
|
||||
|
||||
#include "station_map.h"
|
||||
#include "tunnelbridge_map.h"
|
||||
|
@ -116,7 +117,7 @@ static void GenericPlaceRail(TileIndex tile, Track track)
|
|||
*/
|
||||
static void PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track)
|
||||
{
|
||||
if (GetRailTileType(tile) == RAIL_TILE_DEPOT) return;
|
||||
if (IsRailDepot(tile)) return;
|
||||
if (GetRailTileType(tile) == RAIL_TILE_SIGNALS && !_settings_client.gui.auto_remove_signals) return;
|
||||
if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) return;
|
||||
|
||||
|
@ -137,24 +138,26 @@ static const DiagDirection _place_depot_extra_dir[12] = {
|
|||
DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_NW, DIAGDIR_NE,
|
||||
};
|
||||
|
||||
void CcRailDepot(Commands, const CommandCost &result, TileIndex tile, RailType, DiagDirection dir)
|
||||
void CcRailDepot(Commands, const CommandCost &result, TileIndex start_tile, RailType, DiagDirection dir, bool, DepotID, TileIndex end_tile)
|
||||
{
|
||||
if (result.Failed()) return;
|
||||
|
||||
if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, tile);
|
||||
if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, start_tile);
|
||||
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
|
||||
|
||||
tile += TileOffsByDiagDir(dir);
|
||||
TileArea ta(start_tile, end_tile);
|
||||
for (TileIndex t : ta) {
|
||||
TileIndex tile = t + TileOffsByDiagDir(dir);
|
||||
|
||||
if (IsTileType(tile, MP_RAILWAY)) {
|
||||
PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]);
|
||||
if (IsTileType(tile, MP_RAILWAY)) {
|
||||
PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]);
|
||||
|
||||
/* Don't place the rail straight out of the depot of there is another depot across from it. */
|
||||
Tile double_depot_tile = tile + TileOffsByDiagDir(dir);
|
||||
bool is_double_depot = IsValidTile(double_depot_tile) && IsRailDepotTile(double_depot_tile);
|
||||
if (!is_double_depot) PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 4], _place_depot_extra_track[dir + 4]);
|
||||
Tile double_depot_tile = tile + TileOffsByDiagDir(dir);
|
||||
bool is_double_depot = IsValidTile(double_depot_tile) && IsRailDepotTile(double_depot_tile);
|
||||
if (!is_double_depot) PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 4], _place_depot_extra_track[dir + 4]);
|
||||
|
||||
PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 8], _place_depot_extra_track[dir + 8]);
|
||||
PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 8], _place_depot_extra_track[dir + 8]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,6 +320,7 @@ void CcBuildRailTunnel(Commands, const CommandCost &result, TileIndex tile)
|
|||
static void ToggleRailButton_Remove(Window *w)
|
||||
{
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
|
||||
w->ToggleWidgetLoweredState(WID_RAT_REMOVE);
|
||||
w->SetWidgetDirty(WID_RAT_REMOVE);
|
||||
_remove_button_clicked = w->IsWidgetLowered(WID_RAT_REMOVE);
|
||||
|
@ -333,8 +337,9 @@ static bool RailToolbar_CtrlChanged(Window *w)
|
|||
if (w->IsWidgetDisabled(WID_RAT_REMOVE)) return false;
|
||||
|
||||
/* allow ctrl to switch remove mode only for these widgets */
|
||||
|
||||
for (WidgetID i = WID_RAT_BUILD_NS; i <= WID_RAT_BUILD_STATION; i++) {
|
||||
if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_WAYPOINT) && w->IsWidgetLowered(i)) {
|
||||
if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_DEPOT) && w->IsWidgetLowered(i)) {
|
||||
ToggleRailButton_Remove(w);
|
||||
return true;
|
||||
}
|
||||
|
@ -454,6 +459,7 @@ struct BuildRailToolbarWindow : Window {
|
|||
if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true);
|
||||
if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
if (this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true);
|
||||
this->Window::Close();
|
||||
}
|
||||
|
||||
|
@ -533,6 +539,7 @@ struct BuildRailToolbarWindow : Window {
|
|||
case WID_RAT_BUILD_EW:
|
||||
case WID_RAT_BUILD_Y:
|
||||
case WID_RAT_AUTORAIL:
|
||||
case WID_RAT_BUILD_DEPOT:
|
||||
case WID_RAT_BUILD_WAYPOINT:
|
||||
case WID_RAT_BUILD_STATION:
|
||||
case WID_RAT_BUILD_SIGNALS:
|
||||
|
@ -688,9 +695,14 @@ struct BuildRailToolbarWindow : Window {
|
|||
PlaceProc_DemolishArea(tile);
|
||||
break;
|
||||
|
||||
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);
|
||||
case WID_RAT_BUILD_DEPOT: {
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
|
||||
|
||||
ViewportPlaceMethod vpm = (DiagDirToAxis(_build_depot_direction) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED;
|
||||
VpStartPlaceSizing(tile, vpm, _remove_button_clicked ? DDSP_REMOVE_DEPOT : DDSP_BUILD_DEPOT);
|
||||
VpSetPlaceSizingLimit(_settings_game.depot.depot_spread);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RAT_BUILD_WAYPOINT:
|
||||
PlaceRail_Waypoint(tile);
|
||||
|
@ -786,6 +798,20 @@ struct BuildRailToolbarWindow : Window {
|
|||
}
|
||||
}
|
||||
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_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true);
|
||||
|
||||
if (this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true);
|
||||
|
||||
this->RaiseButtons();
|
||||
this->DisableWidget(WID_RAT_REMOVE);
|
||||
this->SetWidgetDirty(WID_RAT_REMOVE);
|
||||
|
@ -804,6 +832,7 @@ struct BuildRailToolbarWindow : Window {
|
|||
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_RAIL);
|
||||
CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_RAIL);
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
|
||||
CloseWindowByClass(WC_BUILD_BRIDGE);
|
||||
}
|
||||
|
||||
|
@ -815,8 +844,11 @@ struct BuildRailToolbarWindow : Window {
|
|||
|
||||
EventState OnCTRLStateChange() override
|
||||
{
|
||||
/* do not toggle Remove button by Ctrl when placing station */
|
||||
if (!this->IsWidgetLowered(WID_RAT_BUILD_STATION) && !this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT) && RailToolbar_CtrlChanged(this)) return ES_HANDLED;
|
||||
/* do not toggle Remove button by Ctrl when placing station or depot */
|
||||
if (!this->IsWidgetLowered(WID_RAT_BUILD_STATION) &&
|
||||
!this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT) &&
|
||||
!this->IsWidgetLowered(WID_RAT_BUILD_DEPOT) &&
|
||||
RailToolbar_CtrlChanged(this)) return ES_HANDLED;
|
||||
return ES_NOT_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -1705,6 +1737,12 @@ struct BuildRailDepotWindow : public PickerWindowBase {
|
|||
this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction);
|
||||
}
|
||||
|
||||
void Close([[maybe_unused]] int data = 0) override
|
||||
{
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
|
||||
this->PickerWindowBase::Close();
|
||||
}
|
||||
|
||||
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
|
||||
{
|
||||
if (!IsInsideMM(widget, WID_BRAD_DEPOT_NE, WID_BRAD_DEPOT_NW + 1)) return;
|
||||
|
@ -1734,6 +1772,7 @@ struct BuildRailDepotWindow : public PickerWindowBase {
|
|||
case WID_BRAD_DEPOT_SE:
|
||||
case WID_BRAD_DEPOT_SW:
|
||||
case WID_BRAD_DEPOT_NW:
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
|
||||
this->RaiseWidget(WID_BRAD_DEPOT_NE + _build_depot_direction);
|
||||
_build_depot_direction = (DiagDirection)(widget - WID_BRAD_DEPOT_NE);
|
||||
this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction);
|
||||
|
@ -1742,6 +1781,11 @@ struct BuildRailDepotWindow : public PickerWindowBase {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
|
||||
{
|
||||
CheckRedrawDepotHighlight(this, VEH_TRAIN);
|
||||
}
|
||||
};
|
||||
|
||||
/** Nested widget definition of the build rail depot window */
|
||||
|
|
13
src/road.h
13
src/road.h
|
@ -244,6 +244,19 @@ inline bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype)
|
|||
return HasBit(GetRoadTypeInfo(enginetype)->powered_roadtypes, tiletype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an engine with a given \a enginetype is powered on \a road_types.
|
||||
* This would normally just be an equality check,
|
||||
* but for electrified roads (which also support non-electric vehicles).
|
||||
* @param enginetype The RoadType of the engine we are considering.
|
||||
* @param rail_types The RoadTypes we are considering.
|
||||
* @return Whether the engine got power on this tile.
|
||||
*/
|
||||
static inline bool HasPowerOnRoads(RoadType enginetype, RoadTypes road_types)
|
||||
{
|
||||
return (GetRoadTypeInfo(enginetype)->powered_roadtypes & road_types) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cost of building the specified roadtype.
|
||||
* @param roadtype The roadtype being built.
|
||||
|
|
111
src/road_cmd.cpp
111
src/road_cmd.cpp
|
@ -1141,66 +1141,79 @@ std::tuple<CommandCost, Money> CmdRemoveLongRoad(DoCommandFlag flags, TileIndex
|
|||
* @param flags operation to perform
|
||||
* @param rt road type
|
||||
* @param dir entrance direction
|
||||
* @param adjacent allow adjacent depots
|
||||
* @param depot_id depot to join to
|
||||
* @param end_tile end tile of the depot to be built
|
||||
* @return the cost of this operation or an error
|
||||
*
|
||||
* @todo When checking for the tile slope,
|
||||
* distinguish between "Flat land required" and "land sloped in wrong direction"
|
||||
*/
|
||||
CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir)
|
||||
CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile)
|
||||
{
|
||||
if (!ValParamRoadType(rt) || !IsValidDiagDirection(dir)) return CMD_ERROR;
|
||||
|
||||
TileArea ta(tile, end_tile);
|
||||
assert(ta.w == 1 || ta.h == 1);
|
||||
|
||||
/* 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(tile);
|
||||
if (tileh != SLOPE_FLAT) {
|
||||
if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) {
|
||||
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
|
||||
Slope tileh = GetTileSlope(t);
|
||||
if (tileh != SLOPE_FLAT) {
|
||||
if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) {
|
||||
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
|
||||
}
|
||||
cost.AddCost(_price[PR_BUILD_FOUNDATION]);
|
||||
}
|
||||
|
||||
/* Check whether a depot tile exists and it needs to be rotated. */
|
||||
if (IsRoadDepotTile(t) &&
|
||||
GetDepotIndex(t) == join_to &&
|
||||
(HasRoadTypeTram(t) ? rt == GetRoadTypeTram(t) : rt == GetRoadTypeRoad(t))) {
|
||||
if (dir == GetRoadDepotDirection(t)) continue;
|
||||
|
||||
ret = EnsureNoVehicleOnGround(t);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
num_rotated_depot_tiles++;
|
||||
if (flags & DC_EXEC) {
|
||||
SetRoadDepotExitDirection(t, dir);
|
||||
MarkTileDirtyByTile(t);
|
||||
}
|
||||
|
||||
} else {
|
||||
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, t));
|
||||
if (cost.Failed()) return cost;
|
||||
|
||||
num_new_depot_tiles++;
|
||||
if (flags & DC_EXEC) {
|
||||
MakeRoadDepot(t, _current_company, depot->index, dir, rt);
|
||||
MarkTileDirtyByTile(t);
|
||||
}
|
||||
}
|
||||
cost.AddCost(_price[PR_BUILD_FOUNDATION]);
|
||||
}
|
||||
|
||||
/* Allow the user to rotate the depot instead of having to destroy it and build it again */
|
||||
bool rotate_existing_depot = false;
|
||||
if (IsRoadDepotTile(tile) && (HasRoadTypeTram(tile) ? rt == GetRoadTypeTram(tile) : rt == GetRoadTypeRoad(tile)))
|
||||
{
|
||||
CommandCost ret = CheckTileOwnership(tile);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
if (dir == GetRoadDepotDirection(tile)) return CommandCost();
|
||||
|
||||
ret = EnsureNoVehicleOnGround(tile);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
rotate_existing_depot = true;
|
||||
}
|
||||
|
||||
if (!rotate_existing_depot) {
|
||||
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile));
|
||||
if (cost.Failed()) return cost;
|
||||
|
||||
if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
|
||||
|
||||
if (!Depot::CanAllocateItem()) return CMD_ERROR;
|
||||
}
|
||||
if (num_new_depot_tiles + num_rotated_depot_tiles == 0) return CommandCost();
|
||||
|
||||
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. */
|
||||
UpdateCompanyRoadInfrastructure(rt, _current_company, num_new_depot_tiles * ROAD_DEPOT_TRACKBIT_FACTOR);
|
||||
|
||||
/* A road depot has two road bits. */
|
||||
UpdateCompanyRoadInfrastructure(rt, _current_company, ROAD_DEPOT_TRACKBIT_FACTOR);
|
||||
}
|
||||
|
||||
MarkTileDirtyByTile(tile);
|
||||
depot->AfterAddRemove(ta, true);
|
||||
if (join_to == NEW_DEPOT) MakeDefaultName(depot);
|
||||
}
|
||||
|
||||
cost.AddCost(_price[PR_BUILD_DEPOT_ROAD]);
|
||||
cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * (num_new_depot_tiles + num_rotated_depot_tiles));
|
||||
return cost;
|
||||
}
|
||||
|
||||
|
@ -1215,7 +1228,8 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags)
|
|||
if (ret.Failed()) return ret;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
Company *c = Company::GetIfValid(GetTileOwner(tile));
|
||||
Depot *depot = Depot::GetByTile(tile);
|
||||
Company *c = Company::GetIfValid(depot->owner);
|
||||
if (c != nullptr) {
|
||||
/* A road depot has two road bits. */
|
||||
RoadType rt = GetRoadTypeRoad(tile);
|
||||
|
@ -1224,8 +1238,8 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags)
|
|||
DirtyCompanyInfrastructureWindows(c->index);
|
||||
}
|
||||
|
||||
delete Depot::GetByTile(tile);
|
||||
DoClearSquare(tile);
|
||||
depot->AfterAddRemove(TileArea(tile), false);
|
||||
}
|
||||
|
||||
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]);
|
||||
|
@ -2081,7 +2095,7 @@ static bool ClickTile_Road(TileIndex tile)
|
|||
{
|
||||
if (!IsRoadDepot(tile)) return false;
|
||||
|
||||
ShowDepotWindow(tile, VEH_ROAD);
|
||||
ShowDepotWindow(GetDepotIndex(tile));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2269,7 +2283,7 @@ static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int,
|
|||
if (rv->Next() == nullptr) VehicleEnterDepot(rv->First());
|
||||
rv->tile = tile;
|
||||
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, rv->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile));
|
||||
return VETSB_ENTERED_WORMHOLE;
|
||||
}
|
||||
break;
|
||||
|
@ -2543,8 +2557,9 @@ CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_s
|
|||
|
||||
if (IsRoadDepotTile(tile)) {
|
||||
/* Update build vehicle window related to this depot */
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, tile);
|
||||
InvalidateWindowData(WC_BUILD_VEHICLE, tile);
|
||||
DepotID depot_id = GetDepotIndex(tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
|
||||
InvalidateWindowData(WC_BUILD_VEHICLE, depot_id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "direction_type.h"
|
||||
#include "road_type.h"
|
||||
#include "command_type.h"
|
||||
#include "depot_type.h"
|
||||
|
||||
enum RoadStopClassID : uint16_t;
|
||||
|
||||
|
@ -22,7 +23,7 @@ void UpdateNearestTownForRoadTiles(bool invalidate);
|
|||
CommandCost CmdBuildLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, DisallowedRoadDirections drd, bool start_half, bool end_half, bool is_ai);
|
||||
std::tuple<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 CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir);
|
||||
CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile);
|
||||
CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RoadType to_type);
|
||||
|
||||
DEF_CMD_TRAIT(CMD_BUILD_LONG_ROAD, CmdBuildLongRoad, CMD_AUTO | CMD_NO_WATER | CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
|
@ -33,7 +34,7 @@ DEF_CMD_TRAIT(CMD_CONVERT_ROAD, CmdConvertRoad, 0,
|
|||
|
||||
CommandCallback CcPlaySound_CONSTRUCTION_OTHER;
|
||||
CommandCallback CcBuildRoadTunnel;
|
||||
void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex tile, RoadType rt, DiagDirection dir);
|
||||
void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex start_tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile);
|
||||
void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8_t width, uint8_t length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, RoadStopClassID spec_class, uint16_t spec_index, StationID, bool);
|
||||
|
||||
#endif /* ROAD_CMD_H */
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "picker_gui.h"
|
||||
#include "timer/timer.h"
|
||||
#include "timer/timer_game_calendar.h"
|
||||
#include "depot_func.h"
|
||||
|
||||
#include "widgets/road_widget.h"
|
||||
|
||||
|
@ -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 (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
|
||||
if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, start_tile);
|
||||
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
|
||||
ConnectRoadToStructure(tile, dir);
|
||||
|
||||
TileArea ta(start_tile, end_tile);
|
||||
for (TileIndex tile : ta) {
|
||||
ConnectRoadToStructure(tile, dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 (_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();
|
||||
}
|
||||
|
||||
|
@ -636,8 +642,10 @@ struct BuildRoadToolbarWindow : Window {
|
|||
break;
|
||||
|
||||
case WID_ROT_DEPOT:
|
||||
Command<CMD_BUILD_ROAD_DEPOT>::Post(this->rti->strings.err_depot, CcRoadDepot,
|
||||
tile, _cur_roadtype, _road_depot_orientation);
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
|
||||
|
||||
VpSetPlaceSizingLimit(_settings_game.depot.depot_spread);
|
||||
VpStartPlaceSizing(tile, (DiagDirToAxis(_road_depot_orientation) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_DEPOT);
|
||||
break;
|
||||
|
||||
case WID_ROT_BUILD_WAYPOINT:
|
||||
|
@ -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_NORMAL && this->IsWidgetLowered(WID_ROT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true);
|
||||
|
||||
this->RaiseButtons();
|
||||
this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
|
||||
this->SetWidgetDirty(WID_ROT_REMOVE);
|
||||
|
@ -687,6 +697,7 @@ struct BuildRoadToolbarWindow : Window {
|
|||
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD);
|
||||
CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_ROAD);
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
|
||||
CloseWindowByClass(WC_BUILD_BRIDGE);
|
||||
}
|
||||
|
||||
|
@ -805,6 +816,18 @@ struct BuildRoadToolbarWindow : Window {
|
|||
}
|
||||
break;
|
||||
|
||||
case DDSP_BUILD_DEPOT: {
|
||||
bool adjacent = _ctrl_pressed;
|
||||
StringID error_string = this->rti->strings.err_depot;
|
||||
|
||||
auto proc = [=](DepotID join_to) -> bool {
|
||||
return Command<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:
|
||||
Command<CMD_CONVERT_ROAD>::Post(rti->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype);
|
||||
break;
|
||||
|
@ -1106,6 +1129,12 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
|
|||
this->FinishInitNested(TRANSPORT_ROAD);
|
||||
}
|
||||
|
||||
void Close([[maybe_unused]] int data = 0) override
|
||||
{
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
|
||||
this->PickerWindowBase::Close();
|
||||
}
|
||||
|
||||
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
|
||||
{
|
||||
if (!IsInsideMM(widget, WID_BROD_DEPOT_NE, WID_BROD_DEPOT_NW + 1)) return;
|
||||
|
@ -1135,6 +1164,7 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
|
|||
case WID_BROD_DEPOT_NE:
|
||||
case WID_BROD_DEPOT_SW:
|
||||
case WID_BROD_DEPOT_SE:
|
||||
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
|
||||
this->RaiseWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
|
||||
_road_depot_orientation = (DiagDirection)(widget - WID_BROD_DEPOT_NE);
|
||||
this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
|
||||
|
@ -1146,6 +1176,11 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
|
||||
{
|
||||
CheckRedrawDepotHighlight(this, VEH_ROAD);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr NWidgetPart _nested_build_road_depot_widgets[] = {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "framerate_type.h"
|
||||
#include "roadveh_cmd.h"
|
||||
#include "road_cmd.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -250,6 +251,41 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length)
|
|||
v->vcache.cached_max_speed = (max_speed != 0) ? max_speed * 4 : RoadVehInfo(v->engine_type)->max_speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an adequate tile for placing an engine.
|
||||
* @param[in,out] tile A tile of the depot.
|
||||
* @param e Engine to be built.
|
||||
* @param flags Flags of the command.
|
||||
* @return CommandCost() or an error message if the depot is not appropriate.
|
||||
*/
|
||||
CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, const Engine *e, DoCommandFlag flags)
|
||||
{
|
||||
assert(IsRoadDepotTile(tile));
|
||||
|
||||
Depot *dep = Depot:: GetByTile(tile);
|
||||
|
||||
/* Check that the vehicle can drive on some tile of the depot */
|
||||
RoadType rt = e->u.road.roadtype;
|
||||
const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
|
||||
if ((dep->r_types.road_types & rti->powered_roadtypes) == 0) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
|
||||
|
||||
/* Use same tile if possible when replacing. */
|
||||
if (flags & DC_AUTOREPLACE) {
|
||||
/* Use same tile if possible when replacing. */
|
||||
if (HasTileAnyRoadType(tile, rti->powered_roadtypes)) return CommandCost();
|
||||
}
|
||||
|
||||
tile = INVALID_TILE;
|
||||
for (auto &depot_tile : dep->depot_tiles) {
|
||||
if (!HasTileAnyRoadType(depot_tile, rti->powered_roadtypes)) continue;
|
||||
tile = depot_tile;
|
||||
break;
|
||||
}
|
||||
|
||||
assert(tile != INVALID_TILE);
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a road vehicle.
|
||||
* @param flags type of operation.
|
||||
|
@ -260,10 +296,12 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length)
|
|||
*/
|
||||
CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
|
||||
{
|
||||
/* Check that the vehicle can drive on the road in question */
|
||||
assert(IsRoadDepotTile(tile));
|
||||
RoadType rt = e->u.road.roadtype;
|
||||
const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
|
||||
if (!HasTileAnyRoadType(tile, rti->powered_roadtypes)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
|
||||
|
||||
CommandCost check = FindDepotTileForPlacingEngine(tile, e, flags);
|
||||
if (check.Failed()) return check;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
const RoadVehicleInfo *rvi = &e->u.road;
|
||||
|
@ -1018,7 +1056,9 @@ bool RoadVehLeaveDepot(RoadVehicle *v, bool first)
|
|||
|
||||
if (first) {
|
||||
/* We are leaving a depot, but have to go to the exact same one; re-enter */
|
||||
if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
|
||||
if (v->current_order.IsType(OT_GOTO_DEPOT) &&
|
||||
IsRoadDepotTile(v->tile) &&
|
||||
v->current_order.GetDestination() == GetDepotIndex(v->tile)) {
|
||||
VehicleEnterDepot(v);
|
||||
return true;
|
||||
}
|
||||
|
@ -1043,7 +1083,7 @@ bool RoadVehLeaveDepot(RoadVehicle *v, bool first)
|
|||
v->UpdatePosition();
|
||||
v->UpdateInclination(true, true);
|
||||
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -294,6 +294,10 @@ static void InitializeWindowsAndCaches()
|
|||
}
|
||||
}
|
||||
|
||||
for (Depot *dep : Depot::Iterate()) {
|
||||
dep->RescanDepotTiles();
|
||||
}
|
||||
|
||||
RecomputePrices();
|
||||
|
||||
GroupStatistics::UpdateAfterLoad();
|
||||
|
@ -795,6 +799,16 @@ bool AfterLoadGame()
|
|||
_settings_game.linkgraph.recalc_time *= CalendarTime::SECONDS_PER_DAY;
|
||||
}
|
||||
|
||||
if (IsSavegameVersionBefore(SLV_DEPOT_SPREAD)) {
|
||||
_settings_game.depot.depot_spread = DEF_MAX_DEPOT_SPREAD;
|
||||
_settings_game.depot.distant_join_depots = true;
|
||||
}
|
||||
|
||||
if (IsSavegameVersionBefore(SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS)) {
|
||||
_settings_game.depot.allow_no_comp_railtype_replacements = false;
|
||||
_settings_game.depot.allow_no_comp_roadtype_replacements = false;
|
||||
}
|
||||
|
||||
/* Load the sprites */
|
||||
GfxLoadSprites();
|
||||
LoadStringWidthTable();
|
||||
|
@ -2425,28 +2439,6 @@ bool AfterLoadGame()
|
|||
for (Depot *d : Depot::Iterate()) d->build_date = TimerGameCalendar::date;
|
||||
}
|
||||
|
||||
/* In old versions it was possible to remove an airport while a plane was
|
||||
* taking off or landing. This gives all kind of problems when building
|
||||
* another airport in the same station so we don't allow that anymore.
|
||||
* For old savegames with such aircraft we just throw them in the air and
|
||||
* treat the aircraft like they were flying already. */
|
||||
if (IsSavegameVersionBefore(SLV_146)) {
|
||||
for (Aircraft *v : Aircraft::Iterate()) {
|
||||
if (!v->IsNormalAircraft()) continue;
|
||||
Station *st = GetTargetAirportIfValid(v);
|
||||
if (st == nullptr && v->state != FLYING) {
|
||||
v->state = FLYING;
|
||||
UpdateAircraftCache(v);
|
||||
AircraftNextAirportPos_and_Order(v);
|
||||
/* get aircraft back on running altitude */
|
||||
if ((v->vehstatus & VS_CRASHED) == 0) {
|
||||
GetAircraftFlightLevelBounds(v, &v->z_pos, nullptr);
|
||||
SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlightLevel(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move the animation frame to the same location (m7) for all objects. */
|
||||
if (IsSavegameVersionBefore(SLV_147)) {
|
||||
for (auto t : Map::Iterate()) {
|
||||
|
@ -2792,6 +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. */
|
||||
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;
|
||||
|
|
|
@ -108,6 +108,7 @@ const SaveLoadCompat _station_normal_sl_compat[] = {
|
|||
SLC_VAR("airport.layout"),
|
||||
SLC_VAR("airport.flags"),
|
||||
SLC_VAR("airport.rotation"),
|
||||
SLC_VAR("airport.hangar"),
|
||||
SLC_VAR("storage"),
|
||||
SLC_VAR("airport.psa"),
|
||||
SLC_VAR("indtype"),
|
||||
|
|
|
@ -27,6 +27,12 @@ static const SaveLoad _depot_desc[] = {
|
|||
SLE_CONDVAR(Depot, town_cn, SLE_UINT16, SLV_141, SL_MAX_VERSION),
|
||||
SLE_CONDSSTR(Depot, name, SLE_STR, SLV_141, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Depot, build_date, SLE_INT32, SLV_142, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Depot, owner, SLE_UINT8, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Depot, veh_type, SLE_UINT8, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Depot, ta.tile, SLE_UINT32, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Depot, ta.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Depot, ta.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
|
||||
SLE_CONDREF(Depot, station, REF_STATION, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
|
||||
};
|
||||
|
||||
struct DEPTChunkHandler : ChunkHandler {
|
||||
|
|
|
@ -690,6 +690,8 @@ static bool LoadOldDepot(LoadgameState *ls, int num)
|
|||
if (d->xy != 0) {
|
||||
d->town = RemapTown(d->xy);
|
||||
} else {
|
||||
d->owner = INVALID_OWNER;
|
||||
d->veh_type = VEH_INVALID;
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "../order_backup.h"
|
||||
#include "../settings_type.h"
|
||||
#include "../network/network.h"
|
||||
#include "../depot_map.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
|
@ -243,11 +244,14 @@ struct ORDLChunkHandler : ChunkHandler {
|
|||
}
|
||||
};
|
||||
|
||||
static TileIndex _tile;
|
||||
|
||||
SaveLoadTable GetOrderBackupDescription()
|
||||
{
|
||||
static const SaveLoad _order_backup_desc[] = {
|
||||
SLE_VAR(OrderBackup, user, SLE_UINT32),
|
||||
SLE_VAR(OrderBackup, tile, SLE_UINT32),
|
||||
SLEG_CONDVAR("tile", _tile, SLE_UINT32, SL_MIN_VERSION, SLV_DEPOTID_BACKUP_ORDERS),
|
||||
SLE_CONDVAR(OrderBackup, depot_id, SLE_UINT16, SLV_DEPOTID_BACKUP_ORDERS, SL_MAX_VERSION),
|
||||
SLE_VAR(OrderBackup, group, SLE_UINT16),
|
||||
SLE_CONDVAR(OrderBackup, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SL_MIN_VERSION, SLV_192),
|
||||
SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, SLV_192, SL_MAX_VERSION),
|
||||
|
@ -297,6 +301,8 @@ struct BKORChunkHandler : ChunkHandler {
|
|||
OrderBackup *ob = new (index) OrderBackup();
|
||||
SlObject(ob, slt);
|
||||
}
|
||||
|
||||
if (IsSavegameVersionBefore(SLV_DEPOTID_BACKUP_ORDERS)) _order_backup_pool.CleanPool();
|
||||
}
|
||||
|
||||
void FixPointers() const override
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "../stdafx.h"
|
||||
#include "../debug.h"
|
||||
#include "../station_base.h"
|
||||
#include "../depot_base.h"
|
||||
#include "../thread.h"
|
||||
#include "../town.h"
|
||||
#include "../network/network.h"
|
||||
|
@ -1121,6 +1122,7 @@ static size_t ReferenceToInt(const void *obj, SLRefType rt)
|
|||
case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1;
|
||||
case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1;
|
||||
case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1;
|
||||
case REF_DEPOT: return ((const Depot*)obj)->index + 1;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
@ -1202,6 +1204,10 @@ static void *IntToReference(size_t index, SLRefType rt)
|
|||
if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
|
||||
SlErrorCorrupt("Referencing invalid LinkGraphJob");
|
||||
|
||||
case REF_DEPOT:
|
||||
if (Depot::IsValidID(index)) return Depot::Get(index);
|
||||
SlErrorCorrupt("Referencing invalid Depot");
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -383,6 +383,13 @@ enum SaveLoadVersion : uint16_t {
|
|||
SLV_GROUP_NUMBERS, ///< 336 PR#12297 Add per-company group numbers.
|
||||
SLV_INCREASE_STATION_TYPE_FIELD_SIZE, ///< 337 PR#12572 Increase size of StationType field in map array
|
||||
SLV_ROAD_WAYPOINTS, ///< 338 PR#12572 Road waypoints
|
||||
SLV_ADD_DEPOTS_TO_HANGARS, ///< 339 PR#10691 Add depots to airports that have a hangar.
|
||||
|
||||
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
|
||||
};
|
||||
|
@ -598,6 +605,7 @@ enum SLRefType {
|
|||
REF_STORAGE = 9, ///< Load/save a reference to a persistent storage.
|
||||
REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph.
|
||||
REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job.
|
||||
REF_DEPOT = 12, ///< Load/save a reference to a depot.
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -610,6 +610,7 @@ public:
|
|||
SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION),
|
||||
SLE_VAR(Station, airport.flags, SLE_UINT64),
|
||||
SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION),
|
||||
SLE_CONDREF(Station, airport.hangar, REF_DEPOT, SLV_ADD_DEPOTS_TO_HANGARS, SL_MAX_VERSION),
|
||||
SLEG_CONDARR("storage", _old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161),
|
||||
SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION),
|
||||
|
||||
|
|
|
@ -17,33 +17,16 @@
|
|||
ScriptDepotList::ScriptDepotList(ScriptTile::TransportType transport_type)
|
||||
{
|
||||
EnforceDeityOrCompanyModeValid_Void();
|
||||
::TileType tile_type;
|
||||
switch (transport_type) {
|
||||
default: return;
|
||||
static_assert(VEH_TRAIN == (int)ScriptTile::TRANSPORT_RAIL);
|
||||
static_assert(VEH_ROAD == (int)ScriptTile::TRANSPORT_ROAD);
|
||||
static_assert(VEH_SHIP == (int)ScriptTile::TRANSPORT_WATER);
|
||||
|
||||
case ScriptTile::TRANSPORT_ROAD: tile_type = ::MP_ROAD; break;
|
||||
case ScriptTile::TRANSPORT_RAIL: tile_type = ::MP_RAILWAY; break;
|
||||
case ScriptTile::TRANSPORT_WATER: tile_type = ::MP_WATER; break;
|
||||
|
||||
case ScriptTile::TRANSPORT_AIR: {
|
||||
/* Hangars are not seen as real depots by the depot code. */
|
||||
bool is_deity = ScriptCompanyMode::IsDeity();
|
||||
CompanyID owner = ScriptObject::GetCompany();
|
||||
for (const Station *st : Station::Iterate()) {
|
||||
if (is_deity || st->owner == owner) {
|
||||
for (uint i = 0; i < st->airport.GetNumHangars(); i++) {
|
||||
this->AddItem(st->airport.GetHangarTile(i).base());
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle 'standard' depots. */
|
||||
bool is_deity = ScriptCompanyMode::IsDeity();
|
||||
CompanyID owner = ScriptObject::GetCompany();
|
||||
for (const Depot *depot : Depot::Iterate()) {
|
||||
if ((is_deity || ::GetTileOwner(depot->xy) == owner) && ::IsTileType(depot->xy, tile_type)) this->AddItem(depot->xy.base());
|
||||
if (depot->veh_type != (VehicleType)transport_type ||
|
||||
(!is_deity && ::GetTileOwner(depot->xy) != owner)) continue;
|
||||
|
||||
this->AddItem(depot->xy.base());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
EnforcePrecondition(false, ::IsValidTile(front));
|
||||
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)
|
||||
|
|
|
@ -245,18 +245,13 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
|
|||
|
||||
const Order *order = ::ResolveOrder(vehicle_id, order_position);
|
||||
if (order == nullptr || order->GetType() == OT_CONDITIONAL) return INVALID_TILE;
|
||||
const Vehicle *v = ::Vehicle::Get(vehicle_id);
|
||||
|
||||
switch (order->GetType()) {
|
||||
case OT_GOTO_DEPOT: {
|
||||
/* We don't know where the nearest depot is... (yet) */
|
||||
if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) return INVALID_TILE;
|
||||
|
||||
if (v->type != VEH_AIRCRAFT) return ::Depot::Get(order->GetDestination())->xy;
|
||||
/* Aircraft's hangars are referenced by StationID, not DepotID */
|
||||
const Station *st = ::Station::Get(order->GetDestination());
|
||||
if (!st->airport.HasHangar()) return INVALID_TILE;
|
||||
return st->airport.GetHangarTile(0);
|
||||
return ::Depot::Get(order->GetDestination())->xy;
|
||||
}
|
||||
|
||||
case OT_GOTO_STATION: {
|
||||
|
|
|
@ -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);
|
||||
|
||||
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)
|
||||
|
|
|
@ -240,6 +240,18 @@ public:
|
|||
*/
|
||||
static bool BuildRailDepot(TileIndex tile, TileIndex front);
|
||||
|
||||
/**
|
||||
* Removes rail depots from an area.
|
||||
* @param start_tile Start tile of the area.
|
||||
* @param end_tile End tile of the area.
|
||||
* @pre ScriptMap::IsValidTile(start_tile).
|
||||
* @pre ScriptMap::IsValidTile(end_tile).
|
||||
* @game @pre Valid ScriptCompanyMode active in scope.
|
||||
* @exception ScriptError::ERR_FLAT_LAND_REQUIRED
|
||||
* @return Whether all depot tiles of the owner in the area have been/can be cleared or not.
|
||||
*/
|
||||
static bool RemoveRailDepot(TileIndex start_tile, TileIndex end_tile);
|
||||
|
||||
/**
|
||||
* Build a rail station.
|
||||
* @param tile Place to build the station.
|
||||
|
|
|
@ -535,7 +535,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD
|
|||
|
||||
DiagDirection entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? DIAGDIR_SE : DIAGDIR_NW) : (::TileX(tile) < ::TileX(front) ? DIAGDIR_SW : DIAGDIR_NE);
|
||||
|
||||
return ScriptObject::Command<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)
|
||||
|
|
|
@ -2145,6 +2145,15 @@ static SettingsContainer &GetSettingsTree()
|
|||
|
||||
SettingsPage *limitations = main->Add(new SettingsPage(STR_CONFIG_SETTING_LIMITATIONS));
|
||||
{
|
||||
SettingsPage *depots = limitations->Add(new SettingsPage(STR_CONFIG_SETTING_DEPOTS));
|
||||
{
|
||||
depots->Add(new SettingEntry("depot.depot_spread"));
|
||||
depots->Add(new SettingEntry("depot.distant_join_depots"));
|
||||
|
||||
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.autoslope"));
|
||||
limitations->Add(new SettingEntry("construction.extra_dynamite"));
|
||||
|
|
|
@ -380,6 +380,22 @@ static void SpriteZoomMinChanged(int32_t)
|
|||
MarkWholeScreenDirty();
|
||||
}
|
||||
|
||||
static bool CheckDifferentRailRoadTypesReplacements(int32_t &new_value)
|
||||
{
|
||||
if (_game_mode == GM_NORMAL) {
|
||||
if (new_value == 0) {
|
||||
ShowErrorMessage(STR_CONFIG_SETTING_REPLACEMENTS_DIFF_TYPE, INVALID_STRING_ID, WL_ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void InvalidateReplacementWindows(int32_t)
|
||||
{
|
||||
InvalidateWindowClassesData(WC_REPLACE_VEHICLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any possible saveload window and delete any newgrf dialogue as
|
||||
* its widget parts might change. Reinit all windows as it allows access to the
|
||||
|
|
|
@ -570,6 +570,15 @@ struct StationSettings {
|
|||
uint8_t station_spread; ///< amount a station may spread
|
||||
};
|
||||
|
||||
/** Settings related to depots. */
|
||||
struct DepotSettings {
|
||||
uint8_t depot_spread; ///< amount a depot may spread
|
||||
bool distant_join_depots; ///< allow to join non-adjacent depots
|
||||
|
||||
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. */
|
||||
struct VehicleDefaultSettings {
|
||||
bool servint_ispercent; ///< service intervals are in percents
|
||||
|
@ -603,6 +612,7 @@ struct GameSettings {
|
|||
EconomySettings economy; ///< settings to change the economy
|
||||
LinkGraphSettings linkgraph; ///< settings for link graph calculations
|
||||
StationSettings station; ///< settings related to station management
|
||||
DepotSettings depot; ///< settings related to depot management
|
||||
LocaleSettings locale; ///< settings related to used currency/unit system in the current game
|
||||
};
|
||||
|
||||
|
|
|
@ -187,14 +187,14 @@ static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
|
|||
const Depot *best_depot = nullptr;
|
||||
uint best_dist_sq = std::numeric_limits<uint>::max();
|
||||
for (const Depot *depot : Depot::Iterate()) {
|
||||
if (depot->veh_type != VEH_SHIP || depot->owner != v->owner) continue;
|
||||
|
||||
const TileIndex tile = depot->xy;
|
||||
if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
|
||||
const uint dist_sq = DistanceSquare(tile, v->tile);
|
||||
if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance &&
|
||||
visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) {
|
||||
best_dist_sq = dist_sq;
|
||||
best_depot = depot;
|
||||
}
|
||||
const uint dist_sq = DistanceSquare(tile, v->tile);
|
||||
if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance &&
|
||||
visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) {
|
||||
best_dist_sq = dist_sq;
|
||||
best_depot = depot;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +222,7 @@ static void CheckIfShipNeedsService(Vehicle *v)
|
|||
}
|
||||
|
||||
v->current_order.MakeGoToDepot(depot->index, ODTFB_SERVICE);
|
||||
v->SetDestTile(depot->xy);
|
||||
v->SetDestTile(depot->GetBestDepotTile(v));
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
|
||||
}
|
||||
|
||||
|
@ -424,12 +424,13 @@ static bool CheckShipLeaveDepot(Ship *v)
|
|||
|
||||
v->cur_speed = 0;
|
||||
v->UpdateViewport(true, true);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
|
||||
DepotID depot_id = GetDepotIndex(v->tile);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
|
||||
|
||||
VehicleServiceInDepot(v);
|
||||
v->LeaveUnbunchingDepot();
|
||||
v->PlayLeaveStationSound();
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
|
||||
SetWindowClassesDirty(WC_SHIPS_LIST);
|
||||
|
||||
return false;
|
||||
|
@ -961,5 +962,5 @@ ClosestDepot Ship::FindClosestDepot()
|
|||
const Depot *depot = FindClosestShipDepot(this, MAX_SHIP_DEPOT_SEARCH_DISTANCE);
|
||||
if (depot == nullptr) return ClosestDepot();
|
||||
|
||||
return ClosestDepot(depot->xy, depot->index);
|
||||
return ClosestDepot(depot->GetBestDepotTile(this), depot->index);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "core/random_func.hpp"
|
||||
#include "linkgraph/linkgraph.h"
|
||||
#include "linkgraph/linkgraphschedule.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -727,6 +728,52 @@ Money AirportMaintenanceCost(Owner owner)
|
|||
return total_cost >> 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hangar on the airport.
|
||||
*/
|
||||
void Airport::AddHangar()
|
||||
{
|
||||
assert(this->hangar == nullptr);
|
||||
assert(Depot::CanAllocateItem());
|
||||
assert(this->GetNumHangars() > 0);
|
||||
Station *st = Station::GetByTile(this->GetHangarTile(0));
|
||||
this->hangar = new Depot(this->GetHangarTile(0), 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
|
||||
{
|
||||
return lhs->index < rhs->index;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "linkgraph/linkgraph_type.h"
|
||||
#include "newgrf_storage.h"
|
||||
#include "bitmap_type.h"
|
||||
#include "depot_type.h"
|
||||
|
||||
static const uint8_t INITIAL_STATION_RATING = 175;
|
||||
static const uint8_t MAX_STATION_RATING = 255;
|
||||
|
@ -294,6 +295,7 @@ struct Airport : public TileArea {
|
|||
uint8_t type; ///< Type of this airport, @see AirportTypes
|
||||
uint8_t layout; ///< Airport layout number.
|
||||
Direction rotation; ///< How this airport is rotated.
|
||||
Depot *hangar; ///< The corresponding hangar of this airport, if any.
|
||||
|
||||
PersistentStorage *psa; ///< Persistent storage for NewGRF airports.
|
||||
|
||||
|
@ -404,6 +406,9 @@ struct Airport : public TileArea {
|
|||
return num;
|
||||
}
|
||||
|
||||
void AddHangar();
|
||||
void RemoveHangar();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Retrieve hangar information of a hangar at a given tile.
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
#include "timer/timer_game_tick.h"
|
||||
#include "cheat_type.h"
|
||||
#include "road_func.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "widgets/station_widget.h"
|
||||
|
||||
|
@ -2539,6 +2540,8 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, uint8_t airport
|
|||
|
||||
/* Check if a valid, buildable airport was chosen for construction */
|
||||
const AirportSpec *as = AirportSpec::Get(airport_type);
|
||||
|
||||
if (!as->depots.empty() && !Depot::CanAllocateItem()) return CMD_ERROR;
|
||||
if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
|
||||
if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
|
||||
|
||||
|
@ -2632,6 +2635,8 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, uint8_t airport
|
|||
AirportTileAnimationTrigger(st, iter, AAT_BUILT);
|
||||
}
|
||||
|
||||
if (!as->depots.empty()) st->airport.AddHangar();
|
||||
|
||||
UpdateAirplanesOnNewStation(st);
|
||||
|
||||
Company::Get(st->owner)->infrastructure.airport++;
|
||||
|
@ -2674,11 +2679,7 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags)
|
|||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
|
||||
TileIndex tile_cur = st->airport.GetHangarTile(i);
|
||||
OrderBackup::Reset(tile_cur, false);
|
||||
CloseWindowById(WC_VEHICLE_DEPOT, tile_cur);
|
||||
}
|
||||
st->airport.RemoveHangar();
|
||||
|
||||
/* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
|
||||
* And as for construction, always remove it, even if the setting is not set, in order to avoid the
|
||||
|
@ -3692,8 +3693,8 @@ static bool ClickTile_Station(TileIndex tile)
|
|||
if (bst->facilities & FACIL_WAYPOINT) {
|
||||
ShowWaypointWindow(Waypoint::From(bst));
|
||||
} else if (IsHangar(tile)) {
|
||||
const Station *st = Station::From(bst);
|
||||
ShowDepotWindow(st->airport.GetHangarTile(st->airport.GetHangarNum(tile)), VEH_AIRCRAFT);
|
||||
assert(Station::From(bst)->airport.HasHangar());
|
||||
ShowDepotWindow(Station::From(bst)->airport.hangar->index);
|
||||
} else {
|
||||
ShowStationViewWindow(bst->index);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ static bool CheckRoadSide(int32_t &new_value);
|
|||
static bool CheckDynamicEngines(int32_t &new_value);
|
||||
static void StationCatchmentChanged(int32_t new_value);
|
||||
static void MaxVehiclesChanged(int32_t new_value);
|
||||
static bool CheckDifferentRailRoadTypesReplacements(int32_t &new_value);
|
||||
static void InvalidateReplacementWindows(int32_t new_value);
|
||||
|
||||
static const SettingVariant _game_settings_table[] = {
|
||||
[post-amble]
|
||||
|
@ -145,6 +147,47 @@ str = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS
|
|||
strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS_HELPTEXT
|
||||
post_cb = [](auto) { CloseWindowById(WC_SELECT_STATION, 0); }
|
||||
|
||||
[SDT_BOOL]
|
||||
var = depot.distant_join_depots
|
||||
from = SLV_DEPOT_SPREAD
|
||||
def = true
|
||||
str = STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS
|
||||
strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS_HELPTEXT
|
||||
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]
|
||||
var = vehicle.road_side
|
||||
type = SLE_UINT8
|
||||
|
|
|
@ -26,6 +26,8 @@ void VpStartDragging(ViewportDragDropSelectionProcess process);
|
|||
void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process);
|
||||
void VpSetPresizeRange(TileIndex from, TileIndex to);
|
||||
void VpSetPlaceSizingLimit(int limit);
|
||||
void VpSetPlaceFixedSize(uint8_t fixed_size);
|
||||
void VpResetFixedSize();
|
||||
|
||||
void UpdateTileSelection();
|
||||
|
||||
|
|
|
@ -55,11 +55,12 @@ struct TileHighlightData {
|
|||
Point new_pos; ///< New value for \a pos; used to determine whether to redraw the selection.
|
||||
Point new_size; ///< New value for \a size; used to determine whether to redraw the selection.
|
||||
Point new_outersize; ///< New value for \a outersize; used to determine whether to redraw the selection.
|
||||
uint8_t dirty; ///< Whether the build station window needs to redraw due to the changed selection.
|
||||
uint8_t dirty; ///< Whether the build station window needs to redraw due to the changed selection.
|
||||
|
||||
Point selstart; ///< The location where the dragging started.
|
||||
Point selend; ///< The location where the drag currently ends.
|
||||
uint8_t sizelimit; ///< Whether the selection is limited in length, and what the maximum length is.
|
||||
uint8_t sizelimit; ///< Whether the selection is limited in length, and what the maximum length is.
|
||||
uint8_t fixed_size; ///< The fixed length for one of the sides.
|
||||
|
||||
HighLightStyle drawstyle; ///< Lower bits 0-3 are reserved for detailed highlight information.
|
||||
HighLightStyle next_drawstyle; ///< Queued, but not yet drawn style.
|
||||
|
|
|
@ -353,4 +353,6 @@ protected: // These functions should not be called outside acceleration code.
|
|||
}
|
||||
};
|
||||
|
||||
bool HasCompatibleDepotTile(TileIndex tile, const Train *t);
|
||||
|
||||
#endif /* TRAIN_H */
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
/** @file train_cmd.cpp Handling of trains. */
|
||||
|
||||
#include "depot_map.h"
|
||||
#include "stdafx.h"
|
||||
#include "error.h"
|
||||
#include "articulated_vehicles.h"
|
||||
|
@ -38,6 +39,7 @@
|
|||
#include "misc_cmd.h"
|
||||
#include "timer/timer_game_calendar.h"
|
||||
#include "timer/timer_game_economy.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "table/train_sprites.h"
|
||||
|
@ -604,6 +606,65 @@ void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a train chain is compatible with a depot tile.
|
||||
* @param tile Tile to check.
|
||||
* @param t Train chain to check.
|
||||
* @return Whether the full train chain is compatible with this tile.
|
||||
*/
|
||||
bool IsVehicleCompatibleWithDepotTile(TileIndex tile, const Train *t)
|
||||
{
|
||||
assert(IsRailDepotTile(tile));
|
||||
for (const Train *u = t; u != nullptr; u = u->Next()) {
|
||||
RailType rail_type = Engine::Get(u->engine_type)->u.rail.railtype;
|
||||
if (!IsCompatibleRail(rail_type, GetRailType(tile))) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a depot has a tile where a train chain can be stored.
|
||||
* @param tile A tile of the depot.
|
||||
* @param t The train to check.
|
||||
* @return True iff the depot has a tile compatible with the chain.
|
||||
*/
|
||||
bool HasCompatibleDepotTile(TileIndex tile, const Train *t)
|
||||
{
|
||||
assert(IsRailDepotTile(tile));
|
||||
Depot *dep = Depot::GetByTile(tile);
|
||||
|
||||
for (auto &depot_tile : dep->depot_tiles) {
|
||||
if (IsVehicleCompatibleWithDepotTile(depot_tile, t)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a tile of a depot compatible with the rail type of a rail vehicle.
|
||||
* @param depot_id Index of the depot.
|
||||
* @param rail_type Rail type of the new vehicle.
|
||||
* @param is_engine Whether the vehicle is an engine.
|
||||
* @return A compatible tile of the depot or INVALID_TILE if no compatible tile is found.
|
||||
*/
|
||||
TileIndex FindCompatibleDepotTile(DepotID depot_id, RailType rail_type, bool is_engine)
|
||||
{
|
||||
assert(Depot::IsValidID(depot_id));
|
||||
Depot *depot = Depot::Get(depot_id);
|
||||
|
||||
for (auto &dep_tile : depot->depot_tiles) {
|
||||
if (is_engine) {
|
||||
if (HasPowerOnRail(rail_type, GetRailType(dep_tile))) return dep_tile;
|
||||
} else {
|
||||
if (IsCompatibleRail(rail_type, GetRailType(dep_tile))) return dep_tile;
|
||||
}
|
||||
}
|
||||
|
||||
return INVALID_TILE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a railroad wagon.
|
||||
* @param flags type of operation.
|
||||
|
@ -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)
|
||||
{
|
||||
const RailVehicleInfo *rvi = &e->u.rail;
|
||||
assert(IsRailDepotTile(tile));
|
||||
DepotID depot_id = GetDepotIndex(tile);
|
||||
|
||||
/* Check that the wagon can drive on the track in question */
|
||||
if (!IsCompatibleRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR;
|
||||
/* Find a good tile to place the wagon. */
|
||||
tile = FindCompatibleDepotTile(depot_id, rvi->railtype, false);
|
||||
if (tile == INVALID_TILE) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
Train *v = new Train();
|
||||
|
@ -645,7 +709,7 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const
|
|||
v->SetWagon();
|
||||
|
||||
v->SetFreeWagon();
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
|
||||
|
||||
v->cargo_type = e->GetDefaultCargoType();
|
||||
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. */
|
||||
for (Train *w : Train::Iterate()) {
|
||||
if (w->tile == tile && ///< Same depot
|
||||
if (!IsRailDepotTile(w->tile)) continue;
|
||||
if (GetDepotIndex(w->tile) == depot_id && ///< Same depot
|
||||
w->IsFreeWagon() && ///< A free wagon chain
|
||||
w->engine_type == e->index && ///< Same type
|
||||
w->First() != v && ///< Don't connect to ourself
|
||||
|
@ -692,9 +757,10 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const
|
|||
void NormalizeTrainVehInDepot(const Train *u)
|
||||
{
|
||||
assert(u->IsEngine());
|
||||
DepotID dep_id = GetDepotIndex(u->tile);
|
||||
for (const Train *v : Train::Iterate()) {
|
||||
if (v->IsFreeWagon() && v->tile == u->tile &&
|
||||
v->track == TRACK_BIT_DEPOT) {
|
||||
if (v->IsFreeWagon() && v->IsInDepot() &&
|
||||
GetDepotIndex(v->tile) == dep_id) {
|
||||
if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(DC_EXEC, v->index, u->index, true).Failed()) {
|
||||
break;
|
||||
}
|
||||
|
@ -748,13 +814,14 @@ static void AddRearEngineToMultiheadedTrain(Train *v)
|
|||
*/
|
||||
CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
|
||||
{
|
||||
assert(IsRailDepotTile(tile));
|
||||
const RailVehicleInfo *rvi = &e->u.rail;
|
||||
|
||||
if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret);
|
||||
|
||||
/* Check if depot and new engine uses the same kind of tracks *
|
||||
* We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */
|
||||
if (!HasPowerOnRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR;
|
||||
/* Find a good tile to place the engine and get power on it. */
|
||||
tile = FindCompatibleDepotTile(GetDepotIndex(tile), rvi->railtype, true);
|
||||
if (tile == INVALID_TILE) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
DiagDirection dir = GetRailDepotDirection(tile);
|
||||
|
@ -824,10 +891,10 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin
|
|||
static Train *FindGoodVehiclePos(const Train *src)
|
||||
{
|
||||
EngineID eng = src->engine_type;
|
||||
TileIndex tile = src->tile;
|
||||
DepotID dep_id = GetDepotIndex(src->tile);
|
||||
|
||||
for (Train *dst : Train::Iterate()) {
|
||||
if (dst->IsFreeWagon() && dst->tile == tile && !(dst->vehstatus & VS_CRASHED)) {
|
||||
if (dst->IsFreeWagon() && !(dst->vehstatus & VS_CRASHED) && GetDepotIndex(dst->tile) == dep_id) {
|
||||
/* check so all vehicles in the line have the same engine. */
|
||||
Train *t = dst;
|
||||
while (t->engine_type == eng) {
|
||||
|
@ -984,6 +1051,82 @@ static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *origina
|
|||
return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a train can be placed in a depot tile.
|
||||
* @param train The train.
|
||||
* @param tile The tile to check whether it is possible to place the train.
|
||||
* @return whether it found a depot tile in which to place the train.
|
||||
*/
|
||||
bool CheckPlacement(const Train *train, TileIndex tile)
|
||||
{
|
||||
assert(train != nullptr);
|
||||
assert(IsRailDepotTile(tile));
|
||||
|
||||
RailType rt = GetRailType(tile);
|
||||
for (const Train *t = train; t != nullptr; t = t->Next()) {
|
||||
RailType rail_type = Engine::Get(t->engine_type)->u.rail.railtype;
|
||||
if (!IsCompatibleRail(rail_type, rt)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a valid tile before placing a train in the depot.
|
||||
* @param t The train to place in a rail depot tile.
|
||||
* @return a compatible tile, if any, preferabily the one the first vehicle is or INVALID_TILE if none found.
|
||||
*/
|
||||
TileIndex LookForTileInDepot(const Train *train)
|
||||
{
|
||||
assert(train != nullptr);
|
||||
assert(IsRailDepotTile(train->tile));
|
||||
TileIndex best_tile = INVALID_TILE;
|
||||
|
||||
/* First candidate is the original position of the train. */
|
||||
if (CheckPlacement(train, train->tile)) {
|
||||
if (HasPowerOnRail(train->railtype, GetRailType(train->tile))) return train->tile;
|
||||
best_tile = train->tile;
|
||||
}
|
||||
|
||||
/* Check all depot tiles. */
|
||||
Depot *depot = Depot::GetByTile(train->tile);
|
||||
for (std::vector<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.
|
||||
* @param t the train to check
|
||||
|
@ -994,6 +1137,8 @@ static CommandCost CheckTrainAttachment(Train *t)
|
|||
/* No multi-part train, no need to check. */
|
||||
if (t == nullptr || t->Next() == nullptr) return CommandCost();
|
||||
|
||||
if (LookForTileInDepot(t) == INVALID_TILE) return_cmd_error(STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT);
|
||||
|
||||
/* The maximum length for a train. For each part we decrease this by one
|
||||
* and if the result is negative the train is simply too long. */
|
||||
int allowed_len = _settings_game.vehicle.max_train_length * TILE_SIZE - t->gcache.cached_veh_length;
|
||||
|
@ -1229,7 +1374,7 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
|
|||
Train *dst_head;
|
||||
if (dst != nullptr) {
|
||||
dst_head = dst->First();
|
||||
if (dst_head->tile != src_head->tile) return CMD_ERROR;
|
||||
if (GetDepotIndex(dst_head->tile) != GetDepotIndex(src_head->tile)) return CMD_ERROR;
|
||||
/* Now deal with articulated part of destination wagon */
|
||||
dst = dst->GetLastEnginePart();
|
||||
} else {
|
||||
|
@ -1359,11 +1504,18 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
|
|||
CheckCargoCapacity(dst_head);
|
||||
}
|
||||
|
||||
if (src_head != nullptr) src_head->First()->MarkDirty();
|
||||
if (dst_head != nullptr) dst_head->First()->MarkDirty();
|
||||
if (src_head != nullptr) {
|
||||
PlaceOnRailDepot(src_head->First());
|
||||
src_head->First()->MarkDirty();
|
||||
}
|
||||
|
||||
if (dst_head != nullptr) {
|
||||
PlaceOnRailDepot(dst_head->First());
|
||||
dst_head->First()->MarkDirty();
|
||||
}
|
||||
|
||||
/* We are undoubtedly changing something in the depot and train list. */
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(src->tile));
|
||||
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
|
||||
} else {
|
||||
/* We don't want to execute what we're just tried. */
|
||||
|
@ -1447,7 +1599,7 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b
|
|||
NormaliseTrainHead(new_head);
|
||||
|
||||
/* We are undoubtedly changing something in the depot and train list. */
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
|
||||
|
||||
/* Actually delete the sold 'goods' */
|
||||
|
@ -1966,7 +2118,7 @@ void ReverseTrainDirection(Train *v)
|
|||
{
|
||||
if (IsRailDepotTile(v->tile)) {
|
||||
if (IsWholeTrainInsideDepot(v)) return;
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
}
|
||||
|
||||
/* Clear path reservation in front if train is not stuck. */
|
||||
|
@ -1989,7 +2141,7 @@ void ReverseTrainDirection(Train *v)
|
|||
AdvanceWagonsAfterSwap(v);
|
||||
|
||||
if (IsRailDepotTile(v->tile)) {
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
}
|
||||
|
||||
ToggleBit(v->flags, VRF_TOGGLE_REVERSE);
|
||||
|
@ -2079,7 +2231,7 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool
|
|||
ToggleBit(v->flags, VRF_REVERSE_DIRECTION);
|
||||
|
||||
front->ConsistChanged(CCF_ARRANGE);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, front->tile);
|
||||
if (IsRailDepotTile(front->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile));
|
||||
SetWindowDirty(WC_VEHICLE_DETAILS, front->index);
|
||||
SetWindowDirty(WC_VEHICLE_VIEW, front->index);
|
||||
SetWindowClassesDirty(WC_TRAINS_LIST);
|
||||
|
@ -2273,7 +2425,7 @@ static bool CheckTrainStayInDepot(Train *v)
|
|||
/* if the train got no power, then keep it in the depot */
|
||||
if (v->gcache.cached_power == 0) {
|
||||
v->vehstatus |= VS_STOPPED;
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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. */
|
||||
if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
|
||||
if (v->current_order.IsType(OT_GOTO_DEPOT) &&
|
||||
IsRailDepotTile(v->tile) &&
|
||||
v->current_order.GetDestination() == GetDepotIndex(v->tile)) {
|
||||
/* Service when depot has no reservation. */
|
||||
if (!HasDepotReservation(v->tile)) VehicleEnterDepot(v);
|
||||
return true;
|
||||
|
@ -2334,7 +2488,7 @@ static bool CheckTrainStayInDepot(Train *v)
|
|||
v->UpdatePosition();
|
||||
UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
|
||||
v->UpdateAcceleration();
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -3654,7 +3808,7 @@ static void DeleteLastWagon(Train *v)
|
|||
/* Update the depot window if the first vehicle is in depot -
|
||||
* if v == first, then it is updated in PreDestructor() */
|
||||
if (first->track == TRACK_BIT_DEPOT) {
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, first->tile);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(first->tile));
|
||||
}
|
||||
v->last_station_visited = first->last_station_visited; // for PreDestructor
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "vehicle_func.h"
|
||||
#include "zoom_func.h"
|
||||
#include "train_cmd.h"
|
||||
#include "depot_map.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -29,11 +30,12 @@
|
|||
void CcBuildWagon(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray, TileIndex tile, EngineID, bool, CargoID, ClientID)
|
||||
{
|
||||
if (result.Failed()) return;
|
||||
DepotID depot_id = GetDepotIndex(tile);
|
||||
|
||||
/* find a locomotive in the depot. */
|
||||
const Vehicle *found = nullptr;
|
||||
for (const Train *t : Train::Iterate()) {
|
||||
if (t->IsFrontEngine() && t->tile == tile && t->IsStoppedInDepot()) {
|
||||
if (t->IsFrontEngine() && t->IsStoppedInDepot() && GetDepotIndex(t->tile) == depot_id) {
|
||||
if (found != nullptr) return; // must be exactly one.
|
||||
found = t;
|
||||
}
|
||||
|
|
|
@ -297,7 +297,7 @@ uint Vehicle::Crash(bool)
|
|||
InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
|
||||
SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
|
||||
if (IsDepotTile(this->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile));
|
||||
|
||||
delete this->cargo_payment;
|
||||
assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
|
||||
|
@ -868,8 +868,8 @@ void Vehicle::PreDestructor()
|
|||
if (v->disaster_vehicle != INVALID_VEHICLE) ReleaseDisasterVehicle(v->disaster_vehicle);
|
||||
}
|
||||
|
||||
if (this->Previous() == nullptr) {
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
|
||||
if (this->Previous() == nullptr && IsDepotTile(this->tile)) {
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile));
|
||||
}
|
||||
|
||||
if (this->IsPrimaryVehicle()) {
|
||||
|
@ -1554,6 +1554,7 @@ void VehicleEnterDepot(Vehicle *v)
|
|||
/* Always work with the front of the vehicle */
|
||||
assert(v == v->First());
|
||||
|
||||
DepotID depot_id = GetDepotIndex(v->tile);
|
||||
switch (v->type) {
|
||||
case VEH_TRAIN: {
|
||||
Train *t = Train::From(v);
|
||||
|
@ -1580,7 +1581,7 @@ void VehicleEnterDepot(Vehicle *v)
|
|||
ship->state = TRACK_BIT_DEPOT;
|
||||
ship->UpdateCache();
|
||||
ship->UpdateViewport(true, true);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1595,9 +1596,9 @@ void VehicleEnterDepot(Vehicle *v)
|
|||
if (v->type != VEH_TRAIN) {
|
||||
/* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
|
||||
* We only increase the number of vehicles when the first one enters, so we will not need to search for more vehicles in the depot */
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
|
||||
}
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
|
||||
|
||||
v->vehstatus |= VS_HIDDEN;
|
||||
v->cur_speed = 0;
|
||||
|
@ -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. */
|
||||
if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
|
||||
real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
|
||||
(v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
|
||||
v->current_order.GetDestination() != GetDepotIndex(v->tile)) {
|
||||
/* We are heading for another depot, keep driving. */
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "roadveh_cmd.h"
|
||||
#include "train_cmd.h"
|
||||
#include "ship_cmd.h"
|
||||
#include "depot_base.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
|
@ -178,7 +179,7 @@ std::tuple<CommandCost, VehicleID, uint, uint16_t, CargoArray> CmdBuildVehicle(D
|
|||
NormalizeTrainVehInDepot(Train::From(v));
|
||||
}
|
||||
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
InvalidateWindowClassesData(GetWindowClassForVehicleType(type), 0);
|
||||
SetWindowDirty(WC_COMPANY, _current_company);
|
||||
if (IsLocalCompany()) {
|
||||
|
@ -553,7 +554,7 @@ std::tuple<CommandCost, uint, uint16_t, CargoArray> CmdRefitVehicle(DoCommandFla
|
|||
InvalidateWindowData(WC_VEHICLE_DETAILS, front->index);
|
||||
InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
|
||||
}
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, front->tile);
|
||||
if (IsDepotTile(front->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile));
|
||||
} else {
|
||||
/* Always invalidate the cache; querycost might have filled it. */
|
||||
v->InvalidateNewGRFCacheOfChain();
|
||||
|
@ -639,7 +640,7 @@ CommandCost CmdStartStopVehicle(DoCommandFlag flags, VehicleID veh_id, bool eval
|
|||
|
||||
v->MarkDirty();
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
|
||||
if (IsDepotTile(v->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
SetWindowClassesDirty(GetWindowClassForVehicleType(v->type));
|
||||
InvalidateWindowData(WC_VEHICLE_VIEW, v->index);
|
||||
}
|
||||
|
@ -667,7 +668,7 @@ CommandCost CmdMassStartStopVehicle(DoCommandFlag flags, TileIndex tile, bool do
|
|||
} else {
|
||||
if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
|
||||
/* Get the list of vehicles in the depot */
|
||||
BuildDepotVehicleList(vli.vtype, tile, &list, nullptr);
|
||||
BuildDepotVehicleList(vli.vtype, GetDepotIndex(tile), &list, nullptr);
|
||||
}
|
||||
|
||||
for (const Vehicle *v : list) {
|
||||
|
@ -699,7 +700,7 @@ CommandCost CmdDepotSellAllVehicles(DoCommandFlag flags, TileIndex tile, Vehicle
|
|||
if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
|
||||
|
||||
/* Get the list of vehicles in the depot */
|
||||
BuildDepotVehicleList(vehicle_type, tile, &list, &list);
|
||||
BuildDepotVehicleList(vehicle_type, GetDepotIndex(tile), &list, &list);
|
||||
|
||||
CommandCost last_error = CMD_ERROR;
|
||||
bool had_success = false;
|
||||
|
@ -732,7 +733,7 @@ CommandCost CmdDepotMassAutoReplace(DoCommandFlag flags, TileIndex tile, Vehicle
|
|||
if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
|
||||
|
||||
/* Get the list of vehicles in the depot */
|
||||
BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);
|
||||
BuildDepotVehicleList(vehicle_type, GetDepotIndex(tile), &list, &list, true);
|
||||
|
||||
for (const Vehicle *v : list) {
|
||||
/* Ensure that the vehicle completely in the depot */
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "train_cmd.h"
|
||||
#include "hotkeys.h"
|
||||
#include "group_cmd.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
@ -2122,7 +2123,7 @@ public:
|
|||
}
|
||||
|
||||
case WID_VL_AVAILABLE_VEHICLES:
|
||||
ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
|
||||
ShowBuildVehicleWindow(INVALID_DEPOT, this->vli.vtype);
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile)
|
||||
void ShowVehicleListWindowDepot(CompanyID company, VehicleType vehicle_type, DepotID depot_id)
|
||||
{
|
||||
uint16_t depot_airport_index;
|
||||
|
||||
if (vehicle_type == VEH_AIRCRAFT) {
|
||||
depot_airport_index = GetStationIndex(depot_tile);
|
||||
} else {
|
||||
depot_airport_index = GetDepotIndex(depot_tile);
|
||||
}
|
||||
ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, depot_airport_index);
|
||||
ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, depot_id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3141,7 +3135,7 @@ public:
|
|||
|
||||
case OT_GOTO_DEPOT: {
|
||||
SetDParam(0, v->type);
|
||||
SetDParam(1, v->current_order.GetDestination());
|
||||
SetDParam(1, GetTargetDestination(v->current_order, v->type == VEH_AIRCRAFT));
|
||||
SetDParam(2, PackVelocity(v->GetDisplaySpeed(), v->type));
|
||||
if (v->current_order.GetDestination() == INVALID_DEPOT) {
|
||||
/* This case *only* happens when multiple nearest depot orders
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "station_type.h"
|
||||
#include "engine_type.h"
|
||||
#include "company_type.h"
|
||||
#include "depot_type.h"
|
||||
|
||||
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit = false);
|
||||
|
||||
|
@ -56,7 +57,7 @@ void DrawRoadVehImage(const Vehicle *v, const Rect &r, VehicleID selection, Engi
|
|||
void DrawShipImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type);
|
||||
void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type);
|
||||
|
||||
void ShowBuildVehicleWindow(TileIndex tile, VehicleType type);
|
||||
void ShowBuildVehicleWindow(DepotID depot_id, VehicleType type);
|
||||
|
||||
uint ShowRefitOptionsList(int left, int right, int y, EngineID engine);
|
||||
StringID GetCargoSubtypeText(const Vehicle *v);
|
||||
|
@ -64,7 +65,7 @@ StringID GetCargoSubtypeText(const Vehicle *v);
|
|||
void ShowVehicleListWindow(const Vehicle *v);
|
||||
void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type);
|
||||
void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station);
|
||||
void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile);
|
||||
void ShowVehicleListWindowDepot(CompanyID company, VehicleType vehicle_type, DepotID depot_id);
|
||||
|
||||
/**
|
||||
* Get the height of a single vehicle in the GUIs.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "vehiclelist.h"
|
||||
#include "vehiclelist_func.h"
|
||||
#include "group.h"
|
||||
#include "depot_base.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
@ -95,19 +96,24 @@ static Vehicle *BuildDepotVehicleListProc(Vehicle *v, void *data)
|
|||
|
||||
/**
|
||||
* Generate a list of vehicles inside a depot.
|
||||
* @param type Type of vehicle
|
||||
* @param tile The tile the depot is located on
|
||||
* @param engines Pointer to list to add vehicles to
|
||||
* @param wagons Pointer to list to add wagons to (can be nullptr)
|
||||
* @param type Type of vehicle
|
||||
* @param depot_id The id of the depot
|
||||
* @param engines Pointer to list to add vehicles to
|
||||
* @param wagons Pointer to list to add wagons to (can be nullptr)
|
||||
* @param individual_wagons If true add every wagon to \a wagons which is not attached to an engine. If false only add the first wagon of every row.
|
||||
*/
|
||||
void BuildDepotVehicleList(VehicleType type, TileIndex tile, VehicleList *engines, VehicleList *wagons, bool individual_wagons)
|
||||
void BuildDepotVehicleList(VehicleType type, DepotID depot_id, VehicleList *engines, VehicleList *wagons, bool individual_wagons)
|
||||
{
|
||||
assert(Depot::IsValidID(depot_id));
|
||||
Depot *dep = Depot::Get(depot_id);
|
||||
engines->clear();
|
||||
if (wagons != nullptr && wagons != engines) wagons->clear();
|
||||
|
||||
BuildDepotVehicleListData bdvld{engines, wagons, type, individual_wagons};
|
||||
FindVehicleOnPos(tile, &bdvld, BuildDepotVehicleListProc);
|
||||
|
||||
for (TileIndex tile : dep->depot_tiles) {
|
||||
FindVehicleOnPos(tile, &bdvld, BuildDepotVehicleListProc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "vehicle_type.h"
|
||||
#include "company_type.h"
|
||||
#include "depot_type.h"
|
||||
#include "tile_type.h"
|
||||
|
||||
/** Vehicle List type flags */
|
||||
|
@ -54,7 +55,7 @@ struct VehicleListIdentifier {
|
|||
typedef std::vector<const Vehicle *> VehicleList;
|
||||
|
||||
bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &identifier);
|
||||
void BuildDepotVehicleList(VehicleType type, TileIndex tile, VehicleList *engine_list, VehicleList *wagon_list, bool individual_wagons = false);
|
||||
void BuildDepotVehicleList(VehicleType type, DepotID depot_id, VehicleList *engine_list, VehicleList *wagon_list, bool individual_wagons = false);
|
||||
uint GetUnitNumberDigits(VehicleList &vehicles);
|
||||
|
||||
#endif /* VEHICLELIST_H */
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
#include "network/network_func.h"
|
||||
#include "framerate_type.h"
|
||||
#include "viewport_cmd.h"
|
||||
#include "depot_map.h"
|
||||
|
||||
#include <forward_list>
|
||||
#include <stack>
|
||||
|
@ -1002,6 +1003,7 @@ enum TileHighlightType {
|
|||
const Station *_viewport_highlight_station; ///< Currently selected station for coverage area highlight
|
||||
const Waypoint *_viewport_highlight_waypoint; ///< Currently selected waypoint for coverage area highlight
|
||||
const Town *_viewport_highlight_town; ///< Currently selected town for coverage area highlight
|
||||
DepotID _viewport_highlight_depot = INVALID_DEPOT; ///< Currently selected depot for depot highlight
|
||||
|
||||
/**
|
||||
* Get tile highlight type of coverage area for a given tile.
|
||||
|
@ -1010,6 +1012,10 @@ const Town *_viewport_highlight_town; ///< Currently selected town for coverage
|
|||
*/
|
||||
static TileHighlightType GetTileHighlightType(TileIndex t)
|
||||
{
|
||||
if (_viewport_highlight_depot != INVALID_DEPOT) {
|
||||
if (IsDepotTile(t) && GetDepotIndex(t) == _viewport_highlight_depot) return THT_BLUE;
|
||||
}
|
||||
|
||||
if (_viewport_highlight_station != nullptr) {
|
||||
if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station->index) return THT_WHITE;
|
||||
if (_viewport_highlight_station->TileIsInCatchment(t)) return THT_BLUE;
|
||||
|
@ -2674,6 +2680,8 @@ void UpdateTileSelection()
|
|||
}
|
||||
_thd.new_pos.x = x1 & ~TILE_UNIT_MASK;
|
||||
_thd.new_pos.y = y1 & ~TILE_UNIT_MASK;
|
||||
if (_thd.select_method == VPM_LIMITED_X_FIXED_Y) _thd.new_size.y = (TILE_SIZE * _thd.fixed_size) & ~TILE_UNIT_MASK;
|
||||
if (_thd.select_method == VPM_LIMITED_Y_FIXED_X) _thd.new_size.x = (TILE_SIZE * _thd.fixed_size) & ~TILE_UNIT_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2766,6 +2774,15 @@ void VpSetPlaceSizingLimit(int limit)
|
|||
_thd.sizelimit = limit;
|
||||
}
|
||||
|
||||
void VpSetPlaceFixedSize(uint8_t fixed)
|
||||
{
|
||||
_thd.fixed_size = fixed;
|
||||
}
|
||||
|
||||
void VpResetFixedSize() {
|
||||
VpSetPlaceFixedSize(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights all tiles between a set of two tiles. Used in dock and tunnel placement
|
||||
* @param from TileIndex of the first tile to highlight
|
||||
|
@ -3237,7 +3254,7 @@ void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
|
|||
sx = _thd.selstart.x;
|
||||
sy = _thd.selstart.y;
|
||||
|
||||
int limit = 0;
|
||||
int limit = -1;
|
||||
|
||||
switch (method) {
|
||||
case VPM_X_OR_Y: // drag in X or Y direction
|
||||
|
@ -3250,28 +3267,33 @@ void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
|
|||
}
|
||||
goto calc_heightdiff_single_direction;
|
||||
|
||||
case VPM_LIMITED_Y_FIXED_X:
|
||||
case VPM_X_LIMITED: // Drag in X direction (limited size).
|
||||
limit = (_thd.sizelimit - 1) * TILE_SIZE;
|
||||
[[fallthrough]];
|
||||
|
||||
case VPM_FIX_X: // drag in Y direction
|
||||
x = sx;
|
||||
x = sx + (method == VPM_LIMITED_Y_FIXED_X ? (TILE_SIZE * (_thd.fixed_size - 1)) : 0) ;
|
||||
style = HT_DIR_Y;
|
||||
goto calc_heightdiff_single_direction;
|
||||
|
||||
case VPM_LIMITED_X_FIXED_Y:
|
||||
case VPM_Y_LIMITED: // Drag in Y direction (limited size).
|
||||
limit = (_thd.sizelimit - 1) * TILE_SIZE;
|
||||
[[fallthrough]];
|
||||
|
||||
case VPM_FIX_Y: // drag in X direction
|
||||
y = sy;
|
||||
y = sy + (method == VPM_LIMITED_X_FIXED_Y ? (TILE_SIZE * (_thd.fixed_size - 1)) : 0) ;
|
||||
style = HT_DIR_X;
|
||||
|
||||
calc_heightdiff_single_direction:;
|
||||
if (limit > 0) {
|
||||
x = sx + Clamp(x - sx, -limit, limit);
|
||||
y = sy + Clamp(y - sy, -limit, limit);
|
||||
if (limit >= 0) {
|
||||
if (method != VPM_LIMITED_X_FIXED_Y) y = sy + Clamp(y - sy, -limit, limit);
|
||||
if (method != VPM_LIMITED_Y_FIXED_X) x = sx + Clamp(x - sx, -limit, limit);
|
||||
}
|
||||
|
||||
if (method == VPM_LIMITED_Y_FIXED_X || method == VPM_LIMITED_X_FIXED_Y) goto measure_area;
|
||||
|
||||
if (_settings_client.gui.measure_tooltip) {
|
||||
TileIndex t0 = TileVirtXY(sx, sy);
|
||||
TileIndex t1 = TileVirtXY(x, y);
|
||||
|
@ -3301,6 +3323,7 @@ calc_heightdiff_single_direction:;
|
|||
[[fallthrough]];
|
||||
|
||||
case VPM_X_AND_Y: // drag an X by Y area
|
||||
measure_area:
|
||||
if (_settings_client.gui.measure_tooltip) {
|
||||
static const StringID measure_strings_area[] = {
|
||||
STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
|
||||
|
@ -3589,6 +3612,9 @@ void MarkCatchmentTilesDirty()
|
|||
}
|
||||
MarkWholeScreenDirty();
|
||||
}
|
||||
if (_viewport_highlight_depot != INVALID_DEPOT) {
|
||||
MarkWholeScreenDirty();
|
||||
}
|
||||
}
|
||||
|
||||
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_waypoint != nullptr) SetWindowDirty(WC_WAYPOINT_VIEW, _viewport_highlight_waypoint->index);
|
||||
if (_viewport_highlight_town != nullptr) SetWindowDirty(WC_TOWN_VIEW, _viewport_highlight_town->index);
|
||||
if (_viewport_highlight_depot != INVALID_DEPOT) SetWindowDirty(WC_VEHICLE_DEPOT, _viewport_highlight_depot);
|
||||
}
|
||||
|
||||
static void ClearViewportCatchment()
|
||||
|
@ -3604,6 +3631,7 @@ static void ClearViewportCatchment()
|
|||
_viewport_highlight_station = nullptr;
|
||||
_viewport_highlight_waypoint = 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "tile_map.h"
|
||||
#include "station_type.h"
|
||||
#include "vehicle_type.h"
|
||||
#include "depot_type.h"
|
||||
|
||||
static const int TILE_HEIGHT_STEP = 50; ///< One Z unit tile height difference is displayed as 50m.
|
||||
|
||||
|
@ -102,6 +103,8 @@ struct Town;
|
|||
void SetViewportCatchmentStation(const Station *st, bool sel);
|
||||
void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel);
|
||||
void SetViewportCatchmentTown(const Town *t, bool sel);
|
||||
void SetViewportHighlightDepot(const DepotID dep, bool sel);
|
||||
|
||||
void MarkCatchmentTilesDirty();
|
||||
|
||||
template<class T>
|
||||
|
|
|
@ -99,6 +99,8 @@ enum ViewportPlaceMethod {
|
|||
VPM_FIX_VERTICAL = 6, ///< drag only in vertical direction
|
||||
VPM_X_LIMITED = 7, ///< Drag only in X axis with limited size
|
||||
VPM_Y_LIMITED = 8, ///< Drag only in Y axis with limited size
|
||||
VPM_LIMITED_Y_FIXED_X = 9, ///< Drag only in Y axis with limited size and a fixed value for X axis
|
||||
VPM_LIMITED_X_FIXED_Y = 10, ///< Drag only in X axis with limited size and a fixed value for Y axis
|
||||
VPM_RAILDIRS = 0x40, ///< all rail directions
|
||||
VPM_SIGNALDIRS = 0x80, ///< similar to VMP_RAILDIRS, but with different cursor
|
||||
};
|
||||
|
@ -120,6 +122,8 @@ enum ViewportDragDropSelectionProcess {
|
|||
DDSP_PLANT_TREES, ///< Plant trees
|
||||
DDSP_BUILD_BRIDGE, ///< Bridge placement
|
||||
DDSP_BUILD_OBJECT, ///< Build an object
|
||||
DDSP_BUILD_DEPOT, ///< Depot placement
|
||||
DDSP_REMOVE_DEPOT, ///< Depot removal
|
||||
|
||||
/* Rail specific actions */
|
||||
DDSP_PLACE_RAIL, ///< Rail placement
|
||||
|
|
|
@ -94,66 +94,75 @@ static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
|
|||
/**
|
||||
* Build a ship depot.
|
||||
* @param flags type of operation
|
||||
* @param tile tile where ship depot is built
|
||||
* @param tile first tile where ship depot is built
|
||||
* @param axis depot orientation (Axis)
|
||||
* @param adjacent allow adjacent depots
|
||||
* @param join_to depot to join to
|
||||
* @param end_tile end tile of area to be built
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis)
|
||||
CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis, bool adjacent, DepotID join_to, TileIndex end_tile)
|
||||
{
|
||||
if (!IsValidAxis(axis)) return CMD_ERROR;
|
||||
TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
|
||||
|
||||
if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
|
||||
return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
|
||||
}
|
||||
TileArea complete_area(tile, end_tile);
|
||||
complete_area.Add(tile2);
|
||||
assert(complete_area.w == 2 || complete_area.h == 2);
|
||||
|
||||
if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
|
||||
TileArea northern_tiles(complete_area.tile);
|
||||
northern_tiles.Add(complete_area.tile + (axis == AXIS_X ? TileDiffXY(0, complete_area.h - 1) : TileDiffXY(complete_area.w - 1, 0)));
|
||||
|
||||
if (!IsTileFlat(tile) || !IsTileFlat(tile2)) {
|
||||
/* Prevent depots on rapids */
|
||||
return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
|
||||
}
|
||||
|
||||
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);
|
||||
/* Create a new depot or find a depot to join to. */
|
||||
Depot *depot = nullptr;
|
||||
CommandCost ret = FindJoiningDepot(complete_area, VEH_SHIP, join_to, depot, adjacent, flags);
|
||||
if (ret.Failed()) return ret;
|
||||
if (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);
|
||||
|
||||
/* Get the cost of building all the ship depots. */
|
||||
CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP] * northern_tiles.w * northern_tiles.h);
|
||||
|
||||
/* Update infrastructure counts after the tile clears earlier.
|
||||
* Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
|
||||
* See: MakeWaterKeepingClass() */
|
||||
uint new_water_infra = 0;
|
||||
|
||||
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) {
|
||||
Depot *depot = new Depot(tile);
|
||||
depot->build_date = TimerGameCalendar::date;
|
||||
|
||||
uint new_water_infra = 2 * LOCK_DEPOT_TILE_FACTOR;
|
||||
/* Update infrastructure counts after the tile clears earlier.
|
||||
* Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
|
||||
* See: MakeWaterKeepingClass() */
|
||||
if (wc1 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile) && GetWaterClass(tile) == WATER_CLASS_CANAL && IsTileOwner(tile, _current_company))) new_water_infra++;
|
||||
if (wc2 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile2) && GetWaterClass(tile2) == WATER_CLASS_CANAL && IsTileOwner(tile2, _current_company))) new_water_infra++;
|
||||
|
||||
Company::Get(_current_company)->infrastructure.water += new_water_infra;
|
||||
Company::Get(_current_company)->infrastructure.water += new_water_infra +
|
||||
complete_area.w * complete_area.h * LOCK_DEPOT_TILE_FACTOR;
|
||||
DirtyCompanyInfrastructureWindows(_current_company);
|
||||
|
||||
MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1);
|
||||
MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2);
|
||||
CheckForDockingTile(tile);
|
||||
CheckForDockingTile(tile2);
|
||||
MarkTileDirtyByTile(tile);
|
||||
MarkTileDirtyByTile(tile2);
|
||||
MakeDefaultName(depot);
|
||||
depot->AfterAddRemove(complete_area, true);
|
||||
if (join_to == NEW_DEPOT) MakeDefaultName(depot);
|
||||
}
|
||||
|
||||
return cost;
|
||||
|
@ -275,9 +284,8 @@ static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
|
|||
bool do_clear = (flags & DC_FORCE_CLEAR_TILE) != 0;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
delete Depot::GetByTile(tile);
|
||||
|
||||
Company *c = Company::GetIfValid(GetTileOwner(tile));
|
||||
Depot *depot = Depot::GetByTile(tile);
|
||||
Company *c = Company::GetIfValid(depot->owner);
|
||||
if (c != nullptr) {
|
||||
c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR;
|
||||
if (do_clear && GetWaterClass(tile) == WATER_CLASS_CANAL) c->infrastructure.water--;
|
||||
|
@ -286,6 +294,8 @@ static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
|
|||
|
||||
if (!do_clear) MakeWaterKeepingClass(tile, GetTileOwner(tile));
|
||||
MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
|
||||
|
||||
depot->AfterAddRemove(TileArea(tile, tile2), false);
|
||||
}
|
||||
|
||||
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
|
||||
|
@ -1334,7 +1344,7 @@ static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode,
|
|||
static bool ClickTile_Water(TileIndex tile)
|
||||
{
|
||||
if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
|
||||
ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
|
||||
ShowDepotWindow(GetDepotIndex(tile));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "command_type.h"
|
||||
#include "water_map.h"
|
||||
|
||||
CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis);
|
||||
CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis, bool adjacent, DepotID depot_id, TileIndex end_tile);
|
||||
CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, WaterClass wc, bool diagonal);
|
||||
CommandCost CmdBuildLock(DoCommandFlag flags, TileIndex tile);
|
||||
|
||||
|
|
|
@ -27,9 +27,17 @@ enum DepotWidgets : WidgetID {
|
|||
WID_D_LOCATION, ///< Location button.
|
||||
WID_D_SHOW_RENAME, ///< Show rename panel.
|
||||
WID_D_RENAME, ///< Rename button.
|
||||
WID_D_HIGHLIGHT, ///< Highlight button.
|
||||
WID_D_VEHICLE_LIST, ///< List of vehicles.
|
||||
WID_D_STOP_ALL, ///< Stop all button.
|
||||
WID_D_START_ALL, ///< Start all button.
|
||||
};
|
||||
|
||||
/** 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 */
|
||||
|
|
|
@ -241,6 +241,12 @@ enum WindowClass {
|
|||
*/
|
||||
WC_SELECT_STATION,
|
||||
|
||||
/**
|
||||
* Select depot (when joining depots); %Window numbers:
|
||||
* - #Vehicle type = #JoinDepotWidgets
|
||||
*/
|
||||
WC_SELECT_DEPOT,
|
||||
|
||||
/**
|
||||
* News window; %Window numbers:
|
||||
* - 0 = #NewsWidgets
|
||||
|
@ -346,7 +352,7 @@ enum WindowClass {
|
|||
|
||||
/**
|
||||
* Depot view; %Window numbers:
|
||||
* - #TileIndex = #DepotWidgets
|
||||
* - #DepotID = #DepotWidgets
|
||||
*/
|
||||
WC_VEHICLE_DEPOT,
|
||||
|
||||
|
@ -383,8 +389,8 @@ enum WindowClass {
|
|||
|
||||
/**
|
||||
* Build vehicle; %Window numbers:
|
||||
* - #VehicleType = #BuildVehicleWidgets
|
||||
* - #TileIndex = #BuildVehicleWidgets
|
||||
* - #DepotID = #BuildVehicleWidgets, for existing depots
|
||||
* - #MAX_DEPOTS + VehicleType = #BuildVehicleWidgets, for "Available Trains"...
|
||||
*/
|
||||
WC_BUILD_VEHICLE,
|
||||
|
||||
|
|
Loading…
Reference in New Issue