Add: Road waypoint functionality

This commit is contained in:
Jonathan G Rennison 2024-04-23 19:17:21 +01:00
parent acb52a2af9
commit 3bcf5dfd8f
39 changed files with 708 additions and 154 deletions

View File

@ -990,7 +990,7 @@
</table>
</li>
<li>m6 bit 7: rail station / waypoint may have catenary pylons</li>
<li>m6 bits 6..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint)</li>
<li>m6 bits 6..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint, road waypoint)</li>
<li>m6 bit 2: pbs reservation state for railway stations/waypoints</li>
<li>m6 bit 1: rail station / waypoint may have catenary wires</li>
<li>m6 bit 0: rail station / waypoint is blocked</li>

View File

@ -207,6 +207,9 @@ enum Commands : uint16_t {
CMD_RENAME_WAYPOINT, ///< rename a waypoint
CMD_REMOVE_FROM_RAIL_WAYPOINT, ///< remove a (rectangle of) tiles from a rail waypoint
CMD_BUILD_ROAD_WAYPOINT, ///< build a road waypoint
CMD_REMOVE_FROM_ROAD_WAYPOINT, ///< remove a (rectangle of) tiles from a road waypoint
CMD_BUILD_ROAD_STOP, ///< build a road stop
CMD_REMOVE_ROAD_STOP, ///< remove a road stop
CMD_BUILD_LONG_ROAD, ///< build a complete road (not a "half" one)

View File

@ -2877,6 +2877,8 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}Build ro
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM :{BLACK}Build tramway section using the Autotram mode. Ctrl+Click to remove tramway section. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build road vehicle depot (for buying and servicing vehicles). Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Build tram vehicle depot (for buying and servicing vehicles). Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT :{BLACK}Convert road to waypoint. Ctrl enables joining waypoints. Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT :{BLACK}Convert tram to waypoint. Ctrl enables joining waypoints. Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}Build bus station. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION :{BLACK}Build passenger tram station. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY :{BLACK}Build lorry station. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only
@ -5084,11 +5086,14 @@ STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Adjoins
STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT :{WHITE}Too close to another waypoint
STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT :{WHITE}Can't build train waypoint here...
STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT :{WHITE}Can't build road waypoint here...
STR_ERROR_CAN_T_POSITION_BUOY_HERE :{WHITE}Can't place buoy here...
STR_ERROR_CAN_T_CHANGE_WAYPOINT_NAME :{WHITE}Can't change waypoint name...
STR_ERROR_CAN_T_REMOVE_TRAIN_WAYPOINT :{WHITE}Can't remove train waypoint here...
STR_ERROR_CAN_T_REMOVE_ROAD_WAYPOINT :{WHITE}Can't remove road waypoint here...
STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST :{WHITE}Must remove rail waypoint first
STR_ERROR_MUST_REMOVE_ROADWAYPOINT_FIRST :{WHITE}Must remove road waypoint first
STR_ERROR_BUOY_IN_THE_WAY :{WHITE}... buoy in the way
STR_ERROR_BUOY_IS_IN_USE :{WHITE}... buoy is in use by another company!
@ -5336,6 +5341,7 @@ STR_ERROR_NO_STOP_ARTICULATED_VEHICLE :{WHITE}There ar
STR_ERROR_AIRPORT_NO_PLANES :{WHITE}This plane cannot land at this heliport
STR_ERROR_AIRPORT_NO_HELICOPTERS :{WHITE}This helicopter cannot land at this airport
STR_ERROR_NO_RAIL_WAYPOINT :{WHITE}There is no railway waypoint
STR_ERROR_NO_ROAD_WAYPOINT :{WHITE}There is no road waypoint
STR_ERROR_NO_BUOY :{WHITE}There is no buoy
# Timetable related errors

View File

@ -437,6 +437,9 @@ uint32_t GetNearbyTileInformation(TileIndex tile, bool grf_version8)
/* Fake tile type for trees on shore */
if (IsTileType(tile, MP_TREES) && GetTreeGround(tile) == TREE_GROUND_SHORE) tile_type = MP_WATER;
/* Fake tile type for road waypoints */
if (IsRoadWaypointTile(tile)) tile_type = MP_ROAD;
auto [tileh, z] = GetTilePixelSlope(tile);
/* Return 0 if the tile is a land tile */
uint8_t terrain_type = (HasTileWaterClass(tile) ? (GetWaterClass(tile) + 1) & 3 : 0) << 5 | GetTerrainType(tile) << 2 | (tile_type == MP_WATER ? 1 : 0) << 1;

View File

