diff --git a/docs/landscape.html b/docs/landscape.html
index f592cd0f6a..c48aedcebc 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -876,6 +876,22 @@
m2: index into the array of stations
m3 bits 7..4: persistent random data for railway stations/waypoints and airports)
m3 bits 7..4: owner of tram tracks (road stop)
+ m3 bits 3..2: ground type (road waypoints)
+
+
+ 0 |
+ on bare land |
+
+
+ 1 |
+ on grass |
+
+
+ 2 |
+ paved |
+
+
+
m4: custom station id; 0 means standard graphics
m4: Roadtype for road stops
m5: graphics index (range from 0..255 for each station type):
@@ -990,13 +1006,14 @@
m6 bit 7: rail station / waypoint may have catenary pylons
- m6 bit 6: rail station / waypoint may have catenary wires
- m6 bits 5..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint)
+ m6 bits 6..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint, road waypoint)
m6 bit 2: pbs reservation state for railway stations/waypoints
+ m6 bit 1: rail station / waypoint may have catenary wires
m6 bit 0: rail station / waypoint is blocked
m7 bits 4..0: owner of road (road stops)
m7: animation frame (railway stations/waypoints, airports)
+ m8 bit 15: Snow or desert present (road waypoints)
m8 bits 11..6: Tramtype
m8 bits 5..0: track type for railway stations/waypoints
m8 bits 5..0: custom road stop id; 0 means standard graphics
diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html
index a0fa527a6b..11e5952777 100644
--- a/docs/landscape_grid.html
+++ b/docs/landscape_grid.html
@@ -181,14 +181,14 @@ the array so you can quickly see what is used and what is not.
OOOO OOOO OOOO OOOO |
- 5 |
+ 5 |
rail station |
- OXX XXXXX |
- XXXX XXXX XXXX XXXX |
+ OXX XXXXX |
+ XXXX XXXX XXXX XXXX |
XXXX OOOO |
XXXX XXXX |
XXXX XXXX |
- XXXXX XOX |
+ XXXXX XXX |
XXXX XXXX |
OOOO OOOO OOXX XXXX |
@@ -199,12 +199,17 @@ the array so you can quickly see what is used and what is not.
road stop |
XXXX OOOO |
- OOXX XXXX |
- OOOO OXXX |
- OOXX XOOO |
- OOOX XXXX |
+ OOXX XXXX |
+ OOOO OXXX |
+ OXXX XOOO |
+ OOOX XXXX |
OOOO XXXX XX XXXXXX |
+
+ road waypoint |
+ XXXX XXOO |
+ XOOO XXXX XXOO OOOO |
+
airport |
XXXX OOOO |
diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf
index 7f4f6cbc49..6cee79959e 100644
Binary files a/media/baseset/openttd.grf and b/media/baseset/openttd.grf differ
diff --git a/media/baseset/openttd.grf.hash b/media/baseset/openttd.grf.hash
index 25a50247f5..f3b216de53 100644
--- a/media/baseset/openttd.grf.hash
+++ b/media/baseset/openttd.grf.hash
@@ -1 +1 @@
-4f03553f614a06d86dc06376db3353c7
+8bc3926cb50e19747de498357417d973
diff --git a/media/baseset/openttd/CMakeLists.txt b/media/baseset/openttd/CMakeLists.txt
index 30844b8804..ee93ba80b1 100644
--- a/media/baseset/openttd/CMakeLists.txt
+++ b/media/baseset/openttd/CMakeLists.txt
@@ -20,6 +20,7 @@ if(GRFCODEC_FOUND)
${CMAKE_CURRENT_SOURCE_DIR}/openttdgui.nfo
${CMAKE_CURRENT_SOURCE_DIR}/palette.nfo
${CMAKE_CURRENT_SOURCE_DIR}/roadstops.nfo
+ ${CMAKE_CURRENT_SOURCE_DIR}/road_waypoints.nfo
${CMAKE_CURRENT_SOURCE_DIR}/signals.nfo
${CMAKE_CURRENT_SOURCE_DIR}/sloped_tracks.nfo
${CMAKE_CURRENT_SOURCE_DIR}/tramtracks.nfo
@@ -42,6 +43,7 @@ if(GRFCODEC_FOUND)
${CMAKE_CURRENT_SOURCE_DIR}/openttdgui_convert_tram.png
${CMAKE_CURRENT_SOURCE_DIR}/openttdgui_group_livery.png
${CMAKE_CURRENT_SOURCE_DIR}/roadstops.png
+ ${CMAKE_CURRENT_SOURCE_DIR}/road_waypoints.png
${CMAKE_CURRENT_SOURCE_DIR}/signals.png
${CMAKE_CURRENT_SOURCE_DIR}/sloped_tracks.png
${CMAKE_CURRENT_SOURCE_DIR}/tramtracks.png
diff --git a/media/baseset/openttd/openttd.nfo b/media/baseset/openttd/openttd.nfo
index c1ed751c55..3d59f08543 100644
--- a/media/baseset/openttd/openttd.nfo
+++ b/media/baseset/openttd/openttd.nfo
@@ -98,3 +98,4 @@
#include "mono.nfo"
#include "tunnel_portals.nfo"
#include "palette.nfo"
+#include "road_waypoints.nfo"
diff --git a/media/baseset/openttd/road_waypoints.nfo b/media/baseset/openttd/road_waypoints.nfo
new file mode 100644
index 0000000000..4e7a27019b
--- /dev/null
+++ b/media/baseset/openttd/road_waypoints.nfo
@@ -0,0 +1,14 @@
+// This file is part of OpenTTD.
+// OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+// OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+//
+
+ -1 * 0 0C "Road waypoints"
+//@@LINT OFF
+ -1 * 3 05 19 04
+//@@LINT ON
+ -1 sprites/road_waypoints.png 8bpp 10 10 64 40 -5 -22 normal
+ -1 sprites/road_waypoints.png 8bpp 90 10 64 40 -31 -9 normal
+ -1 sprites/road_waypoints.png 8bpp 170 10 64 35 -31 -4 normal
+ -1 sprites/road_waypoints.png 8bpp 240 10 64 35 -57 -17 normal
diff --git a/media/baseset/openttd/road_waypoints.png b/media/baseset/openttd/road_waypoints.png
new file mode 100644
index 0000000000..2934cb5b22
Binary files /dev/null and b/media/baseset/openttd/road_waypoints.png differ
diff --git a/src/command_type.h b/src/command_type.h
index dae768104b..3913131350 100644
--- a/src/command_type.h
+++ b/src/command_type.h
@@ -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)
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 36fea5c491..816757e324 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -2881,6 +2881,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
@@ -5088,11 +5090,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!
@@ -5340,6 +5345,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
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index 0b8628f370..f6ebcff603 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -4887,7 +4887,7 @@ static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, ByteR
break;
case 0x12: // General flags
- rs->flags = (uint8_t)buf->ReadDWord(); // Future-proofing, size this as 4 bytes, but we only need one byte's worth of flags at present
+ rs->flags = (uint16_t)buf->ReadDWord(); // Future-proofing, size this as 4 bytes, but we only need two byte's worth of flags at present
break;
case 0x15: // Cost multipliers
@@ -6425,6 +6425,7 @@ static constexpr auto _action5_types = std::to_array({
/* 0x16 */ { A5BLOCK_ALLOW_OFFSET, SPR_AIRPORT_PREVIEW_BASE, 1, SPR_AIRPORT_PREVIEW_COUNT, "Airport preview graphics" },
/* 0x17 */ { A5BLOCK_ALLOW_OFFSET, SPR_RAILTYPE_TUNNEL_BASE, 1, RAILTYPE_TUNNEL_BASE_COUNT, "Railtype tunnel base" },
/* 0x18 */ { A5BLOCK_ALLOW_OFFSET, SPR_PALETTE_BASE, 1, PALETTE_SPRITE_COUNT, "Palette" },
+ /* 0x19 */ { A5BLOCK_ALLOW_OFFSET, SPR_ROAD_WAYPOINTS_BASE, 1, ROAD_WAYPOINTS_SPRITE_COUNT, "Road waypoints" },
});
/**
diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp
index 9589e31ef4..822b641cbe 100644
--- a/src/newgrf_commons.cpp
+++ b/src/newgrf_commons.cpp
@@ -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;
diff --git a/src/newgrf_roadstop.cpp b/src/newgrf_roadstop.cpp
index eaa76fe5f9..aaafd88738 100644
--- a/src/newgrf_roadstop.cpp
+++ b/src/newgrf_roadstop.cpp
@@ -119,6 +119,15 @@ uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] u
/* Animation frame */
case 0x49: return this->tile == INVALID_TILE ? 0 : this->st->GetRoadStopAnimationFrame(this->tile);
+ /* Misc info */
+ case 0x50: {
+ uint32_t result = 0;
+ if (this->tile == INVALID_TILE) {
+ SetBit(result, 4);
+ }
+ return result;
+ }
+
/* Variables which use the parameter */
/* Variables 0x60 to 0x65 and 0x69 are handled separately below */
@@ -127,7 +136,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 +152,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 +160,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 +175,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 +186,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;
@@ -283,7 +293,19 @@ void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec,
SpriteID image = dts->ground.sprite;
PaletteID pal = dts->ground.pal;
- if (GB(image, 0, SPRITE_WIDTH) != 0) {
+ RoadStopDrawMode draw_mode;
+ if (HasBit(spec->flags, RSF_DRAW_MODE_REGISTER)) {
+ draw_mode = (RoadStopDrawMode)GetRegister(0x100);
+ } else {
+ draw_mode = spec->draw_mode;
+ }
+
+ if (type == STATION_ROADWAYPOINT) {
+ DrawSprite(SPR_ROAD_PAVED_STRAIGHT_X, PAL_NONE, x, y);
+ if ((draw_mode & ROADSTOP_DRAW_MODE_WAYP_GROUND) && GB(image, 0, SPRITE_WIDTH) != 0) {
+ DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y);
+ }
+ } else if (GB(image, 0, SPRITE_WIDTH) != 0) {
DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y);
}
@@ -292,7 +314,7 @@ void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec,
uint sprite_offset = 5 - view;
/* Road underlay takes precedence over tram */
- if (spec->draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) {
+ if (type == STATION_ROADWAYPOINT || draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) {
if (rti->UsesOverlay()) {
SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND);
DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
@@ -305,7 +327,7 @@ void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec,
}
} else {
/* Bay stop */
- if ((spec->draw_mode & ROADSTOP_DRAW_MODE_ROAD) && rti->UsesOverlay()) {
+ if ((draw_mode & ROADSTOP_DRAW_MODE_ROAD) && rti->UsesOverlay()) {
SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_ROADSTOP);
DrawSprite(ground + view, PAL_NONE, x, y);
}
diff --git a/src/newgrf_roadstop.h b/src/newgrf_roadstop.h
index 4a6980883d..a2db330cef 100644
--- a/src/newgrf_roadstop.h
+++ b/src/newgrf_roadstop.h
@@ -59,6 +59,7 @@ enum RoadStopDrawMode : uint8_t {
ROADSTOP_DRAW_MODE_NONE = 0,
ROADSTOP_DRAW_MODE_ROAD = 1 << 0, ///< Bay stops: Draw the road itself
ROADSTOP_DRAW_MODE_OVERLAY = 1 << 1, ///< Drive-through stops: Draw the road overlay, e.g. pavement
+ ROADSTOP_DRAW_MODE_WAYP_GROUND = 1 << 2, ///< Waypoints: Draw the sprite layout ground tile (on top of the road)
};
DECLARE_ENUM_AS_BIT_SET(RoadStopDrawMode)
@@ -69,6 +70,7 @@ enum RoadStopSpecFlags {
RSF_NO_AUTO_ROAD_CONNECTION = 4, ///< No auto road connection.
RSF_BUILD_MENU_ROAD_ONLY = 5, ///< Only show in the road build menu (not tram).
RSF_BUILD_MENU_TRAM_ONLY = 6, ///< Only show in the tram build menu (not road).
+ RSF_DRAW_MODE_REGISTER = 8, ///< Read draw mode from register 0x100.
};
/** Scope resolver for road stops. */
@@ -132,7 +134,7 @@ struct RoadStopSpec {
RoadStopAvailabilityType stop_type = ROADSTOPTYPE_ALL;
RoadStopDrawMode draw_mode = ROADSTOP_DRAW_MODE_ROAD | ROADSTOP_DRAW_MODE_OVERLAY;
uint8_t callback_mask = 0;
- uint8_t flags = 0;
+ uint16_t flags = 0;
CargoTypes cargo_triggers = 0; ///< Bitmask of cargo types which cause trigger re-randomizing
diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp
index 003d879303..754e6767ff 100644
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -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;
}
diff --git a/src/order_gui.cpp b/src/order_gui.cpp
index 397502cd32..fe063dd66e 100644
--- a/src/order_gui.cpp
+++ b/src/order_gui.cpp
@@ -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));
diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp
index 513d3ab0c7..28b4d92375 100644
--- a/src/pathfinder/follow_track.hpp
+++ b/src/pathfinder/follow_track.hpp
@@ -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;
}
}
diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp
index 5ac43cd8c9..060d36ff7e 100644
--- a/src/pathfinder/npf/npf.cpp
+++ b/src/pathfinder/npf/npf.cpp
@@ -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;
}
diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp
index 41102c2e15..209b64b52a 100644
--- a/src/pathfinder/yapf/yapf_road.cpp
+++ b/src/pathfinder/yapf/yapf_road.cpp
@@ -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));
}
diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp
index 137e78d494..1a98a9b6db 100644
--- a/src/rail_gui.cpp
+++ b/src/rail_gui.cpp
@@ -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);
}
};
diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp
index eee9e24efb..db238b76ff 100644
--- a/src/road_cmd.cpp
+++ b/src/road_cmd.cpp
@@ -1359,13 +1359,13 @@ static uint GetRoadSpriteOffset(Slope slope, RoadBits bits)
* By default, roads are always drawn as unpaved if they are on desert or
* above the snow line, but NewGRFs can override this for desert.
*
- * @param tile The tile the road is on
+ * @param snow_or_desert Is snowy or desert tile
* @param roadside What sort of road this is
* @return True if snow/desert road sprites should be used.
*/
-static bool DrawRoadAsSnowDesert(TileIndex tile, Roadside roadside)
+static bool DrawRoadAsSnowDesert(bool snow_or_desert, Roadside roadside)
{
- return (IsOnSnow(tile) &&
+ return (snow_or_desert &&
!(_settings_game.game_creation.landscape == LT_TROPIC && HasGrfMiscBit(GMB_DESERT_PAVED_ROADS) &&
roadside != ROADSIDE_BARREN && roadside != ROADSIDE_GRASS && roadside != ROADSIDE_GRASS_ROAD_WORKS));
}
@@ -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);
@@ -1556,13 +1556,14 @@ void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *roa
* @param roadside Road side type
* @param rti Road type info
* @param offset Road sprite offset
+ * @param snow_or_desert Whether to get snow/desert ground sprite
* @param[out] pal Palette to draw.
*/
-static SpriteID GetRoadGroundSprite(const TileInfo *ti, Roadside roadside, const RoadTypeInfo *rti, uint offset, PaletteID *pal)
+static SpriteID GetRoadGroundSprite(const TileInfo *ti, Roadside roadside, const RoadTypeInfo *rti, uint offset, bool snow_or_desert, PaletteID *pal)
{
/* Draw bare ground sprite if no road or road uses overlay system. */
if (rti == nullptr || rti->UsesOverlay()) {
- if (DrawRoadAsSnowDesert(ti->tile, roadside)) {
+ if (DrawRoadAsSnowDesert(snow_or_desert, roadside)) {
return SPR_FLAT_SNOW_DESERT_TILE + SlopeToSpriteOffset(ti->tileh);
}
@@ -1577,7 +1578,7 @@ static SpriteID GetRoadGroundSprite(const TileInfo *ti, Roadside roadside, const
/* Draw original road base sprite */
SpriteID image = SPR_ROAD_Y + offset;
- if (DrawRoadAsSnowDesert(ti->tile, roadside)) {
+ if (DrawRoadAsSnowDesert(snow_or_desert, roadside)) {
image += 19;
} else {
switch (roadside) {
@@ -1591,6 +1592,30 @@ static SpriteID GetRoadGroundSprite(const TileInfo *ti, Roadside roadside, const
return image;
}
+/**
+ * Draw road ground sprites.
+ * @param ti TileInfo
+ * @param road Road bits
+ * @param tram Tram bits
+ * @param road_rti Road road type information
+ * @param tram_rti Tram road type information
+ * @param roadside Roadside type
+ * @param snow_or_desert Whether to draw snow/desert ground sprites
+ */
+void DrawRoadGroundSprites(const TileInfo *ti, RoadBits road, RoadBits tram, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, Roadside roadside, bool snow_or_desert)
+{
+ /* Determine sprite offsets */
+ uint road_offset = GetRoadSpriteOffset(ti->tileh, road);
+ uint tram_offset = GetRoadSpriteOffset(ti->tileh, tram);
+
+ /* Draw baseset underlay */
+ PaletteID pal = PAL_NONE;
+ SpriteID image = GetRoadGroundSprite(ti, roadside, road_rti, road == ROAD_NONE ? tram_offset : road_offset, snow_or_desert, &pal);
+ DrawGroundSprite(image, pal);
+
+ DrawRoadOverlays(ti, pal, road_rti, tram_rti, road_offset, tram_offset);
+}
+
/**
* Draw ground sprite and road pieces
* @param ti TileInfo
@@ -1610,18 +1635,7 @@ static void DrawRoadBits(TileInfo *ti)
/* DrawFoundation() modifies ti. */
}
- /* Determine sprite offsets */
- uint road_offset = GetRoadSpriteOffset(ti->tileh, road);
- uint tram_offset = GetRoadSpriteOffset(ti->tileh, tram);
-
- /* Draw baseset underlay */
- Roadside roadside = GetRoadside(ti->tile);
-
- PaletteID pal = PAL_NONE;
- SpriteID image = GetRoadGroundSprite(ti, roadside, road_rti, road == ROAD_NONE ? tram_offset : road_offset, &pal);
- DrawGroundSprite(image, pal);
-
- DrawRoadOverlays(ti, pal, road_rti, tram_rti, road_offset, tram_offset);
+ DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadside(ti->tile), IsOnSnow(ti->tile));
/* Draw one way */
if (road_rti != nullptr) {
@@ -1654,6 +1668,7 @@ static void DrawRoadBits(TileInfo *ti)
if (!HasBit(_display_opt, DO_FULL_DETAIL) || _cur_dpi->zoom > ZOOM_LVL_DETAIL) return;
/* Do not draw details (street lights, trees) under low bridge */
+ Roadside roadside = GetRoadside(ti->tile);
if (IsBridgeAbove(ti->tile) && (roadside == ROADSIDE_TREES || roadside == ROADSIDE_STREET_LIGHTS)) {
int height = GetBridgeHeight(GetNorthernBridgeEnd(ti->tile));
int minz = GetTileMaxZ(ti->tile) + 2;
@@ -1712,7 +1727,7 @@ static void DrawTile_Road(TileInfo *ti)
SpriteID image = SPR_ROAD_Y + axis;
Roadside roadside = GetRoadside(ti->tile);
- if (DrawRoadAsSnowDesert(ti->tile, roadside)) {
+ if (DrawRoadAsSnowDesert(IsOnSnow(ti->tile), roadside)) {
image += 19;
} else {
switch (roadside) {
@@ -1728,7 +1743,7 @@ static void DrawTile_Road(TileInfo *ti)
if (IsCrossingBarred(ti->tile)) image += 2;
Roadside roadside = GetRoadside(ti->tile);
- if (DrawRoadAsSnowDesert(ti->tile, roadside)) {
+ if (DrawRoadAsSnowDesert(IsOnSnow(ti->tile), roadside)) {
image += 8;
} else {
switch (roadside) {
@@ -2448,7 +2463,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)) {
diff --git a/src/road_func.h b/src/road_func.h
index 7c73597b9f..ed3e3db6d0 100644
--- a/src/road_func.h
+++ b/src/road_func.h
@@ -159,6 +159,8 @@ void UpdateAdjacentLevelCrossingTilesOnLevelCrossingRemoval(TileIndex tile, Axis
void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count);
struct TileInfo;
+enum Roadside : uint8_t;
void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rit, uint road_offset, uint tram_offset, bool draw_underlay = true);
+void DrawRoadGroundSprites(const TileInfo *ti, RoadBits road, RoadBits tram, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, Roadside roadside, bool snow_or_desert);
#endif /* ROAD_FUNC_H */
diff --git a/src/road_gui.cpp b/src/road_gui.cpp
index efb8cc2713..d2b3617b16 100644
--- a/src/road_gui.cpp
+++ b/src/road_gui.cpp
@@ -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"
@@ -51,6 +53,7 @@
static void ShowRVStationPicker(Window *parent, RoadStopType rs);
static void ShowRoadDepotPicker(Window *parent);
+static void ShowBuildRoadWaypointPicker(Window *parent);
static bool _remove_button_clicked;
static bool _one_way_button_clicked;
@@ -64,6 +67,8 @@ static RoadType _cur_roadtype;
static DiagDirection _road_depot_orientation;
+static uint16_t _cur_waypoint_type; ///< Currently selected waypoint type
+
struct RoadStopGUISettings {
DiagDirection orientation;
@@ -236,6 +241,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::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 +377,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(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
+ this->GetWidget(WID_ROT_BUILD_WAYPOINT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget(WID_ROT_BUS_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget(WID_ROT_TRUCK_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
} else {
this->GetWidget(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(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(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(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 +476,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 +537,13 @@ struct BuildRoadToolbarWindow : Window {
}
break;
+ case WID_ROT_BUILD_WAYPOINT:
+ this->last_started_action = widget;
+ if (HandlePlacePushButton(this, WID_ROT_BUILD_WAYPOINT, SPR_CURSOR_WAYPOINT, HT_RECT) && RoadStopClass::Get(ROADSTOP_CLASS_WAYP)->GetSpecCount() > 1) {
+ ShowBuildRoadWaypointPicker(this);
+ }
+ 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 +633,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 +678,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 +751,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::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 = _cur_waypoint_type;
+
+ auto proc = [=](bool test, StationID to_join) -> bool {
+ if (test) {
+ return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, axis, ta.w, ta.h, ROADSTOP_CLASS_WAYP, waypoint_type, INVALID_STATION, adjacent).Succeeded();
+ } else {
+ return Command::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 +818,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 +871,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 +886,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 +913,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 +956,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),
@@ -1790,6 +1870,238 @@ static void ShowRVStationPicker(Window *parent, RoadStopType rs)
new BuildRoadStationWindow(RoadTypeIsRoad(_cur_roadtype) ? &_road_station_picker_desc : &_tram_station_picker_desc, parent, rs);
}
+struct BuildRoadWaypointWindow : PickerWindowBase {
+ using WaypointList = GUIList;
+ static const uint FILTER_LENGTH = 20;
+
+ const RoadStopClass *waypoints;
+ WaypointList list;
+ StringFilter string_filter; ///< Filter for waypoint name
+ static QueryString editbox; ///< Filter editbox
+
+ BuildRoadWaypointWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent)
+ {
+ this->waypoints = RoadStopClass::Get(ROADSTOP_CLASS_WAYP);
+
+ this->CreateNestedTree();
+
+ NWidgetMatrix *matrix = this->GetWidget(WID_BROW_WAYPOINT_MATRIX);
+ matrix->SetScrollbar(this->GetScrollbar(WID_BROW_SCROLL));
+
+ this->FinishInitNested(TRANSPORT_ROAD);
+
+ this->querystrings[WID_BROW_FILTER] = &this->editbox;
+ this->editbox.cancel_button = QueryString::ACTION_CLEAR;
+ this->string_filter.SetFilterTerm(this->editbox.text.buf);
+
+ this->list.ForceRebuild();
+ this->BuildPickerList();
+ }
+
+ void Close([[maybe_unused]] int data = 0) override
+ {
+ CloseWindowById(WC_SELECT_STATION, 0);
+ this->PickerWindowBase::Close();
+ }
+
+ bool FilterByText(const RoadStopSpec *roadstopspec)
+ {
+ if (this->string_filter.IsEmpty()) return true;
+ this->string_filter.ResetState();
+ if (roadstopspec == nullptr) {
+ this->string_filter.AddLine(GetString(STR_STATION_CLASS_WAYP_WAYPOINT));
+ } else {
+ this->string_filter.AddLine(GetString(roadstopspec->name));
+ if (roadstopspec->grf_prop.grffile != nullptr) {
+ const GRFConfig *gc = GetGRFConfig(roadstopspec->grf_prop.grffile->grfid);
+ this->string_filter.AddLine(gc->GetName());
+ }
+ }
+ return this->string_filter.GetState();
+ }
+
+ void BuildPickerList()
+ {
+ if (!this->list.NeedRebuild()) return;
+
+ this->list.clear();
+ this->list.reserve(this->waypoints->GetSpecCount());
+ for (uint i = 0; i < this->waypoints->GetSpecCount(); i++) {
+ const RoadStopSpec *roadstopspec = this->waypoints->GetSpec(i);
+ if (!FilterByText(roadstopspec)) continue;
+
+ this->list.push_back(i);
+ }
+ this->list.RebuildDone();
+
+ NWidgetMatrix *matrix = this->GetWidget(WID_BROW_WAYPOINT_MATRIX);
+ matrix->SetCount((int)this->list.size());
+ matrix->SetClicked(this->UpdateSelection(_cur_waypoint_type));
+ }
+
+ uint UpdateSelection(uint type)
+ {
+ auto found = std::find(std::begin(this->list), std::end(this->list), type);
+ if (found != std::end(this->list)) return found - std::begin(this->list);
+
+ /* Selection isn't in the list, default to first */
+ if (this->list.empty()) {
+ _cur_waypoint_type = 0;
+ return -1;
+ } else {
+ _cur_waypoint_type = this->list.front();
+ return 0;
+ }
+ }
+
+ void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
+ {
+ switch (widget) {
+ case WID_BROW_WAYPOINT_MATRIX:
+ /* Two blobs high and three wide. */
+ size.width += resize.width * 2;
+ size.height += resize.height * 1;
+
+ /* Resizing in X direction only at blob size, but at pixel level in Y. */
+ resize.height = 1;
+ break;
+
+ case WID_BROW_WAYPOINT:
+ size.width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal();
+ size.height = ScaleGUITrad(58) + WidgetDimensions::scaled.fullbevel.Vertical();
+ break;
+ }
+ }
+
+ void SetStringParameters(WidgetID widget) const override
+ {
+ if (widget == WID_BROW_NAME) {
+ if (!this->list.empty() && IsInsideBS(_cur_waypoint_type, 0, this->waypoints->GetSpecCount())) {
+ const RoadStopSpec *roadstopspec = this->waypoints->GetSpec(_cur_waypoint_type);
+ if (roadstopspec == nullptr) {
+ SetDParam(0, STR_STATION_CLASS_WAYP_WAYPOINT);
+ } else {
+ SetDParam(0, roadstopspec->name);
+ }
+ } else {
+ SetDParam(0, STR_EMPTY);
+ }
+ }
+ }
+
+ void OnPaint() override
+ {
+ this->BuildPickerList();
+ this->DrawWidgets();
+ }
+
+ void DrawWidget(const Rect &r, WidgetID widget) const override
+ {
+ switch (widget) {
+ case WID_BROW_WAYPOINT: {
+ uint16_t type = this->list.at(this->GetWidget(widget)->GetParentWidget()->GetCurrentElement());
+ const RoadStopSpec *roadstopspec = this->waypoints->GetSpec(type);
+
+ DrawPixelInfo tmp_dpi;
+ Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
+ if (FillDrawPixelInfo(&tmp_dpi, ir)) {
+ AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
+ int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
+ int y = (ir.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
+ if (roadstopspec == nullptr) {
+ StationPickerDrawSprite(x, y, STATION_ROADWAYPOINT, INVALID_RAILTYPE, _cur_roadtype, 4);
+ } else {
+ DrawRoadStopTile(x, y, _cur_roadtype, roadstopspec, STATION_ROADWAYPOINT, 4);
+ }
+ }
+
+ if (!IsRoadStopAvailable(roadstopspec, STATION_ROADWAYPOINT)) {
+ GfxFillRect(ir, PC_BLACK, FILLRECT_CHECKER);
+ }
+ }
+ }
+ }
+
+ void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
+ {
+ switch (widget) {
+ case WID_BROW_WAYPOINT: {
+ uint16_t sel = this->GetWidget(widget)->GetParentWidget()->GetCurrentElement();
+ assert(sel < this->list.size());
+ uint16_t type = this->list.at(sel);
+
+ const RoadStopSpec *roadstopspec = this->waypoints->GetSpec(type);
+ if (!IsRoadStopAvailable(roadstopspec, STATION_ROADWAYPOINT)) return;
+
+ _cur_waypoint_type = type;
+ this->GetWidget(widget)->GetParentWidget()->SetClicked(sel);
+ if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
+ this->SetDirty();
+ break;
+ }
+ }
+ }
+
+ void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
+ {
+ if (!gui_scope) return;
+ this->list.ForceRebuild();
+ }
+
+ void OnEditboxChanged(WidgetID wid) override
+ {
+ if (wid == WID_BROW_FILTER) {
+ this->string_filter.SetFilterTerm(this->editbox.text.buf);
+ this->InvalidateData();
+ }
+ }
+
+ void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
+ {
+ CheckRedrawRoadWaypointCoverage(this);
+ }
+};
+
+/* static */ QueryString BuildRoadWaypointWindow::editbox(BuildRoadWaypointWindow::FILTER_LENGTH * MAX_CHAR_LENGTH, BuildRoadWaypointWindow::FILTER_LENGTH);
+
+/** Nested widget definition for the build NewGRF road waypoint window */
+static constexpr NWidgetPart _nested_build_road_waypoint_widgets[] = {
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
+ NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_WAYPOINT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
+ EndContainer(),
+ NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
+ NWidget(WWT_EDITBOX, COLOUR_DARK_GREEN, WID_BROW_FILTER), SetPadding(2), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BROW_SCROLL),
+ NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BROW_WAYPOINT_MATRIX), SetPIP(0, 2, 0), SetPadding(WidgetDimensions::unscaled.picker),
+ NWidget(WWT_PANEL, COLOUR_GREY, WID_BROW_WAYPOINT), SetDataTip(0x0, STR_WAYPOINT_GRAPHICS_TOOLTIP), SetScrollbar(WID_BROW_SCROLL), EndContainer(),
+ EndContainer(),
+ EndContainer(),
+ NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BROW_SCROLL),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
+ NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BROW_NAME), SetPadding(2), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetAlignment(SA_CENTER),
+ EndContainer(),
+ NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
+ EndContainer(),
+};
+
+static WindowDesc _build_road_waypoint_desc(
+ WDP_AUTO, "build_road_waypoint", 0, 0,
+ WC_BUILD_WAYPOINT, WC_BUILD_TOOLBAR,
+ WDF_CONSTRUCTION,
+ std::begin(_nested_build_road_waypoint_widgets), std::end(_nested_build_road_waypoint_widgets)
+);
+
+static void ShowBuildRoadWaypointPicker(Window *parent)
+{
+ new BuildRoadWaypointWindow(&_build_road_waypoint_desc, parent);
+}
+
void InitializeRoadGui()
{
_road_depot_orientation = DIAGDIR_NW;
diff --git a/src/road_map.cpp b/src/road_map.cpp
index d5642ff48c..66fe49010f 100644
--- a/src/road_map.cpp
+++ b/src/road_map.cpp
@@ -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));
diff --git a/src/road_map.h b/src/road_map.h
index 9179579b4c..06c000384c 100644
--- a/src/road_map.h
+++ b/src/road_map.h
@@ -474,7 +474,7 @@ inline void ToggleSnow(Tile t)
/** The possible road side decorations. */
-enum Roadside {
+enum Roadside : uint8_t {
ROADSIDE_BARREN = 0, ///< Road on barren land
ROADSIDE_GRASS = 1, ///< Road on grass
ROADSIDE_PAVED = 2, ///< Road with paved sidewalks
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index 734059d580..add521c024 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -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);
}
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index a1d016a1f1..249a635e03 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -907,6 +907,15 @@ bool AfterLoadGame()
}
}
+ if (IsSavegameVersionBefore(SLV_ROAD_WAYPOINTS)) {
+ /* Expansion of station type field in m6 */
+ for (auto t : Map::Iterate()) {
+ if (IsTileType(t, MP_STATION)) {
+ ClrBit(t.m6(), 6);
+ }
+ }
+ }
+
for (auto t : Map::Iterate()) {
switch (GetTileType(t)) {
case MP_STATION: {
@@ -1082,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:
@@ -1136,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));
@@ -1287,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;
diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp
index 50a247da54..136b469f8b 100644
--- a/src/saveload/company_sl.cpp
+++ b/src/saveload/company_sl.cpp
@@ -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);
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index 05ffac1c04..722a8f98d2 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -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
};
diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp
index 0db9509e53..b7e7c1c74d 100644
--- a/src/saveload/station_sl.cpp
+++ b/src/saveload/station_sl.cpp
@@ -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;
diff --git a/src/script/api/script_road.cpp b/src/script/api/script_road.cpp
index bff546e8ce..25b7fc2631 100644
--- a/src/script/api/script_road.cpp
+++ b/src/script/api/script_road.cpp
@@ -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::Do(tile, 1, 1, GetRoadStopType(tile), false);
}
diff --git a/src/station.cpp b/src/station.cpp
index efb767d50b..e0dab59067 100644
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -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;
}
}
}
diff --git a/src/station_base.h b/src/station_base.h
index 12ed87355a..94b9f00943 100644
--- a/src/station_base.h
+++ b/src/station_base.h
@@ -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
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index 615e680723..72499dff40 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -66,6 +66,7 @@
#include "timer/timer_game_economy.h"
#include "timer/timer_game_tick.h"
#include "cheat_type.h"
+#include "road_func.h"
#include "widgets/station_widget.h"
@@ -111,10 +112,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
-CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st)
+template
+CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
{
ta.Expand(1);
@@ -122,7 +124,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 +961,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 +979,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 +1029,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 +1037,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 +1055,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::Do(flags, cur_tile);
if (ret.Failed()) return ret;
@@ -1149,6 +1153,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 +1161,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
-CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st)
+template
+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 +1176,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 +1189,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 +1210,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(existing_station, station_to_join, adjacent, ta, st);
+ return FindJoiningBaseStation(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
}
/**
@@ -1214,11 +1220,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(existing_waypoint, waypoint_to_join, adjacent, ta, wp);
+ if (is_road) {
+ return FindJoiningBaseStation(existing_waypoint, waypoint_to_join, adjacent, ta, wp, [](const Waypoint *wp) -> bool { return HasBit(wp->waypoint_flags, WPF_ROAD); });
+ } else {
+ return FindJoiningBaseStation(existing_waypoint, waypoint_to_join, adjacent, ta, wp, [](const Waypoint *wp) -> bool { return !HasBit(wp->waypoint_flags, WPF_ROAD); });
+ }
}
/**
@@ -1605,6 +1616,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 +1885,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 +1898,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(existing_stop, station_to_join, adjacent, ta, st);
+ return FindJoiningBaseStation(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
}
/**
@@ -1884,15 +1906,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 +1928,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 +2007,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 +2043,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 +2081,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 +2241,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(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 +2389,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);
}
/**
@@ -3130,6 +3252,20 @@ draw_default_foundation:
DrawClearLandTile(ti, 3);
}
}
+ } else if (IsRoadWaypointTile(ti->tile)) {
+ RoadBits bits = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? ROAD_X : ROAD_Y;
+ RoadType road_rt = GetRoadTypeRoad(ti->tile);
+ RoadType tram_rt = GetRoadTypeTram(ti->tile);
+ RoadBits road = (road_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
+ RoadBits tram = (tram_rt != INVALID_ROADTYPE) ? bits : ROAD_NONE;
+ const RoadTypeInfo *road_rti = (road_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(road_rt) : nullptr;
+ const RoadTypeInfo *tram_rti = (tram_rt != INVALID_ROADTYPE) ? GetRoadTypeInfo(tram_rt) : nullptr;
+
+ if (ti->tileh != SLOPE_FLAT) {
+ DrawFoundation(ti, FOUNDATION_LEVELED);
+ }
+
+ DrawRoadGroundSprites(ti, road, tram, road_rti, tram_rti, GetRoadWaypointRoadside(ti->tile), IsRoadWaypointOnSnowOrDesert(ti->tile));
} else {
if (layout != nullptr) {
/* Sprite layout which needs preprocessing */
@@ -3154,7 +3290,7 @@ draw_default_foundation:
draw_ground = true;
}
- if (draw_ground && !IsRoadStop(ti->tile)) {
+ if (draw_ground && !IsAnyRoadStop(ti->tile)) {
SpriteID image = t->ground.sprite;
PaletteID pal = t->ground.pal;
RailTrackOffset overlay_offset;
@@ -3181,7 +3317,7 @@ draw_default_foundation:
if (HasStationRail(ti->tile) && HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
- if (IsRoadStop(ti->tile)) {
+ if (IsAnyRoadStop(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);
@@ -3192,13 +3328,21 @@ draw_default_foundation:
StationType type = GetStationType(ti->tile);
const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
+ RoadStopDrawMode stop_draw_mode{};
if (stopspec != nullptr) {
+ stop_draw_mode = stopspec->draw_mode;
int view = dir;
if (IsDriveThroughStopTile(ti->tile)) view += 4;
st = BaseStation::GetByTile(ti->tile);
RoadStopResolverObject object(stopspec, st, ti->tile, INVALID_ROADTYPE, type, view);
const SpriteGroup *group = object.Resolve();
if (group != nullptr && group->type == SGT_TILELAYOUT) {
+ if (HasBit(stopspec->flags, RSF_DRAW_MODE_REGISTER)) {
+ stop_draw_mode = static_cast(GetRegister(0x100));
+ }
+ if (type == STATION_ROADWAYPOINT && (stop_draw_mode & ROADSTOP_DRAW_MODE_WAYP_GROUND)) {
+ draw_ground = true;
+ }
t = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(nullptr);
}
}
@@ -3215,14 +3359,15 @@ draw_default_foundation:
}
if (IsDriveThroughStopTile(ti->tile)) {
- uint sprite_offset = axis == AXIS_X ? 1 : 0;
-
- DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
+ if (type != STATION_ROADWAYPOINT && (stopspec == nullptr || (stop_draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) != 0)) {
+ uint sprite_offset = axis == AXIS_X ? 1 : 0;
+ DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
+ }
} else {
/* Non-drivethrough road stops are only valid for roads. */
assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
- if ((stopspec == nullptr || (stopspec->draw_mode & ROADSTOP_DRAW_MODE_ROAD) != 0) && road_rti->UsesOverlay()) {
+ if ((stopspec == nullptr || (stop_draw_mode & ROADSTOP_DRAW_MODE_ROAD) != 0) && road_rti->UsesOverlay()) {
SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
DrawGroundSprite(ground + dir, PAL_NONE);
}
@@ -3290,7 +3435,7 @@ void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, Ro
}
/* Default waypoint has no railtype specific sprites */
- DrawRailTileSeqInGUI(x, y, t, st == STATION_WAYPOINT ? 0 : total_offset, 0, pal);
+ DrawRailTileSeqInGUI(x, y, t, (st == STATION_WAYPOINT || st == STATION_ROADWAYPOINT) ? 0 : total_offset, 0, pal);
}
static int GetSlopePixelZ_Station(TileIndex tile, uint, uint, bool)
@@ -3383,7 +3528,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 +3552,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 +3581,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;
@@ -3476,6 +3622,40 @@ static void TileLoop_Station(TileIndex tile)
TileLoop_Water(tile);
break;
+ case STATION_ROADWAYPOINT: {
+ switch (_settings_game.game_creation.landscape) {
+ case LT_ARCTIC:
+ if (IsRoadWaypointOnSnowOrDesert(tile) != (GetTileZ(tile) > GetSnowLine())) {
+ ToggleRoadWaypointOnSnowOrDesert(tile);
+ MarkTileDirtyByTile(tile);
+ }
+ break;
+
+ case LT_TROPIC:
+ if (GetTropicZone(tile) == TROPICZONE_DESERT && !IsRoadWaypointOnSnowOrDesert(tile)) {
+ ToggleRoadWaypointOnSnowOrDesert(tile);
+ MarkTileDirtyByTile(tile);
+ }
+ break;
+ }
+
+ HouseZonesBits grp = HZB_TOWN_EDGE;
+ const Town *t = ClosestTownFromTile(tile, UINT_MAX);
+ if (t != nullptr) {
+ grp = GetTownRadiusGroup(t, tile);
+ }
+
+ /* Adjust road ground type depending on 'grp' (grp is the distance to the center) */
+ Roadside new_rs = grp > HZB_TOWN_EDGE ? ROADSIDE_PAVED : ROADSIDE_GRASS;
+ Roadside cur_rs = GetRoadWaypointRoadside(tile);
+
+ if (new_rs != cur_rs) {
+ SetRoadWaypointRoadside(tile, cur_rs == ROADSIDE_BARREN ? new_rs : ROADSIDE_BARREN);
+ MarkTileDirtyByTile(tile);
+ }
+ break;
+ }
+
default: break;
}
}
@@ -3493,7 +3673,7 @@ static void AnimateTile_Station(TileIndex tile)
return;
}
- if (IsRoadStopTile(tile)) {
+ if (IsAnyRoadStopTile(tile)) {
AnimateRoadStopTile(tile);
return;
}
@@ -3553,7 +3733,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 +4559,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 +4596,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 +4624,11 @@ static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_o
} else {
if (IsDriveThroughStopTile(tile)) {
/* Remove the drive-through road stop */
- Command::Do(DC_EXEC | DC_BANKRUPT, tile, 1, 1, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, false);
+ if (IsRoadWaypoint(tile)) {
+ Command::Do(DC_EXEC | DC_BANKRUPT, tile, tile);
+ } else {
+ Command::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 +4695,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 +4715,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;
diff --git a/src/station_gui.cpp b/src/station_gui.cpp
index 241a512b12..e3e0c65f6d 100644
--- a/src/station_gui.cpp
+++ b/src/station_gui.cpp
@@ -41,6 +41,29 @@
#include "safeguards.h"
+struct StationTypeFilter
+{
+ using StationType = Station;
+
+ 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; }
+};
+
+template
+struct GenericWaypointTypeFilter
+{
+ using StationType = Waypoint;
+
+ 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;
+using RoadWaypointTypeFilter = GenericWaypointTypeFilter;
+
/**
* Calculates and draws the accepted or supplied cargo around the selected tile(s)
* @param left x position where the string is to be drawn
@@ -87,7 +110,7 @@ void FindStationsAroundSelection()
{
/* With distant join we don't know which station will be selected, so don't show any */
if (_ctrl_pressed) {
- SetViewportCatchmentSpecializedStation(nullptr, true);
+ SetViewportCatchmentSpecializedStation(nullptr, true);
return;
}
@@ -96,9 +119,9 @@ void FindStationsAroundSelection()
/* If the current tile is already a station, then it must be the nearest station. */
if (IsTileType(location.tile, MP_STATION) && GetTileOwner(location.tile) == _local_company) {
- T *st = T::GetByTile(location.tile);
- if (st != nullptr) {
- SetViewportCatchmentSpecializedStation(st, true);
+ typename T::StationType *st = T::StationType::GetByTile(location.tile);
+ if (st != nullptr && T::IsValidBaseStation(st)) {
+ SetViewportCatchmentSpecializedStation(st, true);
return;
}
}
@@ -107,17 +130,17 @@ 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::EXPECTED_FACIL == FACIL_WAYPOINT && !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(0, x - max_c), std::max(0, y - max_c)), TileXY(std::min(Map::MaxX(), x + location.w + max_c), std::min(Map::MaxY(), y + location.h + max_c)));
- T *adjacent = nullptr;
+ typename T::StationType *adjacent = nullptr;
/* Direct loop instead of ForAllStationsAroundTiles as we are not interested in catchment area */
for (TileIndex tile : ta) {
if (IsTileType(tile, MP_STATION) && GetTileOwner(tile) == _local_company) {
- T *st = T::GetByTile(tile);
- if (st == nullptr) continue;
+ typename T::StationType *st = T::StationType::GetByTile(tile);
+ if (st == nullptr || !T::IsValidBaseStation(st)) continue;
if (adjacent != nullptr && st != adjacent) {
/* Multiple nearby, distant join is required. */
adjacent = nullptr;
@@ -126,7 +149,7 @@ void FindStationsAroundSelection()
adjacent = st;
}
}
- SetViewportCatchmentSpecializedStation(adjacent, true);
+ SetViewportCatchmentSpecializedStation(adjacent, true);
}
/**
@@ -148,12 +171,13 @@ void CheckRedrawStationCoverage(const Window *w)
w->SetDirty();
if (_settings_client.gui.station_show_coverage && _thd.drawstyle == HT_RECT) {
- FindStationsAroundSelection();
+ FindStationsAroundSelection();
}
}
}
-void CheckRedrawWaypointCoverage(const Window *)
+template
+void CheckRedrawWaypointCoverage()
{
/* Test if ctrl state changed */
static bool _last_ctrl_pressed;
@@ -166,11 +190,21 @@ void CheckRedrawWaypointCoverage(const Window *)
_thd.dirty &= ~1;
if (_thd.drawstyle == HT_RECT) {
- FindStationsAroundSelection();
+ FindStationsAroundSelection();
}
}
}
+void CheckRedrawRailWaypointCoverage(const Window *)
+{
+ CheckRedrawWaypointCoverage();
+}
+
+void CheckRedrawRoadWaypointCoverage(const Window *)
+{
+ CheckRedrawWaypointCoverage();
+}
+
/**
* Draw small boxes of cargo amount and ratings data at the given
* coordinates. If amount exceeds 576 units, it is shown 'full', same
@@ -2171,7 +2205,7 @@ static std::vector _stations_nearby_list;
* station spread.
* @param tile Tile just being checked
* @param user_data Pointer to TileArea context
- * @tparam T the type of station to look for
+ * @tparam T the station filter type
*/
template
static bool AddNearbyStation(TileIndex tile, void *user_data)
@@ -2196,7 +2230,7 @@ static bool AddNearbyStation(TileIndex tile, void *user_data)
/* This station is (likely) a waypoint */
if (!T::IsValidID(sid)) return false;
- T *st = T::Get(sid);
+ BaseStation *st = BaseStation::Get(sid);
if (st->owner != _local_company || std::find(_stations_nearby_list.begin(), _stations_nearby_list.end(), sid) != _stations_nearby_list.end()) return false;
if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST).Succeeded()) {
@@ -2213,10 +2247,10 @@ static bool AddNearbyStation(TileIndex tile, void *user_data)
* @param ta Base tile area of the to-be-built station
* @param distant_join Search for adjacent stations (false) or stations fully
* within station spread
- * @tparam T the type of station to look for
+ * @tparam T the station filter type, for stations to look for
*/
template
-static const T *FindStationsNearby(TileArea ta, bool distant_join)
+static const BaseStation *FindStationsNearby(TileArea ta, bool distant_join)
{
TileArea ctx = ta;
@@ -2226,12 +2260,12 @@ static const T *FindStationsNearby(TileArea ta, bool distant_join)
/* Check the inside, to return, if we sit on another station */
for (TileIndex t : ta) {
- if (t < Map::Size() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return T::GetByTile(t);
+ if (t < Map::Size() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return BaseStation::GetByTile(t);
}
/* Look for deleted stations */
for (const BaseStation *st : BaseStation::Iterate()) {
- if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company) {
+ if (T::IsValidBaseStation(st) && !st->IsInUse() && st->owner == _local_company) {
/* Include only within station spread (yes, it is strictly less than) */
if (std::max(DistanceMax(ta.tile, st->xy), DistanceMax(TileAddXY(ta.tile, ta.w - 1, ta.h - 1), st->xy)) < _settings_game.station.station_spread) {
_deleted_stations_nearby.push_back({st->xy, st->index});
@@ -2274,7 +2308,7 @@ static constexpr NWidgetPart _nested_select_station_widgets[] = {
/**
* Window for selecting stations/waypoints to (distant) join to.
- * @tparam T The type of station to join with
+ * @tparam T The station filter type, for stations to join with
*/
template
struct SelectStationWindow : Window {
@@ -2289,7 +2323,7 @@ struct SelectStationWindow : Window {
{
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_JS_SCROLLBAR);
- this->GetWidget(WID_JS_CAPTION)->widget_data = T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
+ this->GetWidget(WID_JS_CAPTION)->widget_data = T::IsWaypoint() ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
this->FinishInitNested(0);
this->OnInvalidateData(0);
@@ -2298,7 +2332,7 @@ struct SelectStationWindow : Window {
void Close([[maybe_unused]] int data = 0) override
{
- SetViewportCatchmentSpecializedStation(nullptr, true);
+ SetViewportCatchmentSpecializedStation(nullptr, true);
_thd.freeze = false;
this->Window::Close();
@@ -2309,13 +2343,13 @@ struct SelectStationWindow : Window {
if (widget != WID_JS_PANEL) return;
/* Determine the widest string */
- Dimension d = GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
+ Dimension d = GetStringBoundingBox(T::IsWaypoint() ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
for (const auto &station : _stations_nearby_list) {
if (station == NEW_STATION) continue;
- const T *st = T::Get(station);
+ const BaseStation *st = BaseStation::Get(station);
SetDParam(0, st->index);
SetDParam(1, st->facilities);
- d = maxdim(d, GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION));
+ d = maxdim(d, GetStringBoundingBox(T::IsWaypoint() ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION));
}
resize.height = d.height;
@@ -2333,12 +2367,12 @@ struct SelectStationWindow : Window {
auto [first, last] = this->vscroll->GetVisibleRangeIterators(_stations_nearby_list);
for (auto it = first; it != last; ++it, tr.top += this->resize.step_height) {
if (*it == NEW_STATION) {
- DrawString(tr, T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
+ DrawString(tr, T::IsWaypoint() ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
} else {
- const T *st = T::Get(*it);
+ const BaseStation *st = BaseStation::Get(*it);
SetDParam(0, st->index);
SetDParam(1, st->facilities);
- DrawString(tr, T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION);
+ DrawString(tr, T::IsWaypoint() ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION);
}
}
@@ -2387,14 +2421,14 @@ struct SelectStationWindow : Window {
void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override
{
if (widget != WID_JS_PANEL) {
- SetViewportCatchmentSpecializedStation(nullptr, true);
+ SetViewportCatchmentSpecializedStation(nullptr, true);
return;
}
/* Show coverage area of station under cursor */
auto it = this->vscroll->GetScrolledItemFromWidget(_stations_nearby_list, pt.y, this, WID_JS_PANEL, WidgetDimensions::scaled.framerect.top);
- const T *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : T::Get(*it);
- SetViewportCatchmentSpecializedStation(st, true);
+ const typename T::StationType *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : T::StationType::Get(*it);
+ SetViewportCatchmentSpecializedStation(st, true);
}
};
@@ -2410,7 +2444,7 @@ static WindowDesc _select_station_desc(
* Check whether we need to show the station selection window.
* @param cmd Command to build the station.
* @param ta Tile area of the to-be-built station
- * @tparam T the type of station
+ * @tparam T the station filter type
* @return whether we need to show the station selection window.
*/
template
@@ -2437,7 +2471,7 @@ static bool StationJoinerNeeded(TileArea ta, const StationPickerCmdProc &proc)
/* Test for adjacent station or station below selection.
* If adjacent-stations is disabled and we are building next to a station, do not show the selection window.
* but join the other station immediately. */
- const T *st = FindStationsNearby(ta, false);
+ const BaseStation *st = FindStationsNearby(ta, false);
return st == nullptr && (_settings_game.station.adjacent_stations || std::any_of(std::begin(_stations_nearby_list), std::end(_stations_nearby_list), [](StationID s) { return s != NEW_STATION; }));
}
@@ -2465,15 +2499,25 @@ void ShowSelectBaseStationIfNeeded(TileArea ta, StationPickerCmdProc&& proc)
*/
void ShowSelectStationIfNeeded(TileArea ta, StationPickerCmdProc proc)
{
- ShowSelectBaseStationIfNeeded(ta, std::move(proc));
+ ShowSelectBaseStationIfNeeded(ta, std::move(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(ta, std::move(proc));
+ ShowSelectBaseStationIfNeeded(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(ta, std::move(proc));
}
diff --git a/src/station_gui.h b/src/station_gui.h
index 009bcd7bb9..9b142d02b6 100644
--- a/src/station_gui.h
+++ b/src/station_gui.h
@@ -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;
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 */
diff --git a/src/station_map.h b/src/station_map.h
index 34228e9ecf..e4edd660e3 100644
--- a/src/station_map.h
+++ b/src/station_map.h
@@ -44,7 +44,7 @@ static const int GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET = 4; ///< The offset for the
inline StationType GetStationType(Tile t)
{
assert(IsTileType(t, MP_STATION));
- return (StationType)GB(t.m6(), 3, 3);
+ return (StationType)GB(t.m6(), 3, 4);
}
/**
@@ -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,21 +265,64 @@ 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);
+/**
+ * Get the decorations of a road waypoint.
+ * @param tile The tile to query.
+ * @return The road decoration of the tile.
+ */
+static inline Roadside GetRoadWaypointRoadside(Tile tile)
+{
+ assert(IsRoadWaypointTile(tile));
+ return (Roadside)GB(tile.m3(), 2, 2);
+}
+
+/**
+ * Set the decorations of a road waypoint.
+ * @param tile The tile to change.
+ * @param s The new road decoration of the tile.
+ */
+static inline void SetRoadWaypointRoadside(Tile tile, Roadside s)
+{
+ assert(IsRoadWaypointTile(tile));
+ SB(tile.m3(), 2, 2, s);
+}
+
+/**
+ * Check if a road waypoint tile has snow/desert.
+ * @param t The tile to query.
+ * @return True if the tile has snow/desert.
+ */
+static inline bool IsRoadWaypointOnSnowOrDesert(Tile t)
+{
+ assert(IsRoadWaypointTile(t));
+ return HasBit(t.m8(), 15);
+}
+
+/**
+ * Toggle the snow/desert state of a road waypoint tile.
+ * @param t The tile to change.
+ */
+static inline void ToggleRoadWaypointOnSnowOrDesert(Tile t)
+{
+ assert(IsRoadWaypointTile(t));
+ ToggleBit(t.m8(), 15);
+}
+
/**
* Get the station graphics of this airport tile
* @param t the tile to query
@@ -252,13 +338,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 {
@@ -362,7 +448,7 @@ inline void SetStationTileBlocked(Tile t, bool b)
inline bool CanStationTileHaveWires(Tile t)
{
assert(HasStationRail(t));
- return HasBit(t.m6(), 6);
+ return HasBit(t.m6(), 1);
}
/**
@@ -374,7 +460,7 @@ inline bool CanStationTileHaveWires(Tile t)
inline void SetStationTileHaveWires(Tile t, bool b)
{
assert(HasStationRail(t));
- SB(t.m6(), 6, 1, b ? 1 : 0);
+ SB(t.m6(), 1, 1, b ? 1 : 0);
}
/**
@@ -555,12 +641,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 +654,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);
}
@@ -632,7 +718,7 @@ inline void MakeStation(Tile t, Owner o, StationID sid, StationType st, uint8_t
t.m4() = 0;
t.m5() = section;
SB(t.m6(), 2, 1, 0);
- SB(t.m6(), 3, 3, st);
+ SB(t.m6(), 3, 4, st);
t.m7() = 0;
t.m8() = 0;
}
@@ -699,9 +785,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);
diff --git a/src/station_type.h b/src/station_type.h
index 4e6968ac31..54d7939e66 100644
--- a/src/station_type.h
+++ b/src/station_type.h
@@ -37,6 +37,7 @@ enum StationType {
STATION_DOCK,
STATION_BUOY,
STATION_WAYPOINT,
+ STATION_ROADWAYPOINT,
};
/** Types of RoadStops */
diff --git a/src/table/sprites.h b/src/table/sprites.h
index ea522724b5..0ab4f1bc99 100644
--- a/src/table/sprites.h
+++ b/src/table/sprites.h
@@ -307,8 +307,16 @@ static const uint16_t EMPTY_BOUNDING_BOX_SPRITE_COUNT = 1;
static const SpriteID SPR_PALETTE_BASE = SPR_EMPTY_BOUNDING_BOX + EMPTY_BOUNDING_BOX_SPRITE_COUNT;
static const uint16_t PALETTE_SPRITE_COUNT = 1;
+/** Road waypoint sprites. */
+static const SpriteID SPR_ROAD_WAYPOINTS_BASE = SPR_PALETTE_BASE + PALETTE_SPRITE_COUNT;
+static const SpriteID SPR_ROAD_WAYPOINT_Y_W = SPR_ROAD_WAYPOINTS_BASE;
+static const SpriteID SPR_ROAD_WAYPOINT_Y_E = SPR_ROAD_WAYPOINTS_BASE + 1;
+static const SpriteID SPR_ROAD_WAYPOINT_X_W = SPR_ROAD_WAYPOINTS_BASE + 2;
+static const SpriteID SPR_ROAD_WAYPOINT_X_E = SPR_ROAD_WAYPOINTS_BASE + 3;
+static const uint16_t ROAD_WAYPOINTS_SPRITE_COUNT = 4;
+
/* From where can we start putting NewGRFs? */
-static const SpriteID SPR_NEWGRFS_BASE = SPR_PALETTE_BASE + PALETTE_SPRITE_COUNT;
+static const SpriteID SPR_NEWGRFS_BASE = SPR_ROAD_WAYPOINTS_BASE + ROAD_WAYPOINTS_SPRITE_COUNT;
/* Manager face sprites */
static const SpriteID SPR_GRADIENT = 874; // background gradient behind manager face
diff --git a/src/table/station_land.h b/src/table/station_land.h
index 6e3db1d0d0..fc47de8d77 100644
--- a/src/table/station_land.h
+++ b/src/table/station_land.h
@@ -763,6 +763,20 @@ static const DrawTileSeqStruct _station_display_datas_0171[] = {
TILE_SEQ_END()
};
+/* road waypoint X */
+static const DrawTileSeqStruct _station_display_datas_road_waypoint_X[] = {
+ TILE_SEQ_LINE( 0, 0, 0, 16, 3, 16, SPR_ROAD_WAYPOINT_X_W | (1U << PALETTE_MODIFIER_COLOUR))
+ TILE_SEQ_LINE( 0, 13, 0, 16, 3, 16, SPR_ROAD_WAYPOINT_X_E | (1U << PALETTE_MODIFIER_COLOUR))
+ TILE_SEQ_END()
+};
+
+/* road waypoint Y */
+static const DrawTileSeqStruct _station_display_datas_road_waypoint_Y[] = {
+ TILE_SEQ_LINE(13, 0, 0, 3, 16, 16, SPR_ROAD_WAYPOINT_Y_W | (1U << PALETTE_MODIFIER_COLOUR))
+ TILE_SEQ_LINE( 0, 0, 0, 3, 16, 16, SPR_ROAD_WAYPOINT_Y_E | (1U << PALETTE_MODIFIER_COLOUR))
+ TILE_SEQ_END()
+};
+
static const DrawTileSeqStruct _station_display_datas_waypoint_X[] = {
TILE_SEQ_LINE( 0, 0, 0, 16, 5, 23, SPR_WAYPOINT_X_1 | (1U << PALETTE_MODIFIER_COLOUR))
TILE_SEQ_LINE( 0, 11, 0, 16, 5, 23, SPR_WAYPOINT_X_2 | (1U << PALETTE_MODIFIER_COLOUR))
@@ -955,6 +969,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(0, nullptr)
+ TILE_SPRITE_LINE(0, nullptr)
+ TILE_SPRITE_LINE(0, nullptr)
+ TILE_SPRITE_LINE(0, nullptr)
+ TILE_SPRITE_LINE(SPR_ROAD_PAVED_STRAIGHT_X, _station_display_datas_road_waypoint_X)
+ TILE_SPRITE_LINE(SPR_ROAD_PAVED_STRAIGHT_Y, _station_display_datas_road_waypoint_Y)
+};
+
static const DrawTileSprites _station_display_datas_oilrig[] = {
TILE_SPRITE_LINE(SPR_FLAT_WATER_TILE, _station_display_nothing)
};
@@ -999,4 +1022,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,
};
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp
index 2fae67fc47..16eac9d467 100644
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -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);
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 2e1b82b172..d329b0b09a 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -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);
}
diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp
index 240a5cb9c5..dc20cd469c 100644
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -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));
diff --git a/src/viewport_type.h b/src/viewport_type.h
index 4a433387dd..0fde051f38 100644
--- a/src/viewport_type.h
+++ b/src/viewport_type.h
@@ -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
diff --git a/src/waypoint.cpp b/src/waypoint.cpp
index db1b36b1ec..b08a971bad 100644
--- a/src/waypoint.cpp
+++ b/src/waypoint.cpp
@@ -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;
diff --git a/src/waypoint_base.h b/src/waypoint_base.h
index f431958e6e..091ef7fed2 100644
--- a/src/waypoint_base.h
+++ b/src/waypoint_base.h
@@ -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 {
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.
diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp
index 0ffffcfc09..565a76411d 100644
--- a/src/waypoint_cmd.cpp
+++ b/src/waypoint_cmd.cpp
@@ -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]);
diff --git a/src/waypoint_cmd.h b/src/waypoint_cmd.h
index 02914124c6..05c916c952 100644
--- a/src/waypoint_cmd.h
+++ b/src/waypoint_cmd.h
@@ -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)
diff --git a/src/waypoint_func.h b/src/waypoint_func.h
index 2906fa6369..4342a9b4ee 100644
--- a/src/waypoint_func.h
+++ b/src/waypoint_func.h
@@ -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);
diff --git a/src/waypoint_gui.cpp b/src/waypoint_gui.cpp
index f73e301a17..845da26023 100644
--- a/src/waypoint_gui.cpp
+++ b/src/waypoint_gui.cpp
@@ -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(WID_W_SHOW_VEHICLES)->SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP);
+ }
+ if (this->vt == VEH_ROAD) {
+ this->GetWidget(WID_W_SHOW_VEHICLES)->SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP);
+ }
+ if (this->vt != VEH_SHIP) {
this->GetWidget(WID_W_CENTER_VIEW)->tool_tip = STR_WAYPOINT_VIEW_CENTER_TOOLTIP;
this->GetWidget(WID_W_RENAME)->tool_tip = STR_WAYPOINT_VIEW_CHANGE_WAYPOINT_NAME;
}
diff --git a/src/widgets/road_widget.h b/src/widgets/road_widget.h
index 5da57d024c..6fa7fb24f6 100644
--- a/src/widgets/road_widget.h
+++ b/src/widgets/road_widget.h
@@ -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.
@@ -70,4 +71,13 @@ enum BuildRoadStationWidgets : WidgetID {
WID_BROS_NEWST_SCROLL, ///< Scrollbar of the #WID_BROS_NEWST_LIST.
};
+/** Widgets of the #BuildRoadWaypointWindow class. */
+enum BuildRoadWaypointWidgets : WidgetID {
+ WID_BROW_FILTER, ///< Text filter.
+ WID_BROW_WAYPOINT_MATRIX, ///< Matrix with waypoints.
+ WID_BROW_WAYPOINT, ///< A single waypoint.
+ WID_BROW_SCROLL, ///< Scrollbar for the matrix.
+ WID_BROW_NAME, ///< Name of selected waypoint.
+};
+
#endif /* WIDGETS_ROAD_WIDGET_H */