Allow converting track by individual track pieces instead of whole tiles
Ctrl-click on convert button See: #509
This commit is contained in:
@@ -53,6 +53,7 @@ CommandProc CmdBuildBridge;
|
|||||||
CommandProcEx CmdBuildRailStation;
|
CommandProcEx CmdBuildRailStation;
|
||||||
CommandProc CmdRemoveFromRailStation;
|
CommandProc CmdRemoveFromRailStation;
|
||||||
CommandProc CmdConvertRail;
|
CommandProc CmdConvertRail;
|
||||||
|
CommandProc CmdConvertRailTrack;
|
||||||
|
|
||||||
CommandProc CmdBuildSingleSignal;
|
CommandProc CmdBuildSingleSignal;
|
||||||
CommandProc CmdRemoveSingleSignal;
|
CommandProc CmdRemoveSingleSignal;
|
||||||
@@ -334,6 +335,7 @@ static const Command _command_proc_table[] = {
|
|||||||
DEF_CMD(CmdBuildTunnel, CMD_DEITY | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_TUNNEL
|
DEF_CMD(CmdBuildTunnel, CMD_DEITY | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_TUNNEL
|
||||||
DEF_CMD(CmdRemoveFromRailStation, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_FROM_RAIL_STATION
|
DEF_CMD(CmdRemoveFromRailStation, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_FROM_RAIL_STATION
|
||||||
DEF_CMD(CmdConvertRail, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_CONVERT_RAIL
|
DEF_CMD(CmdConvertRail, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_CONVERT_RAIL
|
||||||
|
DEF_CMD(CmdConvertRailTrack, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_CONVERT_RAIL_TRACK
|
||||||
DEF_CMD(CmdBuildRailWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_RAIL_WAYPOINT
|
DEF_CMD(CmdBuildRailWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_RAIL_WAYPOINT
|
||||||
DEF_CMD(CmdBuildRoadWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD_WAYPOINT
|
DEF_CMD(CmdBuildRoadWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD_WAYPOINT
|
||||||
DEF_CMD(CmdRenameWaypoint, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_WAYPOINT
|
DEF_CMD(CmdRenameWaypoint, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_WAYPOINT
|
||||||
|
@@ -296,6 +296,7 @@ enum Commands {
|
|||||||
|
|
||||||
CMD_REMOVE_FROM_RAIL_STATION, ///< remove a (rectangle of) tiles from a rail station
|
CMD_REMOVE_FROM_RAIL_STATION, ///< remove a (rectangle of) tiles from a rail station
|
||||||
CMD_CONVERT_RAIL, ///< convert a rail type
|
CMD_CONVERT_RAIL, ///< convert a rail type
|
||||||
|
CMD_CONVERT_RAIL_TRACK, ///< convert a rail type (track)
|
||||||
|
|
||||||
CMD_BUILD_RAIL_WAYPOINT, ///< build a waypoint
|
CMD_BUILD_RAIL_WAYPOINT, ///< build a waypoint
|
||||||
CMD_BUILD_ROAD_WAYPOINT, ///< build a road waypoint
|
CMD_BUILD_ROAD_WAYPOINT, ///< build a road waypoint
|
||||||
|
@@ -2068,3 +2068,5 @@ STR_STATION_VIEW_RENAME_TOOLTIP_EXTRA :{BLACK}{STRING}
|
|||||||
STR_ERROR_CAN_T_EXCHANGE_STATION_NAMES :{WHITE}Can't exchange station names...
|
STR_ERROR_CAN_T_EXCHANGE_STATION_NAMES :{WHITE}Can't exchange station names...
|
||||||
STR_ERROR_STATIONS_NOT_IN_SAME_TOWN :{WHITE}... stations are not in the same town
|
STR_ERROR_STATIONS_NOT_IN_SAME_TOWN :{WHITE}... stations are not in the same town
|
||||||
STR_ERROR_STATION_ATTACHED_TO_INDUSTRY :{WHITE}... station is attached to an industry
|
STR_ERROR_STATION_ATTACHED_TO_INDUSTRY :{WHITE}... station is attached to an industry
|
||||||
|
|
||||||
|
STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_EXTRA :{STRING}{}{BLACK}Ctrl+Click to convert track pieces instead of whole tiles.
|
||||||
|
344
src/rail_cmd.cpp
344
src/rail_cmd.cpp
@@ -2323,6 +2323,36 @@ static Vehicle *UpdateTrainPowerProc(Vehicle *v, void *data)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UpdateTrainPowerProcData {
|
||||||
|
TrainList *train_list;
|
||||||
|
TrackBits track_bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Update power of train under which is the railtype being converted */
|
||||||
|
static Vehicle *UpdateTrainPowerProcAcrossTunnelBridge(Vehicle *v, void *data)
|
||||||
|
{
|
||||||
|
UpdateTrainPowerProcData *utpp_data = static_cast<UpdateTrainPowerProcData*>(data);
|
||||||
|
|
||||||
|
TrackBits vehicle_track = Train::From(v)->track;
|
||||||
|
if (!(vehicle_track & TRACK_BIT_WORMHOLE) && !(utpp_data->track_bits & vehicle_track)) return nullptr;
|
||||||
|
|
||||||
|
include(*(utpp_data->train_list), Train::From(v)->First());
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update power of train under which is the railtype being converted */
|
||||||
|
static Vehicle *UpdateTrainPowerProcOnTrackBits(Vehicle *v, void *data)
|
||||||
|
{
|
||||||
|
UpdateTrainPowerProcData *utpp_data = static_cast<UpdateTrainPowerProcData*>(data);
|
||||||
|
|
||||||
|
if (!(utpp_data->track_bits & Train::From(v)->track)) return nullptr;
|
||||||
|
|
||||||
|
include(*(utpp_data->train_list), Train::From(v)->First());
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
struct EnsureNoIncompatibleRailtypeTrainOnGroundData {
|
struct EnsureNoIncompatibleRailtypeTrainOnGroundData {
|
||||||
int z;
|
int z;
|
||||||
RailType type;
|
RailType type;
|
||||||
@@ -2659,6 +2689,320 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
|
|||||||
return found_convertible_track ? cost : error;
|
return found_convertible_track ? cost : error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert rail on a stretch of track.
|
||||||
|
* @param tile start tile of drag
|
||||||
|
* @param flags operation to perform
|
||||||
|
* @param p1 end tile of drag
|
||||||
|
* @param p2 various bitstuffed elements
|
||||||
|
* - p2 = (bit 0-5) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev)
|
||||||
|
* - p2 = (bit 6-8) - track-orientation, valid values: 0-5 (Track enum)
|
||||||
|
* @param text unused
|
||||||
|
* @return the cost of this operation or an error
|
||||||
|
*/
|
||||||
|
CommandCost CmdConvertRailTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||||
|
{
|
||||||
|
const RailType totype = Extract<RailType, 0, 6>(p2);
|
||||||
|
const TileIndex end_tile = p1;
|
||||||
|
|
||||||
|
if (!ValParamRailtype(totype)) return CMD_ERROR;
|
||||||
|
if (end_tile >= MapSize()) return CMD_ERROR;
|
||||||
|
|
||||||
|
const Track start_track = Extract<Track, 6, 3>(p2);
|
||||||
|
Trackdir trackdir = TrackToTrackdir(start_track);
|
||||||
|
|
||||||
|
CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
|
||||||
|
if (ret.Failed()) return ret;
|
||||||
|
|
||||||
|
TrainList affected_trains;
|
||||||
|
|
||||||
|
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||||
|
CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
|
||||||
|
bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
|
||||||
|
|
||||||
|
std::vector<TileIndex> exclude_tiles;
|
||||||
|
|
||||||
|
auto advance_tile = [&]() -> bool {
|
||||||
|
if (tile == end_tile) return false;
|
||||||
|
|
||||||
|
tile += ToTileIndexDiff(_trackdelta[trackdir]);
|
||||||
|
|
||||||
|
/* toggle railbit for the non-diagonal tracks */
|
||||||
|
if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
do {
|
||||||
|
if (std::find(exclude_tiles.begin(), exclude_tiles.end(), tile) != exclude_tiles.end()) continue;
|
||||||
|
|
||||||
|
const Track track = TrackdirToTrack(trackdir);
|
||||||
|
const TileType tt = GetTileType(tile);
|
||||||
|
|
||||||
|
TrackBits all_track_bits = TRACK_BIT_NONE;
|
||||||
|
|
||||||
|
/* Check if our track piece matches any track on tile */
|
||||||
|
switch (tt) {
|
||||||
|
case MP_RAILWAY:
|
||||||
|
if (IsPlainRail(tile)) {
|
||||||
|
if (!HasTrack(tile, track)) continue;
|
||||||
|
all_track_bits = GetTrackBits(tile);
|
||||||
|
} else if (IsRailDepot(tile)) {
|
||||||
|
if (GetRailDepotTrack(tile) != track) continue;
|
||||||
|
all_track_bits = TrackToTrackBits(track);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MP_STATION:
|
||||||
|
if (!HasStationRail(tile) || GetRailStationTrack(tile) != track) continue;
|
||||||
|
all_track_bits = GetRailStationTrackBits(tile);
|
||||||
|
break;
|
||||||
|
case MP_ROAD:
|
||||||
|
if (!IsLevelCrossing(tile) || GetCrossingRailTrack(tile) != track) continue;
|
||||||
|
if (RailNoLevelCrossings(totype)) {
|
||||||
|
error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
all_track_bits = GetCrossingRailBits(tile);
|
||||||
|
break;
|
||||||
|
case MP_TUNNELBRIDGE:
|
||||||
|
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL || !HasBit(GetTunnelBridgeTrackBits(tile), track)) continue;
|
||||||
|
all_track_bits = GetTunnelBridgeTrackBits(tile);
|
||||||
|
break;
|
||||||
|
default: continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Original railtype we are converting from */
|
||||||
|
const RailType type = GetRailTypeByTrack(tile, track);
|
||||||
|
|
||||||
|
/* Converting to the same type or converting 'hidden' elrail -> rail */
|
||||||
|
if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
|
||||||
|
|
||||||
|
/* Trying to convert other's rail */
|
||||||
|
CommandCost ret = CheckTileOwnership(tile);
|
||||||
|
if (ret.Failed()) {
|
||||||
|
error = ret;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Track bits on the tile to convert */
|
||||||
|
const TrackBits track_bits = (all_track_bits == TRACK_BIT_HORZ || all_track_bits == TRACK_BIT_VERT) ? TrackToTrackBits(track) : all_track_bits;
|
||||||
|
|
||||||
|
std::vector<Train *> vehicles_affected;
|
||||||
|
|
||||||
|
auto find_train_reservations = [&vehicles_affected, &totype, &flags](TileIndex tile, TrackBits reserved) -> CommandCost {
|
||||||
|
if (!(flags & DC_EXEC) && _settings_game.vehicle.train_braking_model != TBM_REALISTIC) {
|
||||||
|
/* Nothing to do */
|
||||||
|
return CommandCost();
|
||||||
|
}
|
||||||
|
Track track;
|
||||||
|
while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
|
||||||
|
Train *v = GetTrainForReservation(tile, track);
|
||||||
|
bool check_train = false;
|
||||||
|
if (v != nullptr && !HasPowerOnRail(v->railtype, totype)) {
|
||||||
|
check_train = true;
|
||||||
|
} else if (v != nullptr && _settings_game.vehicle.train_braking_model == TBM_REALISTIC) {
|
||||||
|
RailType original = GetRailTypeByTrack(tile, track);
|
||||||
|
if ((uint)(GetRailTypeInfo(original)->max_speed - 1) > (uint)(GetRailTypeInfo(totype)->max_speed - 1)) {
|
||||||
|
check_train = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (check_train) {
|
||||||
|
CommandCost ret = CheckTrainReservationPreventsTrackModification(v);
|
||||||
|
if (ret.Failed()) return ret;
|
||||||
|
|
||||||
|
/* No power on new rail type, reroute. */
|
||||||
|
if (flags & DC_EXEC) {
|
||||||
|
FreeTrainTrackReservation(v);
|
||||||
|
vehicles_affected.push_back(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CommandCost();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto yapf_notify_track_change = [](TileIndex tile, TrackBits tracks) {
|
||||||
|
while (tracks != TRACK_BIT_NONE) {
|
||||||
|
YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Vehicle on the tile when not converting Rail <-> ElRail
|
||||||
|
* Tunnels and bridges have special check later */
|
||||||
|
if (tt != MP_TUNNELBRIDGE) {
|
||||||
|
if (!IsCompatibleRail(type, totype)) {
|
||||||
|
CommandCost ret = IsPlainRailTile(tile) ? EnsureNoIncompatibleRailtypeTrainOnTrackBits(tile, track_bits, totype) : EnsureNoIncompatibleRailtypeTrainOnGround(tile, totype);
|
||||||
|
if (ret.Failed()) {
|
||||||
|
error = ret;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommandCost ret = find_train_reservations(tile, GetReservedTrackbits(tile) & track_bits);
|
||||||
|
if (ret.Failed()) return ret;
|
||||||
|
if (flags & DC_EXEC) { // we can safely convert, too
|
||||||
|
/* Update the company infrastructure counters. */
|
||||||
|
if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
|
||||||
|
Company *c = Company::Get(GetTileOwner(tile));
|
||||||
|
uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
|
||||||
|
if (IsPlainRailTile(tile)) {
|
||||||
|
num_pieces = CountBits(track_bits);
|
||||||
|
if (TracksOverlap(track_bits)) num_pieces *= num_pieces;
|
||||||
|
}
|
||||||
|
c->infrastructure.rail[type] -= num_pieces;
|
||||||
|
c->infrastructure.rail[totype] += num_pieces;
|
||||||
|
DirtyCompanyInfrastructureWindows(c->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (track_bits != all_track_bits) {
|
||||||
|
/* only partially converting the tile */
|
||||||
|
if (track_bits & TRACK_BIT_RT_1) {
|
||||||
|
SetRailType(tile, totype);
|
||||||
|
} else {
|
||||||
|
SetSecondaryRailType(tile, totype);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SetRailType(tile, totype);
|
||||||
|
if (IsPlainRailTile(tile)) SetSecondaryRailType(tile, totype);
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkTileDirtyByTile(tile);
|
||||||
|
/* update power of train on this tile */
|
||||||
|
UpdateTrainPowerProcData data;
|
||||||
|
data.train_list = &affected_trains;
|
||||||
|
data.track_bits = track_bits;
|
||||||
|
FindVehicleOnPos(tile, VEH_TRAIN, &data, &UpdateTrainPowerProcOnTrackBits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (tt) {
|
||||||
|
case MP_RAILWAY:
|
||||||
|
switch (GetRailTileType(tile)) {
|
||||||
|
case RAIL_TILE_DEPOT:
|
||||||
|
if (flags & DC_EXEC) {
|
||||||
|
/* notify YAPF about the track layout change */
|
||||||
|
YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile));
|
||||||
|
|
||||||
|
/* Update build vehicle window related to this depot */
|
||||||
|
InvalidateWindowData(WC_VEHICLE_DEPOT, tile);
|
||||||
|
InvalidateWindowData(WC_BUILD_VEHICLE, tile);
|
||||||
|
}
|
||||||
|
found_convertible_track = true;
|
||||||
|
cost.AddCost(RailConvertCost(type, totype));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS
|
||||||
|
if (flags & DC_EXEC) {
|
||||||
|
/* notify YAPF about the track layout change */
|
||||||
|
yapf_notify_track_change(tile, track_bits);
|
||||||
|
}
|
||||||
|
found_convertible_track = true;
|
||||||
|
cost.AddCost(RailConvertCost(type, totype) * CountBits(track_bits));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MP_TUNNELBRIDGE: {
|
||||||
|
TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
|
||||||
|
|
||||||
|
const bool across = (GetAcrossTunnelBridgeTrackBits(tile) & track_bits) != TRACK_BIT_NONE;
|
||||||
|
if (across) exclude_tiles.push_back(endtile);
|
||||||
|
|
||||||
|
/* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
|
||||||
|
if (!IsCompatibleRail(type, totype)) {
|
||||||
|
CommandCost ret;
|
||||||
|
if (across) {
|
||||||
|
ret = TunnelBridgeIsFree(tile, endtile, nullptr, TBIFM_PRIMARY_ONLY);
|
||||||
|
} else {
|
||||||
|
ret = EnsureNoIncompatibleRailtypeTrainOnTrackBits(tile, track_bits, totype);
|
||||||
|
}
|
||||||
|
if (ret.Failed()) {
|
||||||
|
error = ret;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found_convertible_track = true;
|
||||||
|
|
||||||
|
if (across) {
|
||||||
|
uint num_primary_pieces = GetTunnelBridgeLength(tile, endtile) + CountBits(GetPrimaryTunnelBridgeTrackBits(tile)) + CountBits(GetPrimaryTunnelBridgeTrackBits(endtile));
|
||||||
|
cost.AddCost(num_primary_pieces * RailConvertCost(type, totype));
|
||||||
|
} else {
|
||||||
|
cost.AddCost(RailConvertCost(type, totype));
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandCost ret = find_train_reservations(tile, GetTunnelBridgeReservationTrackBits(tile) & track_bits);
|
||||||
|
if (ret.Failed()) return ret;
|
||||||
|
if (across) {
|
||||||
|
ret = find_train_reservations(endtile, GetTunnelBridgeReservationTrackBits(endtile) & GetPrimaryTunnelBridgeTrackBits(endtile));
|
||||||
|
if (ret.Failed()) return ret;
|
||||||
|
}
|
||||||
|
if (across && (uint)(GetRailTypeInfo(type)->max_speed - 1) > (uint)(GetRailTypeInfo(totype)->max_speed - 1)) {
|
||||||
|
ret = CheckTrainInTunnelBridgePreventsTrackModification(tile, endtile);
|
||||||
|
if (ret.Failed()) return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & DC_EXEC) {
|
||||||
|
SubtractRailTunnelBridgeInfrastructure(tile, endtile);
|
||||||
|
|
||||||
|
if (across) {
|
||||||
|
SetRailType(tile, totype);
|
||||||
|
SetRailType(endtile, totype);
|
||||||
|
} else {
|
||||||
|
SetSecondaryRailType(tile, totype);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTrainPowerProcData data;
|
||||||
|
data.train_list = &affected_trains;
|
||||||
|
data.track_bits = track_bits;
|
||||||
|
if (across) {
|
||||||
|
FindVehicleOnPos(tile, VEH_TRAIN, &data, &UpdateTrainPowerProcAcrossTunnelBridge);
|
||||||
|
data.track_bits = GetPrimaryTunnelBridgeTrackBits(endtile);
|
||||||
|
FindVehicleOnPos(endtile, VEH_TRAIN, &data, &UpdateTrainPowerProcAcrossTunnelBridge);
|
||||||
|
} else {
|
||||||
|
FindVehicleOnPos(tile, VEH_TRAIN, &data, &UpdateTrainPowerProcOnTrackBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* notify YAPF about the track layout change */
|
||||||
|
yapf_notify_track_change(tile, track_bits);
|
||||||
|
if (across) yapf_notify_track_change(endtile, GetPrimaryTunnelBridgeTrackBits(endtile));
|
||||||
|
|
||||||
|
if (IsBridge(tile)) {
|
||||||
|
MarkBridgeDirty(tile);
|
||||||
|
} else {
|
||||||
|
MarkTileDirtyByTile(tile);
|
||||||
|
MarkTileDirtyByTile(endtile);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddRailTunnelBridgeInfrastructure(tile, endtile);
|
||||||
|
DirtyCompanyInfrastructureWindows(Company::Get(GetTileOwner(tile))->index);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: // MP_STATION, MP_ROAD
|
||||||
|
if (flags & DC_EXEC) {
|
||||||
|
YapfNotifyTrackLayoutChange(tile, track);
|
||||||
|
}
|
||||||
|
|
||||||
|
found_convertible_track = true;
|
||||||
|
cost.AddCost(RailConvertCost(type, totype));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint i = 0; i < vehicles_affected.size(); ++i) {
|
||||||
|
TryPathReserve(vehicles_affected[i], true);
|
||||||
|
}
|
||||||
|
} while (advance_tile());
|
||||||
|
|
||||||
|
if (flags & DC_EXEC) {
|
||||||
|
/* Railtype changed, update trains as when entering different track */
|
||||||
|
for (Train *v : affected_trains) {
|
||||||
|
v->ConsistChanged(CCF_TRACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found_convertible_track ? cost : error;
|
||||||
|
}
|
||||||
|
|
||||||
static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
|
static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
|
||||||
{
|
{
|
||||||
if (_current_company != OWNER_WATER) {
|
if (_current_company != OWNER_WATER) {
|
||||||
|
@@ -726,10 +726,12 @@ struct BuildRailToolbarWindow : Window {
|
|||||||
BuildRailClick_Remove(this);
|
BuildRailClick_Remove(this);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WID_RAT_CONVERT_RAIL:
|
case WID_RAT_CONVERT_RAIL: {
|
||||||
HandlePlacePushButton(this, WID_RAT_CONVERT_RAIL, GetRailTypeInfo(_cur_railtype)->cursor.convert, HT_RECT | HT_DIAGONAL);
|
bool active = HandlePlacePushButton(this, WID_RAT_CONVERT_RAIL, GetRailTypeInfo(_cur_railtype)->cursor.convert, _ctrl_pressed ? HT_RAIL : HT_RECT | HT_DIAGONAL);
|
||||||
|
if (active && _ctrl_pressed) _thd.square_palette = SPR_ZONING_INNER_HIGHLIGHT_GREEN;
|
||||||
this->last_user_action = widget;
|
this->last_user_action = widget;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: NOT_REACHED();
|
default: NOT_REACHED();
|
||||||
}
|
}
|
||||||
@@ -737,6 +739,14 @@ struct BuildRailToolbarWindow : Window {
|
|||||||
if (_ctrl_pressed) RailToolbar_CtrlChanged(this);
|
if (_ctrl_pressed) RailToolbar_CtrlChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void OnHover(Point pt, int widget) override
|
||||||
|
{
|
||||||
|
if (widget == WID_RAT_CONVERT_RAIL) {
|
||||||
|
uint64 args[] = { STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL };
|
||||||
|
GuiShowTooltips(this, STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_EXTRA, lengthof(args), args, TCC_HOVER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EventState OnHotkey(int hotkey) override
|
EventState OnHotkey(int hotkey) override
|
||||||
{
|
{
|
||||||
MarkTileDirtyByTile(TileVirtXY(_thd.pos.x, _thd.pos.y)); // redraw tile selection
|
MarkTileDirtyByTile(TileVirtXY(_thd.pos.x, _thd.pos.y)); // redraw tile selection
|
||||||
@@ -806,7 +816,11 @@ struct BuildRailToolbarWindow : Window {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case WID_RAT_CONVERT_RAIL:
|
case WID_RAT_CONVERT_RAIL:
|
||||||
|
if (_thd.place_mode & HT_RAIL) {
|
||||||
|
VpStartPlaceSizing(tile, VPM_RAILDIRS, DDSP_CONVERT_RAIL_TRACK);
|
||||||
|
} else {
|
||||||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_RAIL);
|
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_RAIL);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: NOT_REACHED();
|
default: NOT_REACHED();
|
||||||
@@ -847,6 +861,14 @@ struct BuildRailToolbarWindow : Window {
|
|||||||
DoCommandP(end_tile, start_tile, _cur_railtype | (_ctrl_pressed ? (1 << 6) : 0), CMD_CONVERT_RAIL | CMD_MSG(STR_ERROR_CAN_T_CONVERT_RAIL), CcPlaySound_CONSTRUCTION_RAIL);
|
DoCommandP(end_tile, start_tile, _cur_railtype | (_ctrl_pressed ? (1 << 6) : 0), CMD_CONVERT_RAIL | CMD_MSG(STR_ERROR_CAN_T_CONVERT_RAIL), CcPlaySound_CONSTRUCTION_RAIL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DDSP_CONVERT_RAIL_TRACK: {
|
||||||
|
Track track = (Track)(_thd.drawstyle & HT_DIR_MASK); // 0..5
|
||||||
|
TileIndex start_tile = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
|
||||||
|
TileIndex end_tile = TileVirtXY(_thd.selend.x, _thd.selend.y);
|
||||||
|
DoCommandP((_thd.drawstyle & HT_RAIL) ? end_tile : start_tile, end_tile, _cur_railtype | (track << 6), CMD_CONVERT_RAIL_TRACK | CMD_MSG(STR_ERROR_CAN_T_CONVERT_RAIL), CcPlaySound_CONSTRUCTION_RAIL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case DDSP_REMOVE_STATION:
|
case DDSP_REMOVE_STATION:
|
||||||
case DDSP_BUILD_STATION:
|
case DDSP_BUILD_STATION:
|
||||||
if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) {
|
if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) {
|
||||||
@@ -986,7 +1008,7 @@ static const NWidgetPart _nested_build_rail_widgets[] = {
|
|||||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_REMOVE),
|
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_REMOVE),
|
||||||
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_RAIL_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR),
|
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_RAIL_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR),
|
||||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_CONVERT_RAIL),
|
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_CONVERT_RAIL),
|
||||||
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL),
|
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_RAIL, 0),
|
||||||
EndContainer(),
|
EndContainer(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -632,6 +632,7 @@ static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
|
|||||||
TrackBits vehicle_track = Train::From(v)->track;
|
TrackBits vehicle_track = Train::From(v)->track;
|
||||||
if (!(vehicle_track & TRACK_BIT_WORMHOLE)) {
|
if (!(vehicle_track & TRACK_BIT_WORMHOLE)) {
|
||||||
if (info->mode == TBIFM_ACROSS_ONLY && !(GetAcrossBridgePossibleTrackBits(info->t) & vehicle_track)) return nullptr;
|
if (info->mode == TBIFM_ACROSS_ONLY && !(GetAcrossBridgePossibleTrackBits(info->t) & vehicle_track)) return nullptr;
|
||||||
|
if (info->mode == TBIFM_PRIMARY_ONLY && !(GetPrimaryTunnelBridgeTrackBits(info->t) & vehicle_track)) return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,7 +644,7 @@ static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
|
|||||||
* @param tile first end
|
* @param tile first end
|
||||||
* @param endtile second end
|
* @param endtile second end
|
||||||
* @param ignore Ignore this vehicle when searching
|
* @param ignore Ignore this vehicle when searching
|
||||||
* @param mode Whether to only find vehicles which are passing across the bridge/tunnel or on connecting bridge head track pieces
|
* @param mode Whether to only find vehicles which are passing across the bridge/tunnel or on connecting bridge head track pieces, or only on primary track type pieces
|
||||||
* @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
|
* @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
|
||||||
*/
|
*/
|
||||||
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore, TunnelBridgeIsFreeMode mode)
|
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore, TunnelBridgeIsFreeMode mode)
|
||||||
|
@@ -136,6 +136,7 @@ void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRF
|
|||||||
enum TunnelBridgeIsFreeMode {
|
enum TunnelBridgeIsFreeMode {
|
||||||
TBIFM_ALL,
|
TBIFM_ALL,
|
||||||
TBIFM_ACROSS_ONLY,
|
TBIFM_ACROSS_ONLY,
|
||||||
|
TBIFM_PRIMARY_ONLY,
|
||||||
};
|
};
|
||||||
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore = nullptr, TunnelBridgeIsFreeMode mode = TBIFM_ALL);
|
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore = nullptr, TunnelBridgeIsFreeMode mode = TBIFM_ALL);
|
||||||
Train *GetTrainClosestToTunnelBridgeEnd(TileIndex tile, TileIndex other_tile);
|
Train *GetTrainClosestToTunnelBridgeEnd(TileIndex tile, TileIndex other_tile);
|
||||||
|
@@ -186,6 +186,7 @@ enum ViewportDragDropSelectionProcess {
|
|||||||
DDSP_BUILD_STATION, ///< Station placement
|
DDSP_BUILD_STATION, ///< Station placement
|
||||||
DDSP_REMOVE_STATION, ///< Station removal
|
DDSP_REMOVE_STATION, ///< Station removal
|
||||||
DDSP_CONVERT_RAIL, ///< Rail conversion
|
DDSP_CONVERT_RAIL, ///< Rail conversion
|
||||||
|
DDSP_CONVERT_RAIL_TRACK, ///< Rail conversion (track)
|
||||||
|
|
||||||
/* Road specific actions */
|
/* Road specific actions */
|
||||||
DDSP_PLACE_ROAD_X_DIR, ///< Road placement (X axis)
|
DDSP_PLACE_ROAD_X_DIR, ///< Road placement (X axis)
|
||||||
|
Reference in New Issue
Block a user