@ -127,7 +127,7 @@ uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] u
if (this->tile == INVALID_TILE) return UINT_MAX;
TileIndex tile = this->tile;
if (parameter != 0) tile = GetNearbyTile(parameter, tile);
return (IsRoadStopTile(tile) && GetStationIndex(tile) == this->st->index) ? this->st->GetRoadStopAnimationFrame(tile) : UINT_MAX;
return (IsAnyRoadStopTile(tile) && GetStationIndex(tile) == this->st->index) ? this->st->GetRoadStopAnimationFrame(tile) : UINT_MAX;
}
/* Land info of nearby tile */
@ -143,7 +143,7 @@ uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] u
if (this->tile == INVALID_TILE) return 0xFFFFFFFF;
TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
if (!IsRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
if (!IsAnyRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
uint32_t grfid = this->st->roadstop_speclist[GetCustomRoadStopSpecIndex(this->tile)].grfid;
bool same_orientation = GetStationGfx(this->tile) == GetStationGfx(nearby_tile);
@ -151,6 +151,7 @@ uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] u
uint32_t res = GetStationGfx(nearby_tile) << 12 | !same_orientation << 11 | !!same_station << 10;
StationType type = GetStationType(nearby_tile);
if (type == STATION_TRUCK) res |= (1 << 16);
if (type == STATION_ROADWAYPOINT) res |= (2 << 16);
if (type == this->type) SetBit(res, 20);
if (IsCustomRoadStopSpecIndex(nearby_tile)) {
@ -165,7 +166,7 @@ uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] u
if (this->tile == INVALID_TILE) return 0xFFFFFFFF;
TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
if (!IsRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
if (!IsAnyRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
if (!IsCustomRoadStopSpecIndex(nearby_tile)) return 0;
const auto &sm = BaseStation::GetByTile(nearby_tile)->roadstop_speclist[GetCustomRoadStopSpecIndex(nearby_tile)];
@ -176,7 +177,7 @@ uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] u
case 0x6B: {
TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
if (!IsRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
if (!IsAnyRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
if (!IsCustomRoadStopSpecIndex(nearby_tile)) return 0xFFFE;
uint32_t grfid = this->st->roadstop_speclist[GetCustomRoadStopSpecIndex(this->tile)].grfid;

View File

@ -831,6 +831,14 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se
break;
}
case VEH_ROAD: {
if (!(wp->facilities & FACIL_BUS_STOP) || !(wp->facilities & FACIL_TRUCK_STOP)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_NO_ROAD_WAYPOINT);
ret = CheckOwnership(wp->owner);
if (ret.Failed()) return ret;
break;
}
case VEH_SHIP:
if (!(wp->facilities & FACIL_DOCK)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_NO_BUOY);
if (wp->owner != OWNER_NONE) {
@ -842,8 +850,8 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se
/* Order flags can be any of the following for waypoints:
* [non-stop]
* non-stop orders (if any) are only valid for trains */
if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN) return CMD_ERROR;
* non-stop orders (if any) are only valid for trains and road vehicles */
if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) return CMD_ERROR;
break;
}

View File

@ -435,6 +435,15 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
return order;
}
/* check road waypoint */
if (IsRoadWaypointTile(tile) &&
v->type == VEH_ROAD &&
IsTileOwner(tile, _local_company)) {
order.MakeGoToWaypoint(GetStationIndex(tile));
if (_settings_client.gui.new_nonstop != _ctrl_pressed) order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
return order;
}
/* check buoy (no ownership) */
if (IsBuoyTile(tile) && v->type == VEH_SHIP) {
order.MakeGoToWaypoint(GetStationIndex(tile));

View File

@ -225,7 +225,7 @@ protected:
/* special handling for stations */
if (IsRailTT() && HasStationTileRail(m_new_tile)) {
m_is_station = true;
} else if (IsRoadTT() && IsRoadStopTile(m_new_tile)) {
} else if (IsRoadTT() && IsStationRoadStopTile(m_new_tile)) {
m_is_station = true;
}
}

View File

@ -359,6 +359,7 @@ static int32_t NPFRoadPathCost(AyStar *, AyStarNode *current, OpenListNode *)
case MP_STATION: {
cost = NPF_TILE_LENGTH;
if (IsRoadWaypoint(tile)) break;
const RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
if (IsDriveThroughStopTile(tile)) {
/* Increase the cost for drive-through road stops */
@ -1132,7 +1133,7 @@ static void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, const Vehicle *
if (v->type == VEH_TRAIN) {
fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? STATION_RAIL : STATION_WAYPOINT;
} else if (v->type == VEH_ROAD) {
fstd->station_type = RoadVehicle::From(v)->IsBus() ? STATION_BUS : STATION_TRUCK;
fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? (RoadVehicle::From(v)->IsBus() ? STATION_BUS : STATION_TRUCK) : STATION_ROADWAYPOINT;
} else if (v->type == VEH_SHIP) {
fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? STATION_DOCK : STATION_BUOY;
}

View File

@ -70,6 +70,8 @@ protected:
break;
case MP_STATION: {
if (IsRoadWaypoint(tile)) break;
const RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
if (IsDriveThroughStopTile(tile)) {
/* Increase the cost for drive-through road stops */
@ -232,7 +234,7 @@ protected:
TileIndex m_destTile;
TrackdirBits m_destTrackdirs;
StationID m_dest_station;
bool m_bus;
StationType m_station_type;
bool m_non_artic;
public:
@ -240,8 +242,14 @@ public:
{
if (v->current_order.IsType(OT_GOTO_STATION)) {
m_dest_station = v->current_order.GetDestination();
m_bus = v->IsBus();
m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_bus ? STATION_BUS : STATION_TRUCK);
m_station_type = v->IsBus() ? STATION_BUS : STATION_TRUCK;
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_WAYPOINT)) {
m_dest_station = v->current_order.GetDestination();
m_station_type = STATION_ROADWAYPOINT;
m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_station_type);
m_non_artic = !v->HasArticulatedPart();
m_destTrackdirs = INVALID_TRACKDIR_BIT;
} else {
@ -275,7 +283,7 @@ public:
if (m_dest_station != INVALID_STATION) {
return IsTileType(tile, MP_STATION) &&
GetStationIndex(tile) == m_dest_station &&
(m_bus ? IsBusStop(tile) : IsTruckStop(tile)) &&
(m_station_type == GetStationType(tile)) &&
(m_non_artic || IsDriveThroughStopTile(tile));
}

View File

@ -163,7 +163,7 @@ static void PlaceRail_Waypoint(TileIndex tile)
return;
}
Axis axis = GetAxisForNewWaypoint(tile);
Axis axis = GetAxisForNewRailWaypoint(tile);
if (IsValidAxis(axis)) {
/* Valid tile for waypoints */
VpStartPlaceSizing(tile, axis == AXIS_X ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_STATION);
@ -776,7 +776,7 @@ struct BuildRailToolbarWindow : Window {
}
};
ShowSelectWaypointIfNeeded(ta, proc);
ShowSelectRailWaypointIfNeeded(ta, proc);
}
}
break;
@ -816,7 +816,7 @@ struct BuildRailToolbarWindow : Window {
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) CheckRedrawWaypointCoverage(this);
if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) CheckRedrawRailWaypointCoverage(this);
}
/**
@ -2203,7 +2203,7 @@ struct BuildRailWaypointWindow : PickerWindowBase {
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
CheckRedrawWaypointCoverage(this);
CheckRedrawRailWaypointCoverage(this);
}
};

View File

@ -1461,7 +1461,7 @@ void DrawRoadCatenary(const TileInfo *ti)
tram = road = (GetCrossingRailAxis(ti->tile) == AXIS_Y ? ROAD_X : ROAD_Y);
}
} else if (IsTileType(ti->tile, MP_STATION)) {
if (IsRoadStop(ti->tile)) {
if (IsAnyRoadStop(ti->tile)) {
if (IsDriveThroughStopTile(ti->tile)) {
Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
tram = road = (axis == AXIS_X ? ROAD_X : ROAD_Y);
@ -2448,7 +2448,7 @@ CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_s
TileType tt = GetTileType(tile);
switch (tt) {
case MP_STATION:
if (!IsRoadStop(tile)) continue;
if (!IsAnyRoadStop(tile)) continue;
break;
case MP_ROAD:
if (IsLevelCrossing(tile) && RoadNoLevelCrossing(to_type)) {

View File

@ -16,6 +16,7 @@
#include "command_func.h"
#include "road_cmd.h"
#include "station_func.h"
#include "waypoint_func.h"
#include "window_func.h"
#include "vehicle_func.h"
#include "sound_func.h"
@ -33,6 +34,7 @@
#include "strings_func.h"
#include "core/geometry_func.hpp"
#include "station_cmd.h"
#include "waypoint_cmd.h"
#include "road_cmd.h"
#include "tunnelbridge_cmd.h"
#include "newgrf_roadstop.h"
@ -236,6 +238,29 @@ static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType
ShowSelectStationIfNeeded(ta, proc);
}
/**
* Place a road waypoint.
* @param tile Position to start dragging a waypoint.
*/
static void PlaceRoad_Waypoint(TileIndex tile)
{
if (_remove_button_clicked) {
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_ROAD_WAYPOINT);
return;
}
Axis axis = GetAxisForNewRoadWaypoint(tile);
if (IsValidAxis(axis)) {
/* Valid tile for waypoints */
VpStartPlaceSizing(tile, axis == AXIS_X ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_ROAD_WAYPOINT);
VpSetPlaceSizingLimit(_settings_game.station.station_spread);
} else {
/* Tile where we can't build road waypoints. This is always going to fail,
* but provides the user with a proper error message. */
Command<CMD_BUILD_ROAD_WAYPOINT>::Post(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT, tile, AXIS_X, 1, 1, ROADSTOP_CLASS_WAYP, 0, INVALID_STATION, false);
}
}
/**
* Callback for placing a bus station.
* @param tile Position to place the station.
@ -349,22 +374,26 @@ struct BuildRoadToolbarWindow : Window {
bool can_build = CanBuildVehicleInfrastructure(VEH_ROAD, rtt);
this->SetWidgetsDisabledState(!can_build,
WID_ROT_DEPOT,
WID_ROT_BUILD_WAYPOINT,
WID_ROT_BUS_STATION,
WID_ROT_TRUCK_STATION);
if (!can_build) {
CloseWindowById(WC_BUS_STATION, TRANSPORT_ROAD);
CloseWindowById(WC_TRUCK_STATION, TRANSPORT_ROAD);
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD);
CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_ROAD);
}
if (_game_mode != GM_EDITOR) {
if (!can_build) {
/* Show in the tooltip why this button is disabled. */
this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
} else {
this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT);
this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT : STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT);
this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION);
this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION);
}
@ -444,6 +473,7 @@ struct BuildRoadToolbarWindow : Window {
case WID_ROT_BUS_STATION:
case WID_ROT_TRUCK_STATION:
case WID_ROT_BUILD_WAYPOINT:
if (RoadTypeIsRoad(this->roadtype)) this->DisableWidget(WID_ROT_ONE_WAY);
this->SetWidgetDisabledState(WID_ROT_REMOVE, !this->IsWidgetLowered(clicked_widget));
break;
@ -504,6 +534,12 @@ struct BuildRoadToolbarWindow : Window {
}
break;
case WID_ROT_BUILD_WAYPOINT:
if (HandlePlacePushButton(this, WID_ROT_BUILD_WAYPOINT, SPR_CURSOR_WAYPOINT, HT_RECT)) {
this->last_started_action = widget;
}
break;
case WID_ROT_BUS_STATION:
if (HandlePlacePushButton(this, WID_ROT_BUS_STATION, SPR_CURSOR_BUS_STATION, HT_RECT)) {
ShowRVStationPicker(this, ROADSTOP_BUS);
@ -593,6 +629,10 @@ struct BuildRoadToolbarWindow : Window {
tile, _cur_roadtype, _road_depot_orientation);
break;
case WID_ROT_BUILD_WAYPOINT:
PlaceRoad_Waypoint(tile);
break;
case WID_ROT_BUS_STATION:
PlaceRoad_BusStation(tile);
break;
@ -634,6 +674,7 @@ struct BuildRoadToolbarWindow : Window {
CloseWindowById(WC_BUS_STATION, TRANSPORT_ROAD);
CloseWindowById(WC_TRUCK_STATION, TRANSPORT_ROAD);
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD);
CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_ROAD);
CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowByClass(WC_BUILD_BRIDGE);
}
@ -706,6 +747,30 @@ struct BuildRoadToolbarWindow : Window {
break;
}
case DDSP_BUILD_ROAD_WAYPOINT:
case DDSP_REMOVE_ROAD_WAYPOINT:
if (this->IsWidgetLowered(WID_ROT_BUILD_WAYPOINT)) {
if (_remove_button_clicked) {
Command<CMD_REMOVE_FROM_ROAD_WAYPOINT>::Post(STR_ERROR_CAN_T_REMOVE_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile);
} else {
TileArea ta(start_tile, end_tile);
Axis axis = select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y;
bool adjacent = _ctrl_pressed;
uint16_t waypoint_type = 0;
auto proc = [=](bool test, StationID to_join) -> bool {
if (test) {
return Command<CMD_BUILD_ROAD_WAYPOINT>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_ROAD_WAYPOINT>()), ta.tile, axis, ta.w, ta.h, ROADSTOP_CLASS_WAYP, waypoint_type, INVALID_STATION, adjacent).Succeeded();
} else {
return Command<CMD_BUILD_ROAD_WAYPOINT>::Post(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, axis, ta.w, ta.h, ROADSTOP_CLASS_WAYP, waypoint_type, to_join, adjacent);
}
};
ShowSelectRoadWaypointIfNeeded(ta, proc);
}
}
break;
case DDSP_BUILD_BUSSTOP:
case DDSP_REMOVE_BUSSTOP:
if (this->IsWidgetLowered(WID_ROT_BUS_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_BUS, _cur_roadtype)) {
@ -749,6 +814,11 @@ struct BuildRoadToolbarWindow : Window {
return ES_NOT_HANDLED;
}
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
if (this->IsWidgetLowered(WID_ROT_BUILD_WAYPOINT)) CheckRedrawRoadWaypointCoverage(this);
}
/**
* Handler for global hotkeys of the BuildRoadToolbarWindow.
* @param hotkey Hotkey
@ -797,6 +867,7 @@ struct BuildRoadToolbarWindow : Window {
Hotkey('6', "bus_station", WID_ROT_BUS_STATION),
Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION),
Hotkey('8', "oneway", WID_ROT_ONE_WAY),
Hotkey('9', "waypoint", WID_ROT_BUILD_WAYPOINT),
Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE),
Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL),
Hotkey('R', "remove", WID_ROT_REMOVE),
@ -811,6 +882,7 @@ struct BuildRoadToolbarWindow : Window {
Hotkey('5', "depot", WID_ROT_DEPOT),
Hotkey('6', "bus_station", WID_ROT_BUS_STATION),
Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION),
Hotkey('9', "waypoint", WID_ROT_BUILD_WAYPOINT),
Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE),
Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL),
Hotkey('R', "remove", WID_ROT_REMOVE),
@ -837,6 +909,8 @@ static constexpr NWidgetPart _nested_build_road_widgets[] = {
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRUCK_BAY, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
@ -878,6 +952,8 @@ static constexpr NWidgetPart _nested_build_tramway_widgets[] = {
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION),

View File

@ -44,7 +44,7 @@ RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge
}
case MP_STATION:
if (!IsRoadStopTile(tile)) return ROAD_NONE;
if (!IsAnyRoadStopTile(tile)) return ROAD_NONE;
if (IsDriveThroughStopTile(tile)) return (GetRoadStopDir(tile) == DIAGDIR_NE) ? ROAD_X : ROAD_Y;
return DiagDirToRoadBits(GetRoadStopDir(tile));

View File

@ -1328,7 +1328,7 @@ again:
v->tile != tile) {
/* So, keep 'our' state */
dir = (Trackdir)v->state;
} else if (IsRoadStop(v->tile)) {
} else if (IsStationRoadStop(v->tile)) {
/* We're not continuing our drive through road stop, so leave. */
RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
}

View File

@ -907,7 +907,7 @@ bool AfterLoadGame()
}
}
if (IsSavegameVersionBeforeOrAt(SLV_VEHICLE_ECONOMY_AGE)) {
if (IsSavegameVersionBefore(SLV_ROAD_WAYPOINTS)) {
/* Expansion of station type field in m6 */
for (auto t : Map::Iterate()) {
if (IsTileType(t, MP_STATION)) {
@ -1091,7 +1091,7 @@ bool AfterLoadGame()
break;
case MP_STATION:
if (IsRoadStop(t)) SB(t.m7(), 6, 2, 1);
if (IsStationRoadStop(t)) SB(t.m7(), 6, 2, 1);
break;
case MP_TUNNELBRIDGE:
@ -1145,7 +1145,7 @@ bool AfterLoadGame()
break;
case MP_STATION:
if (!IsRoadStop(t)) break;
if (!IsStationRoadStop(t)) break;
if (fix_roadtypes) SB(t.m7(), 6, 2, (RoadTypes)GB(t.m3(), 0, 3));
SB(t.m7(), 0, 5, HasBit(t.m6(), 2) ? OWNER_TOWN : GetTileOwner(t));
@ -1296,7 +1296,7 @@ bool AfterLoadGame()
has_road = true;
break;
case MP_STATION:
has_road = IsRoadStop(t);
has_road = IsAnyRoadStop(t);
break;
case MP_TUNNELBRIDGE:
has_road = GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD;

View File

@ -151,7 +151,8 @@ void AfterLoadCompanyStats()
break;
case STATION_BUS:
case STATION_TRUCK: {
case STATION_TRUCK:
case STATION_ROADWAYPOINT: {
/* Iterate all present road types as each can have a different owner. */
for (RoadTramType rtt : _roadtramtypes) {
RoadType rt = GetRoadType(tile, rtt);

View File

@ -379,6 +379,8 @@ enum SaveLoadVersion : uint16_t {
SLV_SCRIPT_RANDOMIZER, ///< 333 PR#12063 v14.0-RC1 Save script randomizers.
SLV_VEHICLE_ECONOMY_AGE, ///< 334 PR#12141 v14.0 Add vehicle age in economy year, for profit stats minimum age
SLV_ROAD_WAYPOINTS, ///< 335 PR#12572 Road waypoints
SL_MAX_VERSION, ///< Highest possible saveload version
};

View File

@ -655,6 +655,10 @@ public:
SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION),
SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION),
SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION),
SLE_CONDVAR(Waypoint, waypoint_flags, SLE_UINT16, SLV_ROAD_WAYPOINTS, SL_MAX_VERSION),
SLE_CONDVAR(Waypoint, road_waypoint_area.tile, SLE_UINT32, SLV_ROAD_WAYPOINTS, SL_MAX_VERSION),
SLE_CONDVAR(Waypoint, road_waypoint_area.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_ROAD_WAYPOINTS, SL_MAX_VERSION),
SLE_CONDVAR(Waypoint, road_waypoint_area.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_ROAD_WAYPOINTS, SL_MAX_VERSION),
};
inline const static SaveLoadCompatTable compat_description = _station_waypoint_sl_compat;

View File

@ -54,7 +54,7 @@
if (!::IsValidTile(tile)) return false;
if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
return ::IsRoadStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
return ::IsStationRoadStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
}
/* static */ bool ScriptRoad::IsDriveThroughRoadStationTile(TileIndex tile)
@ -604,7 +604,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD
EnforceCompanyModeValid(false);
EnforcePrecondition(false, ::IsValidTile(tile));
EnforcePrecondition(false, IsTileType(tile, MP_STATION));
EnforcePrecondition(false, IsRoadStop(tile));
EnforcePrecondition(false, IsStationRoadStop(tile));
return ScriptObject::Command<CMD_REMOVE_ROAD_STOP>::Do(tile, 1, 1, GetRoadStopType(tile), false);
}

View File

@ -327,13 +327,15 @@ static uint GetTileCatchmentRadius(TileIndex tile, const Station *st)
default: NOT_REACHED();
case STATION_BUOY:
case STATION_WAYPOINT: return CA_NONE;
case STATION_WAYPOINT:
case STATION_ROADWAYPOINT: return CA_NONE;
}
} else {
switch (GetStationType(tile)) {
default: return CA_UNMODIFIED;
case STATION_BUOY:
case STATION_WAYPOINT: return CA_NONE;
case STATION_WAYPOINT:
case STATION_ROADWAYPOINT: return CA_NONE;
}
}
}

View File

@ -511,7 +511,7 @@ public:
inline bool TileBelongsToRoadStop(TileIndex tile) const
{
return IsRoadStopTile(tile) && GetStationIndex(tile) == this->index;
return IsStationRoadStopTile(tile) && GetStationIndex(tile) == this->index;
}
inline bool TileBelongsToAirport(TileIndex tile) const

View File

@ -111,10 +111,11 @@ bool IsHangar(Tile t)
* @param closest_station the closest owned station found so far
* @param company the company whose stations to look for
* @param st to 'return' the found station
* @param filter Filter function
* @return Succeeded command (if zero or one station found) or failed command (for two or more stations found).
*/
template <class T>
CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st)
template <class T, class F>
CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
{
ta.Expand(1);
@ -122,7 +123,7 @@ CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID c
for (TileIndex tile_cur : ta) {
if (IsTileType(tile_cur, MP_STATION)) {
StationID t = GetStationIndex(tile_cur);
if (!T::IsValidID(t) || Station::Get(t)->owner != company) continue;
if (!T::IsValidID(t) || T::Get(t)->owner != company || !filter(T::Get(t))) continue;
if (closest_station == INVALID_STATION) {
closest_station = t;
} else if (closest_station != t) {
@ -959,13 +960,13 @@ static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_
* @param flags Operation to perform.
* @param invalid_dirs Prohibited directions (set of DiagDirections).
* @param is_drive_through True if trying to build a drive-through station.
* @param is_truck_stop True when building a truck stop, false otherwise.
* @param station_type Station type (bus, truck or road waypoint).
* @param axis Axis of a drive-through road stop.
* @param station StationID to be queried and returned if available.
* @param rt Road type to build.
* @param rt Road type to build, may be INVALID_ROADTYPE if an existing road is required.
* @return The cost in case of success, or an error code if it failed.
*/
static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadType rt)
CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
@ -977,10 +978,10 @@ static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoC
* Station points to INVALID_STATION if we can build on any station.
* Or it points to a station if we're only allowed to build on exactly that station. */
if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
if (!IsRoadStop(cur_tile)) {
if (!IsAnyRoadStop(cur_tile)) {
return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
} else {
if (is_truck_stop != IsTruckStop(cur_tile) ||
if (station_type != GetStationType(cur_tile) ||
is_drive_through != IsDriveThroughStopTile(cur_tile)) {
return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
}
@ -1027,7 +1028,7 @@ static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoC
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
if (RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
ret = CheckOwnership(road_owner);
@ -1035,7 +1036,7 @@ static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoC
}
cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
} else if (RoadTypeIsRoad(rt)) {
} else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
@ -1053,12 +1054,14 @@ static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoC
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
if (RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
} else if (RoadTypeIsTram(rt)) {
} else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
} else if (rt == INVALID_ROADTYPE) {
return_cmd_error(STR_ERROR_THERE_IS_NO_ROAD);
} else {
ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
if (ret.Failed()) return ret;
@ -1149,6 +1152,7 @@ void GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const Stat
* Find a nearby station that joins this station.
* @tparam T the class to find a station for
* @tparam error_message the error message when building a station on top of others
* @tparam F the filter functor type
* @param existing_station an existing station we build over
* @param station_to_join the station to join to
* @param adjacent whether adjacent stations are allowed
@ -1156,8 +1160,8 @@ void GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const Stat
* @param st 'return' pointer for the found station
* @return command cost with the error or 'okay'
*/
template <class T, StringID error_message>
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st)
template <class T, StringID error_message, class F>
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
{
assert(*st == nullptr);
bool check_surrounding = true;
@ -1171,7 +1175,8 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station
} else {
/* Extend the current station, and don't check whether it will
* be near any other stations. */
*st = T::GetIfValid(existing_station);
T *candidate = T::GetIfValid(existing_station);
if (candidate != nullptr && filter(candidate)) *st = candidate;
check_surrounding = (*st == nullptr);
}
} else {
@ -1183,7 +1188,7 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station
if (check_surrounding) {
/* Make sure there is no more than one other station around us that is owned by us. */
CommandCost ret = GetStationAround(ta, existing_station, _current_company, st);
CommandCost ret = GetStationAround(ta, existing_station, _current_company, st, filter);
if (ret.Failed()) return ret;
}
@ -1204,7 +1209,7 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station
*/
static CommandCost FindJoiningStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
{
return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st);
return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
}
/**
@ -1214,11 +1219,16 @@ static CommandCost FindJoiningStation(StationID existing_station, StationID stat
* @param adjacent whether adjacent waypoints are allowed
* @param ta the area of the newly build waypoint
* @param wp 'return' pointer for the found waypoint
* @param is_road whether to find a road waypoint
* @return command cost with the error or 'okay'
*/
CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp)
CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road)
{
return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp);
if (is_road) {
return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_ROADWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp, [](const Waypoint *wp) -> bool { return HasBit(wp->waypoint_flags, WPF_ROAD); });
} else {
return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp, [](const Waypoint *wp) -> bool { return !HasBit(wp->waypoint_flags, WPF_ROAD); });
}
}
/**
@ -1605,6 +1615,16 @@ static void MakeShipStationAreaSmaller(Station *st)
UpdateStationDockingTiles(st);
}
static bool TileBelongsToRoadWaypointStation(BaseStation *st, TileIndex tile)
{
return IsRoadWaypointTile(tile) && GetStationIndex(tile) == st->index;
}
void MakeRoadWaypointStationAreaSmaller(BaseStation *st, TileArea &road_waypoint_area)
{
road_waypoint_area = MakeStationAreaSmaller(st, road_waypoint_area, TileBelongsToRoadWaypointStation);
}
/**
* Remove a number of tiles from any rail station within the area.
* @param ta the area to clear station tile from.
@ -1864,6 +1884,7 @@ static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
}
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index = -1);
CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index = -1);
/**
* Find a nearby station that joins this road stop.
@ -1876,7 +1897,7 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int repla
*/
static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID station_to_join, bool adjacent, TileArea ta, Station **st)
{
return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st);
return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
}
/**
@ -1884,15 +1905,15 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio
* @param tile_area Area to check.
* @param flags Operation to perform.
* @param is_drive_through True if trying to build a drive-through station.
* @param is_truck_stop True when building a truck stop, false otherwise.
* @param station_type Station type (bus, truck or road waypoint).
* @param axis Axis of a drive-through road stop.
* @param ddir Entrance direction (#DiagDirection) for normal stops. Converted to the axis for drive-through stops.
* @param station StationID to be queried and returned if available.
* @param rt Road type to build.
* @param rt Road type to build, may be INVALID_ROADTYPE if an existing road is required.
* @param unit_cost The cost to build one road stop of the current type.
* @return The cost in case of success, or an error code if it failed.
*/
static CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlag flags, bool is_drive_through, bool is_truck_stop, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlag flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
{
uint invalid_dirs = 0;
if (is_drive_through) {
@ -1906,10 +1927,10 @@ static CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlag flags
int allowed_z = -1;
CommandCost cost(EXPENSES_CONSTRUCTION);
for (TileIndex cur_tile : tile_area) {
CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, flags, invalid_dirs, is_drive_through, is_truck_stop, axis, est, rt);
CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt);
if (ret.Failed()) return ret;
bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile);
bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile);
/* Only add costs if a stop doesn't already exist in the location */
if (!is_preexisting_roadstop) {
@ -1985,7 +2006,7 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8_t width,
unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
}
StationID est = INVALID_STATION;
CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop, axis, ddir, &est, rt, unit_cost);
CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? STATION_TRUCK : STATION_BUS, axis, ddir, &est, rt, unit_cost);
if (cost.Failed()) return cost;
Station *st = nullptr;
@ -2021,7 +2042,7 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8_t width,
Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
if (IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile)) {
if (IsTileType(cur_tile, MP_STATION) && IsStationRoadStop(cur_tile)) {
RemoveRoadStop(cur_tile, flags, specindex);
}
@ -2059,7 +2080,7 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8_t width,
if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, rs_type, road_rt, tram_rt, axis);
MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, (rs_type == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), road_rt, tram_rt, axis);
road_stop->MakeDriveThrough();
} else {
if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
@ -2219,6 +2240,132 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int repla
return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
}
/**
* Remove a road waypoint
* @param tile TileIndex been queried
* @param flags operation to perform
* @param replacement_spec_index replacement spec index to avoid deallocating, if < 0, tile is not being replaced
* @return cost or failure of operation
*/
CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index)
{
Waypoint *wp = Waypoint::GetByTile(tile);
if (_current_company != OWNER_WATER) {
CommandCost ret = CheckOwnership(wp->owner);
if (ret.Failed()) return ret;
}
/* don't do the check for drive-through road stops when company bankrupts */
if (!(flags & DC_BANKRUPT)) {
CommandCost ret = EnsureNoVehicleOnGround(tile);
if (ret.Failed()) return ret;
}
const RoadStopSpec *spec = GetRoadStopSpec(tile);
if (flags & DC_EXEC) {
/* Update company infrastructure counts. */
for (RoadTramType rtt : _roadtramtypes) {
RoadType rt = GetRoadType(tile, rtt);
UpdateCompanyRoadInfrastructure(rt, GetRoadOwner(tile, rtt), -static_cast<int>(ROAD_STOP_TRACKBIT_FACTOR));
}
Company::Get(wp->owner)->infrastructure.station--;
DirtyCompanyInfrastructureWindows(wp->owner);
DeleteAnimatedTile(tile);
uint specindex = GetCustomRoadStopSpecIndex(tile);
DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
DoClearSquare(tile);
wp->rect.AfterRemoveTile(wp, tile);
wp->RemoveRoadStopTileData(tile);
if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
if (replacement_spec_index < 0) {
MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
UpdateStationSignCoord(wp);
/* if we deleted the whole waypoint, delete the road facility. */
if (wp->road_waypoint_area.tile == INVALID_TILE) {
wp->facilities &= ~(FACIL_BUS_STOP | FACIL_TRUCK_STOP);
SetWindowWidgetDirty(WC_STATION_VIEW, wp->index, WID_SV_ROADVEHS);
wp->UpdateVirtCoord();
DeleteStationIfEmpty(wp);
}
}
}
return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(PR_CLEAR_STATION_TRUCK) : _price[PR_CLEAR_STATION_TRUCK]);
}
/**
* Remove a tile area of road stop or road waypoints
* @param flags operation to perform
* @param roadstop_area tile area of road stop or road waypoint tiles to remove
* @param station_type station type to remove
* @param remove_road Remove roads of drive-through stops?
* @return the cost of this operation or an error
*/
static CommandCost RemoveGenericRoadStop(DoCommandFlag flags, const TileArea &roadstop_area, StationType station_type, bool remove_road)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
bool had_success = false;
for (TileIndex cur_tile : roadstop_area) {
/* Make sure the specified tile is a road stop of the correct type */
if (!IsTileType(cur_tile, MP_STATION) || !IsAnyRoadStop(cur_tile) || GetStationType(cur_tile) != station_type) continue;
/* Save information on to-be-restored roads before the stop is removed. */
RoadBits road_bits = ROAD_NONE;
RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
if (IsDriveThroughStopTile(cur_tile)) {
for (RoadTramType rtt : _roadtramtypes) {
road_type[rtt] = GetRoadType(cur_tile, rtt);
if (road_type[rtt] == INVALID_ROADTYPE) continue;
road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
/* If we don't want to preserve our roads then restore only roads of others. */
if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
}
road_bits = AxisToRoadBits(DiagDirToAxis(GetRoadStopDir(cur_tile)));
}
CommandCost ret;
if (station_type == STATION_ROADWAYPOINT) {
ret = RemoveRoadWaypointStop(cur_tile, flags);
} else {
ret = RemoveRoadStop(cur_tile, flags);
}
if (ret.Failed()) {
last_error = ret;
continue;
}
cost.AddCost(ret);
had_success = true;
/* Restore roads. */
if ((flags & DC_EXEC) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
/* Update company infrastructure counts. */
int count = CountBits(road_bits);
UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
}
}
return had_success ? cost : last_error;
}
/**
* Remove bus or truck stops.
* @param flags Operation to perform.
@ -2241,50 +2388,24 @@ CommandCost CmdRemoveRoadStop(DoCommandFlag flags, TileIndex tile, uint8_t width
TileArea roadstop_area(tile, width, height);
CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
bool had_success = false;
return RemoveGenericRoadStop(flags, roadstop_area, stop_type == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK, remove_road);
}
for (TileIndex cur_tile : roadstop_area) {
/* Make sure the specified tile is a road stop of the correct type */
if (!IsTileType(cur_tile, MP_STATION) || !IsRoadStop(cur_tile) || GetRoadStopType(cur_tile) != stop_type) continue;
/**
* Remove road waypoints.
* @param flags operation to perform
* @param start tile of road waypoint piece to remove
* @param end other edge of the rect to remove
* @return the cost of this operation or an error
*/
CommandCost CmdRemoveFromRoadWaypoint(DoCommandFlag flags, TileIndex start, TileIndex end)
{
if (end == 0) end = start;
if (start >= Map::Size() || end >= Map::Size()) return CMD_ERROR;
/* Save information on to-be-restored roads before the stop is removed. */
RoadBits road_bits = ROAD_NONE;
RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
if (IsDriveThroughStopTile(cur_tile)) {
for (RoadTramType rtt : _roadtramtypes) {
road_type[rtt] = GetRoadType(cur_tile, rtt);
if (road_type[rtt] == INVALID_ROADTYPE) continue;
road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
/* If we don't want to preserve our roads then restore only roads of others. */
if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
}
road_bits = AxisToRoadBits(DiagDirToAxis(GetRoadStopDir(cur_tile)));
}
TileArea roadstop_area(start, end);
CommandCost ret = RemoveRoadStop(cur_tile, flags);
if (ret.Failed()) {
last_error = ret;
continue;
}
cost.AddCost(ret);
had_success = true;
/* Restore roads. */
if ((flags & DC_EXEC) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
/* Update company infrastructure counts. */
int count = CountBits(road_bits);
UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
}
}
return had_success ? cost : last_error;
return RemoveGenericRoadStop(flags, roadstop_area, STATION_ROADWAYPOINT, false);
}
/**
@ -3154,7 +3275,7 @@ draw_default_foundation:
draw_ground = true;
}
if (draw_ground && !IsRoadStop(ti->tile)) {
if (draw_ground && !IsStationRoadStop(ti->tile)) {
SpriteID image = t->ground.sprite;
PaletteID pal = t->ground.pal;
RailTrackOffset overlay_offset;
@ -3181,7 +3302,7 @@ draw_default_foundation:
if (HasStationRail(ti->tile) && HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
if (IsRoadStop(ti->tile)) {
if (IsStationRoadStop(ti->tile)) {
RoadType road_rt = GetRoadTypeRoad(ti->tile);
RoadType tram_rt = GetRoadTypeTram(ti->tile);
const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
@ -3383,7 +3504,7 @@ static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
td->owner[0] = GetTileOwner(tile);
td->build_date = BaseStation::GetByTile(tile)->build_date;
if (IsRoadStop(tile)) FillTileDescRoadStop(tile, td);
if (IsAnyRoadStop(tile)) FillTileDescRoadStop(tile, td);
if (HasStationRail(tile)) FillTileDescRailStation(tile, td);
if (IsAirport(tile)) FillTileDescAirport(tile, td);
@ -3407,6 +3528,7 @@ static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
case STATION_DOCK: str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
case STATION_BUOY: str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
case STATION_WAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
case STATION_ROADWAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
}
td->str = str;
}
@ -3435,7 +3557,7 @@ static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode
break;
case TRANSPORT_ROAD:
if (IsRoadStop(tile)) {
if (IsAnyRoadStop(tile)) {
RoadTramType rtt = (RoadTramType)sub_mode;
if (!HasTileRoadType(tile, rtt)) break;
@ -3493,7 +3615,7 @@ static void AnimateTile_Station(TileIndex tile)
return;
}
if (IsRoadStopTile(tile)) {
if (IsAnyRoadStopTile(tile)) {
AnimateRoadStopTile(tile);
return;
}
@ -3553,7 +3675,7 @@ static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, i
} else if (v->type == VEH_ROAD) {
RoadVehicle *rv = RoadVehicle::From(v);
if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
if (IsRoadStop(tile) && rv->IsFrontEngine()) {
if (IsStationRoadStop(tile) && rv->IsFrontEngine()) {
/* Attempt to allocate a parking bay in a road stop */
return RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv) ? VETSB_CONTINUE : VETSB_CANNOT_ENTER;
}
@ -4379,7 +4501,7 @@ void DeleteOilRig(TileIndex tile)
static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
{
if (IsRoadStopTile(tile)) {
if (IsAnyRoadStopTile(tile)) {
for (RoadTramType rtt : _roadtramtypes) {
/* Update all roadtypes, no matter if they are present */
if (GetRoadOwner(tile, rtt) == old_owner) {
@ -4416,6 +4538,7 @@ static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_o
case STATION_BUS:
case STATION_TRUCK:
case STATION_ROADWAYPOINT:
/* Road stops were already handled above. */
break;
@ -4443,7 +4566,11 @@ static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_o
} else {
if (IsDriveThroughStopTile(tile)) {
/* Remove the drive-through road stop */
Command<CMD_REMOVE_ROAD_STOP>::Do(DC_EXEC | DC_BANKRUPT, tile, 1, 1, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, false);
if (IsRoadWaypoint(tile)) {
Command<CMD_REMOVE_FROM_ROAD_WAYPOINT>::Do(DC_EXEC | DC_BANKRUPT, tile, tile);
} else {
Command<CMD_REMOVE_ROAD_STOP>::Do(DC_EXEC | DC_BANKRUPT, tile, 1, 1, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, false);
}
assert(IsTileType(tile, MP_ROAD));
/* Change owner of tile and all roadtypes */
ChangeTileOwner(tile, old_owner, new_owner);
@ -4510,6 +4637,7 @@ CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
case STATION_AIRPORT: return_cmd_error(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
case STATION_BUS: return_cmd_error(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
case STATION_ROADWAYPOINT: return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
case STATION_BUOY: return_cmd_error(STR_ERROR_BUOY_IN_THE_WAY);
case STATION_DOCK: return_cmd_error(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
case STATION_OILRIG:
@ -4529,6 +4657,12 @@ CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
if (remove_road.Failed()) return remove_road;
}
return RemoveRoadStop(tile, flags);
case STATION_ROADWAYPOINT:
if (IsDriveThroughStopTile(tile)) {
CommandCost remove_road = CanRemoveRoadWithStop(tile, flags);
if (remove_road.Failed()) return remove_road;
}
return RemoveRoadWaypointStop(tile, flags);
case STATION_BUOY: return RemoveBuoy(tile, flags);
case STATION_DOCK: return RemoveDock(tile, flags);
default: break;

View File

@ -47,17 +47,22 @@ struct StationTypeFilter
static bool IsValidID(StationID id) { return Station::IsValidID(id); }
static bool IsValidBaseStation(const BaseStation *st) { return Station::IsExpected(st); }
static bool IsAcceptableWaypointTile(TileIndex) { return false; }
static constexpr bool IsWaypoint() { return false; }
};
struct WaypointTypeFilter
template <bool ROAD, TileType TILE_TYPE>
struct GenericWaypointTypeFilter
{
using StationType = Waypoint;
static bool IsValidID(StationID id) { return Waypoint::IsValidID(id); }
static bool IsValidBaseStation(const BaseStation *st) { return Waypoint::IsExpected(st); }
static bool IsValidID(StationID id) { return Waypoint::IsValidID(id) && HasBit(Waypoint::Get(id)->waypoint_flags, WPF_ROAD) == ROAD; }
static bool IsValidBaseStation(const BaseStation *st) { return Waypoint::IsExpected(st) && HasBit(Waypoint::From(st)->waypoint_flags, WPF_ROAD) == ROAD; }
static bool IsAcceptableWaypointTile(TileIndex tile) { return IsTileType(tile, TILE_TYPE); }
static constexpr bool IsWaypoint() { return true; }
};
using RailWaypointTypeFilter = GenericWaypointTypeFilter<false, MP_RAILWAY>;
using RoadWaypointTypeFilter = GenericWaypointTypeFilter<true, MP_ROAD>;
/**
* Calculates and draws the accepted or supplied cargo around the selected tile(s)
@ -125,8 +130,8 @@ void FindStationsAroundSelection()
uint x = TileX(location.tile);
uint y = TileY(location.tile);
/* Waypoints can only be built on existing rail tiles, so don't extend area if not highlighting a rail tile. */
int max_c = T::IsWaypoint() && !IsTileType(location.tile, MP_RAILWAY) ? 0 : 1;
/* Waypoints can only be built on existing rail/road tiles, so don't extend area if not highlighting a rail tile. */
int max_c = T::IsWaypoint() && !T::IsAcceptableWaypointTile(location.tile) ? 0 : 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)));
typename T::StationType *adjacent = nullptr;
@ -171,7 +176,8 @@ void CheckRedrawStationCoverage(const Window *w)
}
}
void CheckRedrawWaypointCoverage(const Window *)
template <typename T>
void CheckRedrawWaypointCoverage()
{
/* Test if ctrl state changed */
static bool _last_ctrl_pressed;
@ -184,11 +190,21 @@ void CheckRedrawWaypointCoverage(const Window *)
_thd.dirty &= ~1;
if (_thd.drawstyle == HT_RECT) {
FindStationsAroundSelection<WaypointTypeFilter>();
FindStationsAroundSelection<T>();
}
}
}
void CheckRedrawRailWaypointCoverage(const Window *)
{
CheckRedrawWaypointCoverage<RailWaypointTypeFilter>();
}
void CheckRedrawRoadWaypointCoverage(const Window *)
{
CheckRedrawWaypointCoverage<RoadWaypointTypeFilter>();
}
/**
* Draw small boxes of cargo amount and ratings data at the given
* coordinates. If amount exceeds 576 units, it is shown 'full', same
@ -2488,11 +2504,21 @@ void ShowSelectStationIfNeeded(TileArea ta, StationPickerCmdProc proc)
}
/**
* Show the waypoint selection window when needed. If not, build the waypoint.
* Show the rail waypoint selection window when needed. If not, build the waypoint.
* @param ta Area to build the waypoint in
* @param proc Function called to execute the build command.
*/
void ShowSelectWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc)
void ShowSelectRailWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc)
{
ShowSelectBaseStationIfNeeded<WaypointTypeFilter>(ta, std::move(proc));
ShowSelectBaseStationIfNeeded<RailWaypointTypeFilter>(ta, std::move(proc));
}
/**
* Show the road waypoint selection window when needed. If not, build the waypoint.
* @param ta Area to build the waypoint in
* @param proc Function called to execute the build command.
*/
void ShowSelectRoadWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc)
{
ShowSelectBaseStationIfNeeded<RoadWaypointTypeFilter>(ta, std::move(proc));
}

View File

@ -25,11 +25,13 @@ enum StationCoverageType {
int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies);
void CheckRedrawStationCoverage(const Window *w);
void CheckRedrawWaypointCoverage(const Window *w);
void CheckRedrawRailWaypointCoverage(const Window *w);
void CheckRedrawRoadWaypointCoverage(const Window *w);
using StationPickerCmdProc = std::function<bool(bool test, StationID to_join)>;
void ShowSelectStationIfNeeded(TileArea ta, StationPickerCmdProc proc);
void ShowSelectWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc);
void ShowSelectRailWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc);
void ShowSelectRoadWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc);
#endif /* STATION_GUI_H */

View File

@ -193,13 +193,34 @@ inline bool IsBusStop(Tile t)
return GetStationType(t) == STATION_BUS;
}
/**
* Is the station at \a t a road waypoint?
* @param t Tile to check
* @pre IsTileType(t, MP_STATION)
* @return \c true if station is a road waypoint, \c false otherwise
*/
inline bool IsRoadWaypoint(Tile t)
{
return GetStationType(t) == STATION_ROADWAYPOINT;
}
/**
* Is this tile a station tile and a road waypoint?
* @param t the tile to get the information from
* @return true if and only if the tile is a road waypoint
*/
inline bool IsRoadWaypointTile(Tile t)
{
return IsTileType(t, MP_STATION) && IsRoadWaypoint(t);
}
/**
* Is the station at \a t a road station?
* @param t Tile to check
* @pre IsTileType(t, MP_STATION)
* @return \c true if station at the tile is a bus top or a truck stop, \c false otherwise
* @return \c true if station at the tile is a bus stop or a truck stop, \c false otherwise
*/
inline bool IsRoadStop(Tile t)
inline bool IsStationRoadStop(Tile t)
{
assert(IsTileType(t, MP_STATION));
return IsTruckStop(t) || IsBusStop(t);
@ -208,11 +229,33 @@ inline bool IsRoadStop(Tile t)
/**
* Is tile \a t a road stop station?
* @param t Tile to check
* @return \c true if the tile is a station tile and a road stop
* @return \c true if the tile is a station tile and a station road stop
*/
inline bool IsRoadStopTile(Tile t)
inline bool IsStationRoadStopTile(Tile t)
{
return IsTileType(t, MP_STATION) && IsRoadStop(t);
return IsTileType(t, MP_STATION) && IsStationRoadStop(t);
}
/**
* Is the station at \a t a road station?
* @param t Tile to check
* @pre IsTileType(t, MP_STATION)
* @return \c true if station at the tile is a bus stop, truck stop or road waypoint, \c false otherwise
*/
inline bool IsAnyRoadStop(Tile t)
{
assert(IsTileType(t, MP_STATION));
return IsTruckStop(t) || IsBusStop(t) || IsRoadWaypoint(t);
}
/**
* Is tile \a t a road stop station?
* @param t Tile to check
* @return \c true if the tile is a station tile and any road stop type
*/
inline bool IsAnyRoadStopTile(Tile t)
{
return IsTileType(t, MP_STATION) && IsAnyRoadStop(t);
}
/**
@ -222,17 +265,17 @@ inline bool IsRoadStopTile(Tile t)
*/
inline bool IsBayRoadStopTile(Tile t)
{
return IsRoadStopTile(t) && GetStationGfx(t) < GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET;
return IsStationRoadStopTile(t) && GetStationGfx(t) < GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET;
}
/**
* Is tile \a t a drive through road stop station?
* Is tile \a t a drive through road stop station or waypoint?
* @param t Tile to check
* @return \c true if the tile is a station tile and a drive through road stop
*/
inline bool IsDriveThroughStopTile(Tile t)
{
return IsRoadStopTile(t) && GetStationGfx(t) >= GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET;
return IsAnyRoadStopTile(t) && GetStationGfx(t) >= GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET;
}
StationGfx GetTranslatedAirportTileID(StationGfx gfx);
@ -252,13 +295,13 @@ inline StationGfx GetAirportGfx(Tile t)
/**
* Gets the direction the road stop entrance points towards.
* @param t the tile of the road stop
* @pre IsRoadStopTile(t)
* @pre IsAnyRoadStopTile(t)
* @return the direction of the entrance
*/
inline DiagDirection GetRoadStopDir(Tile t)
{
StationGfx gfx = GetStationGfx(t);
assert(IsRoadStopTile(t));
assert(IsAnyRoadStopTile(t));
if (gfx < GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET) {
return (DiagDirection)(gfx);
} else {
@ -555,12 +598,12 @@ inline uint GetCustomStationSpecIndex(Tile t)
/**
* Is there a custom road stop spec on this tile?
* @param t Tile to query
* @pre IsRoadStopTile(t)
* @pre IsAnyRoadStopTile(t)
* @return True if this station is part of a newgrf station.
*/
inline bool IsCustomRoadStopSpecIndex(Tile t)
{
assert(IsRoadStopTile(t));
assert(IsAnyRoadStopTile(t));
return GB(t.m8(), 0, 6) != 0;
}
@ -568,23 +611,23 @@ inline bool IsCustomRoadStopSpecIndex(Tile t)
* Set the custom road stop spec for this tile.
* @param t Tile to set the stationspec of.
* @param specindex The new spec.
* @pre IsRoadStopTile(t)
* @pre IsAnyRoadStopTile(t)
*/
inline void SetCustomRoadStopSpecIndex(Tile t, uint8_t specindex)
{
assert(IsRoadStopTile(t));
assert(IsAnyRoadStopTile(t));
SB(t.m8(), 0, 6, specindex);
}
/**
* Get the custom road stop spec for this tile.
* @param t Tile to query
* @pre IsRoadStopTile(t)
* @pre IsAnyRoadStopTile(t)
* @return The custom station spec of this tile.
*/
inline uint GetCustomRoadStopSpecIndex(Tile t)
{
assert(IsRoadStopTile(t));
assert(IsAnyRoadStopTile(t));
return GB(t.m8(), 0, 6);
}
@ -699,9 +742,9 @@ inline void MakeRoadStop(Tile t, Owner o, StationID sid, RoadStopType rst, RoadT
* @param tram_rt the tram roadtype on this tile
* @param a the direction of the roadstop
*/
inline void MakeDriveThroughRoadStop(Tile t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, Axis a)
inline void MakeDriveThroughRoadStop(Tile t, Owner station, Owner road, Owner tram, StationID sid, StationType rst, RoadType road_rt, RoadType tram_rt, Axis a)
{
MakeStation(t, station, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + a);
MakeStation(t, station, sid, rst, GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + a);
SetRoadTypes(t, road_rt, tram_rt);
SetRoadOwner(t, RTT_ROAD, road);
SetRoadOwner(t, RTT_TRAM, tram);

View File

@ -37,6 +37,7 @@ enum StationType {
STATION_DOCK,
STATION_BUOY,
STATION_WAYPOINT,
STATION_ROADWAYPOINT,
};
/** Types of RoadStops */

View File

@ -955,6 +955,15 @@ static const DrawTileSprites _station_display_datas_bus[] = {
TILE_SPRITE_LINE(SPR_ROAD_PAVED_STRAIGHT_Y, _station_display_datas_0171)
};
static const DrawTileSprites _station_display_datas_road_waypoint[] = {
TILE_SPRITE_LINE(SPR_BUS_STOP_NE_GROUND | (1U << PALETTE_MODIFIER_COLOUR), _station_display_datas_71)
TILE_SPRITE_LINE(SPR_BUS_STOP_SE_GROUND | (1U << PALETTE_MODIFIER_COLOUR), _station_display_datas_72)
TILE_SPRITE_LINE(SPR_BUS_STOP_SW_GROUND | (1U << PALETTE_MODIFIER_COLOUR), _station_display_datas_73)
TILE_SPRITE_LINE(SPR_BUS_STOP_NW_GROUND | (1U << PALETTE_MODIFIER_COLOUR), _station_display_datas_74)
TILE_SPRITE_LINE(SPR_ROAD_PAVED_STRAIGHT_X, _station_display_datas_0170)
TILE_SPRITE_LINE(SPR_ROAD_PAVED_STRAIGHT_Y, _station_display_datas_0171)
};
static const DrawTileSprites _station_display_datas_oilrig[] = {
TILE_SPRITE_LINE(SPR_FLAT_WATER_TILE, _station_display_nothing)
};
@ -999,4 +1008,5 @@ static const DrawTileSprites * const _station_display_datas[] = {
_station_display_datas_dock,
_station_display_datas_buoy,
_station_display_datas_waypoint,
_station_display_datas_road_waypoint,
};

View File

@ -1238,7 +1238,7 @@ static bool CanRoadContinueIntoNextTile(const Town *t, const TileIndex tile, con
/* If the next tile is a station, allow if it's a road station facing the proper direction. Otherwise return false. */
if (IsTileType(next_tile, MP_STATION)) {
/* If the next tile is a road station, allow if it can be entered by the new tunnel/bridge, otherwise disallow. */
return IsRoadStop(next_tile) && (GetRoadStopDir(next_tile) == ReverseDiagDir(road_dir) || (IsDriveThroughStopTile(next_tile) && GetRoadStopDir(next_tile) == road_dir));
return IsAnyRoadStop(next_tile) && (GetRoadStopDir(next_tile) == ReverseDiagDir(road_dir) || (IsDriveThroughStopTile(next_tile) && GetRoadStopDir(next_tile) == road_dir));
}
/* If the next tile is a road depot, allow if it's facing the right way. */
@ -1441,7 +1441,7 @@ static inline bool RoadTypesAllowHouseHere(TileIndex t)
TileIndex cur_tile = t + ToTileIndexDiff(ptr);
if (!IsValidTile(cur_tile)) continue;
if (!(IsTileType(cur_tile, MP_ROAD) || IsRoadStopTile(cur_tile))) continue;
if (!(IsTileType(cur_tile, MP_ROAD) || IsAnyRoadStopTile(cur_tile))) continue;
allow = true;
RoadType road_rt = GetRoadTypeRoad(cur_tile);

View File

@ -2390,7 +2390,7 @@ void Vehicle::LeaveStation()
}
if (this->type == VEH_ROAD && !(this->vehstatus & VS_CRASHED)) {
/* Trigger road stop animation */
if (IsRoadStopTile(this->tile)) {
if (IsStationRoadStopTile(this->tile)) {
TriggerRoadStopRandomisation(st, this->tile, RSRT_VEH_DEPARTS);
TriggerRoadStopAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
}

View File

@ -3178,7 +3178,7 @@ public:
break;
case OT_GOTO_WAYPOINT: {
assert(v->type == VEH_TRAIN || v->type == VEH_SHIP);
assert(v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP);
SetDParam(0, v->current_order.GetDestination());
str = HasBit(v->vehicle_flags, VF_PATHFINDER_LOST) ? STR_VEHICLE_STATUS_CANNOT_REACH_WAYPOINT_VEL : STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL;
SetDParam(1, PackVelocity(v->GetDisplaySpeed(), v->type));

View File

@ -132,8 +132,10 @@ enum ViewportDragDropSelectionProcess {
DDSP_PLACE_ROAD_X_DIR, ///< Road placement (X axis)
DDSP_PLACE_ROAD_Y_DIR, ///< Road placement (Y axis)
DDSP_PLACE_AUTOROAD, ///< Road placement (auto)
DDSP_BUILD_ROAD_WAYPOINT, ///< Road stop placement (waypoint)
DDSP_BUILD_BUSSTOP, ///< Road stop placement (buses)
DDSP_BUILD_TRUCKSTOP, ///< Road stop placement (trucks)
DDSP_REMOVE_ROAD_WAYPOINT, ///< Road stop removal (waypoint)
DDSP_REMOVE_BUSSTOP, ///< Road stop removal (buses)
DDSP_REMOVE_TRUCKSTOP, ///< Road stop removal (trucks)
DDSP_CONVERT_ROAD, ///< Road conversion

View File

@ -38,6 +38,10 @@ void Waypoint::GetTileArea(TileArea *ta, StationType type) const
*ta = this->train_station;
return;
case STATION_ROADWAYPOINT:
*ta = this->road_waypoint_area;
return;
case STATION_BUOY:
ta->tile = this->xy;
ta->w = 1;

View File

@ -12,9 +12,18 @@
#include "base_station_base.h"
/**
* Flags for Waypoint::waypoint_flags.
*/
enum WaypointFlags {
WPF_ROAD = 0, ///< This is a road waypoint
};
/** Representation of a waypoint. */
struct Waypoint final : SpecializedStation<Waypoint, true> {
uint16_t town_cn; ///< The N-1th waypoint for this town (consecutive number)
uint16_t waypoint_flags{}; ///< Waypoint flags, see WaypointFlags
TileArea road_waypoint_area; ///< Tile area the road waypoint part covers
/**
* Create a waypoint at the given tile.

View File

@ -25,6 +25,7 @@
#include "string_func.h"
#include "company_func.h"
#include "newgrf_station.h"
#include "newgrf_roadstop.h"
#include "company_base.h"
#include "water.h"
#include "company_gui.h"
@ -68,15 +69,16 @@ void Waypoint::MoveSign(TileIndex new_xy)
* @param tile to search from
* @param str the string to get the 'type' of
* @param cid previous owner of the waypoint
* @param is_road whether to find a road waypoint
* @return the deleted nearby waypoint
*/
static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile, StringID str, CompanyID cid)
static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile, StringID str, CompanyID cid, bool is_road)
{
Waypoint *best = nullptr;
uint thres = 8;
for (Waypoint *wp : Waypoint::Iterate()) {
if (!wp->IsInUse() && wp->string_id == str && wp->owner == cid) {
if (!wp->IsInUse() && wp->string_id == str && wp->owner == cid && HasBit(wp->waypoint_flags, WPF_ROAD) == is_road) {
uint cur_dist = DistanceManhattan(tile, wp->xy);
if (cur_dist < thres) {
@ -90,13 +92,13 @@ static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile, StringID str, Compan
}
/**
* Get the axis for a new waypoint. This means that if it is a valid
* Get the axis for a new rail waypoint. This means that if it is a valid
* tile to build a waypoint on it returns a valid Axis, otherwise an
* invalid one.
* @param tile the tile to look at.
* @return the axis for the to-be-build waypoint.
*/
Axis GetAxisForNewWaypoint(TileIndex tile)
Axis GetAxisForNewRailWaypoint(TileIndex tile)
{
/* The axis for rail waypoints is easy. */
if (IsRailWaypointTile(tile)) return GetRailStationAxis(tile);
@ -111,6 +113,29 @@ Axis GetAxisForNewWaypoint(TileIndex tile)
}
}
/**
* Get the axis for a new road waypoint. This means that if it is a valid
* tile to build a waypoint on it returns a valid Axis, otherwise an
* invalid one.
* @param tile the tile to look at.
* @return the axis for the to-be-build waypoint.
*/
Axis GetAxisForNewRoadWaypoint(TileIndex tile)
{
/* The axis for rail waypoints is easy. */
if (IsRoadWaypointTile(tile)) return DiagDirToAxis(GetRoadStopDir(tile));
/* Non-plain road type, no valid axis for waypoints. */
if (!IsNormalRoadTile(tile)) return INVALID_AXIS;
RoadBits bits = GetAllRoadBits(tile);
if ((bits & ROAD_Y) == 0) return AXIS_X;
if ((bits & ROAD_X) == 0) return AXIS_Y;
return INVALID_AXIS;
}
extern CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags);
/**
@ -137,7 +162,7 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *
}
}
if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
if (GetAxisForNewRailWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
Owner owner = GetTileOwner(tile);
CommandCost ret = CheckOwnership(owner);
@ -156,8 +181,10 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *
}
extern void GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const StationSpec *statspec);
extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp);
extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road);
extern CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta);
extern CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlag flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost);
extern CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index);
/**
* Convert existing rail to waypoint. Eg build a waypoint station over
@ -212,12 +239,12 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlag flags, TileIndex start_tile, Axis
}
Waypoint *wp = nullptr;
CommandCost ret = FindJoiningWaypoint(est, station_to_join, adjacent, new_location, &wp);
CommandCost ret = FindJoiningWaypoint(est, station_to_join, adjacent, new_location, &wp, false);
if (ret.Failed()) return ret;
/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
TileIndex center_tile = start_tile + (count / 2) * offset;
if (wp == nullptr && reuse) wp = FindDeletedWaypointCloseTo(center_tile, STR_SV_STNAME_WAYPOINT, _current_company);
if (wp == nullptr && reuse) wp = FindDeletedWaypointCloseTo(center_tile, STR_SV_STNAME_WAYPOINT, _current_company, false);
if (wp != nullptr) {
/* Reuse an existing waypoint. */
@ -290,6 +317,145 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlag flags, TileIndex start_tile, Axis
return cost;
}
/**
* Convert existing road to waypoint. Eg build a waypoint station over
* piece of road
* @param flags type of operation
* @param start_tile northern most tile where waypoint will be built
* @param axis orientation (Axis)
* @param width width of waypoint
* @param height height of waypoint
* @param spec_class custom road stop class
* @param spec_index custom road stop id
* @param station_to_join station ID to join (NEW_STATION if build new one)
* @param adjacent allow waypoints directly adjacent to other waypoints.
* @return the cost of this operation or an error
*/
CommandCost CmdBuildRoadWaypoint(DoCommandFlag flags, TileIndex start_tile, Axis axis, uint8_t width, uint8_t height, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
{
if (!IsValidAxis(axis)) return CMD_ERROR;
/* Check if the given station class is valid */
if (spec_class != ROADSTOP_CLASS_WAYP) return CMD_ERROR;
if (spec_index >= RoadStopClass::Get(spec_class)->GetSpecCount()) return CMD_ERROR;
const RoadStopSpec *roadstopspec = RoadStopClass::Get(spec_class)->GetSpec(spec_index);
/* The number of parts to build */
uint8_t count = axis == AXIS_X ? height : width;
if ((axis == AXIS_X ? width : height) != 1) return CMD_ERROR;
if (count == 0 || count > _settings_game.station.station_spread) return CMD_ERROR;
bool reuse = (station_to_join != NEW_STATION);
if (!reuse) station_to_join = INVALID_STATION;
bool distant_join = (station_to_join != INVALID_STATION);
if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR;
TileArea roadstop_area(start_tile, width, height);
/* Total road stop cost. */
Money unit_cost;
if (roadstopspec != nullptr) {
unit_cost = roadstopspec->GetBuildCost(PR_BUILD_STATION_TRUCK);
} else {
unit_cost = _price[PR_BUILD_STATION_TRUCK];
}
StationID est = INVALID_STATION;
CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, true, STATION_ROADWAYPOINT, axis, AxisToDiagDir(axis), &est, INVALID_ROADTYPE, unit_cost);
if (cost.Failed()) return cost;
Waypoint *wp = nullptr;
CommandCost ret = FindJoiningWaypoint(est, station_to_join, adjacent, roadstop_area, &wp, true);
if (ret.Failed()) return ret;
/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
TileIndex center_tile = start_tile + (count / 2) * TileOffsByDiagDir(AxisToDiagDir(OtherAxis(axis)));;
if (wp == nullptr && reuse) wp = FindDeletedWaypointCloseTo(center_tile, STR_SV_STNAME_WAYPOINT, _current_company, true);
if (wp != nullptr) {
/* Reuse an existing waypoint. */
if (!HasBit(wp->waypoint_flags, WPF_ROAD)) return CMD_ERROR;
if (wp->owner != _current_company) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT);
ret = wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TEST);
if (ret.Failed()) return ret;
} else {
/* allocate and initialize new waypoint */
if (!Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
}
/* Check if we can allocate a custom stationspec to this station */
if (AllocateSpecToRoadStop(roadstopspec, wp, false) == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS);
if (flags & DC_EXEC) {
if (wp == nullptr) {
wp = new Waypoint(start_tile);
SetBit(wp->waypoint_flags, WPF_ROAD);
} else if (!wp->IsInUse()) {
/* Move existing (recently deleted) waypoint to the new location */
wp->xy = start_tile;
}
wp->owner = _current_company;
wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TRY);
if (roadstopspec != nullptr) {
/* Include this road stop spec's animation trigger bitmask
* in the station's cached copy. */
wp->cached_roadstop_anim_triggers |= roadstopspec->animation.triggers;
}
wp->delete_ctr = 0;
wp->facilities |= FACIL_BUS_STOP | FACIL_TRUCK_STOP;
wp->build_date = TimerGameCalendar::date;
wp->string_id = STR_SV_STNAME_WAYPOINT;
if (wp->town == nullptr) MakeDefaultName(wp);
wp->UpdateVirtCoord();
uint8_t map_spec_index = AllocateSpecToRoadStop(roadstopspec, wp, true);
/* Check every tile in the area. */
for (TileIndex cur_tile : roadstop_area) {
/* Get existing road types and owners before any tile clearing */
RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
if (IsRoadWaypointTile(cur_tile)) {
RemoveRoadWaypointStop(cur_tile, flags, map_spec_index);
}
wp->road_waypoint_area.Add(cur_tile);
wp->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY);
/* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
* bits first. */
if (IsNormalRoadTile(cur_tile)) {
UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
}
UpdateCompanyRoadInfrastructure(road_rt, road_owner, ROAD_STOP_TRACKBIT_FACTOR);
UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, ROAD_STOP_TRACKBIT_FACTOR);
MakeDriveThroughRoadStop(cur_tile, wp->owner, road_owner, tram_owner, wp->index, STATION_ROADWAYPOINT, road_rt, tram_rt, axis);
SetCustomRoadStopSpecIndex(cur_tile, map_spec_index);
if (roadstopspec != nullptr) wp->SetRoadStopRandomBits(cur_tile, 0);
Company::Get(wp->owner)->infrastructure.station++;
MarkTileDirtyByTile(cur_tile);
}
DirtyCompanyInfrastructureWindows(wp->owner);
}
return cost;
}
/**
* Build a buoy.
* @param flags operation to perform
@ -304,7 +470,7 @@ CommandCost CmdBuildBuoy(DoCommandFlag flags, TileIndex tile)
if (!IsTileFlat(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
Waypoint *wp = FindDeletedWaypointCloseTo(tile, STR_SV_STNAME_BUOY, OWNER_NONE);
Waypoint *wp = FindDeletedWaypointCloseTo(tile, STR_SV_STNAME_BUOY, OWNER_NONE, false);
if (wp == nullptr && !Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_WAYPOINT_BUOY]);

View File

@ -14,14 +14,19 @@
#include "station_type.h"
enum StationClassID : uint16_t;
enum RoadStopClassID : uint16_t;
CommandCost CmdBuildRailWaypoint(DoCommandFlag flags, TileIndex start_tile, Axis axis, uint8_t width, uint8_t height, StationClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent);
CommandCost CmdRemoveFromRailWaypoint(DoCommandFlag flags, TileIndex start, TileIndex end, bool keep_rail);
CommandCost CmdBuildRoadWaypoint(DoCommandFlag flags, TileIndex start_tile, Axis axis, uint8_t width, uint8_t height, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent);
CommandCost CmdRemoveFromRoadWaypoint(DoCommandFlag flags, TileIndex start, TileIndex end);
CommandCost CmdBuildBuoy(DoCommandFlag flags, TileIndex tile);
CommandCost CmdRenameWaypoint(DoCommandFlag flags, StationID waypoint_id, const std::string &text);
DEF_CMD_TRAIT(CMD_BUILD_RAIL_WAYPOINT, CmdBuildRailWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_REMOVE_FROM_RAIL_WAYPOINT, CmdRemoveFromRailWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_BUILD_ROAD_WAYPOINT, CmdBuildRoadWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_REMOVE_FROM_ROAD_WAYPOINT, CmdRemoveFromRoadWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_BUILD_BUOY, CmdBuildBuoy, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_RENAME_WAYPOINT, CmdRenameWaypoint, 0, CMDT_OTHER_MANAGEMENT)

View File

@ -16,7 +16,8 @@
CommandCost RemoveBuoy(TileIndex tile, DoCommandFlag flags);
Axis GetAxisForNewWaypoint(TileIndex tile);
Axis GetAxisForNewRailWaypoint(TileIndex tile);
Axis GetAxisForNewRoadWaypoint(TileIndex tile);
void ShowWaypointWindow(const Waypoint *wp);
void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype);

View File

@ -43,8 +43,25 @@ private:
{
if (!this->wp->IsInUse()) return this->wp->xy;
StationType type;
switch (this->vt) {
case VEH_TRAIN:
type = STATION_WAYPOINT;
break;
case VEH_ROAD:
type = STATION_ROADWAYPOINT;
break;
case VEH_SHIP:
type = STATION_BUOY;
break;
default:
NOT_REACHED();
}
TileArea ta;
this->wp->GetTileArea(&ta, this->vt == VEH_TRAIN ? STATION_WAYPOINT : STATION_BUOY);
this->wp->GetTileArea(&ta, type);
return ta.GetCenterTile();
}
@ -57,11 +74,20 @@ public:
WaypointWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
{
this->wp = Waypoint::Get(window_number);
this->vt = (wp->string_id == STR_SV_STNAME_WAYPOINT) ? VEH_TRAIN : VEH_SHIP;
if (wp->string_id == STR_SV_STNAME_WAYPOINT) {
this->vt = HasBit(this->wp->waypoint_flags, WPF_ROAD) ? VEH_ROAD : VEH_TRAIN;
} else {
this->vt = VEH_SHIP;
}
this->CreateNestedTree();
if (this->vt == VEH_TRAIN) {
this->GetWidget<NWidgetCore>(WID_W_SHOW_VEHICLES)->SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP);
}
if (this->vt == VEH_ROAD) {
this->GetWidget<NWidgetCore>(WID_W_SHOW_VEHICLES)->SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP);
}
if (this->vt != VEH_SHIP) {
this->GetWidget<NWidgetCore>(WID_W_CENTER_VIEW)->tool_tip = STR_WAYPOINT_VIEW_CENTER_TOOLTIP;
this->GetWidget<NWidgetCore>(WID_W_RENAME)->tool_tip = STR_WAYPOINT_VIEW_CHANGE_WAYPOINT_NAME;
}

View File

@ -19,6 +19,7 @@ enum RoadToolbarWidgets : WidgetID {
WID_ROT_AUTOROAD, ///< Autorail.
WID_ROT_DEMOLISH, ///< Demolish.
WID_ROT_DEPOT, ///< Build depot.
WID_ROT_BUILD_WAYPOINT, ///< Build waypoint.
WID_ROT_BUS_STATION, ///< Build bus station.
WID_ROT_TRUCK_STATION, ///< Build truck station.
WID_ROT_ONE_WAY, ///< Build one-way road.