Compare commits

...

10 Commits

Author SHA1 Message Date
Jonathan G Rennison 46e9b7de89
Merge 2e20275f99 into bf8de188ec 2024-04-27 01:33:09 +03:00
Peter Nelson bf8de188ec
Codechange: Use member initialization of GRFFilePropsBase. (#12581)
Don't blame compilers for our sloppy initialisation.

Removes memset, and lengthof.
2024-04-26 22:58:54 +01:00
Peter Nelson 72c55128d2
Codechange: Remove write-only spec_id from RoadStopSpec. (#12582)
Comment is incorrect about its value too.
2024-04-26 21:56:30 +01:00
Peter Nelson a6d401debf
Fix: Properly test for presence of waypoint in NewGRF resolver. (#12579)
Test whether the BaseStation itself a Station or Waypoint, instead of by the station class ID assigned to it.
2024-04-26 17:47:53 +01:00
Jonathan G Rennison 2e20275f99 Add: NewGRF custom road waypoint support 2024-04-24 21:50:29 +01:00
Jonathan G Rennison cdba46255d Change: Add variable ground types for road waypoints 2024-04-24 21:50:29 +01:00
Jonathan G Rennison 60e8b633af Add: Road waypoint default graphics 2024-04-24 21:50:29 +01:00
Jonathan G Rennison 3bcf5dfd8f Add: Road waypoint functionality 2024-04-24 21:50:29 +01:00
Jonathan G Rennison acb52a2af9 Codechange: Use filter template struct for nearby station type filtering 2024-04-24 21:50:29 +01:00
Jonathan G Rennison 7123dfddae Codechange: Increase size of StationType field in map array
Move can station have wires bit to make room
2024-04-24 21:50:29 +01:00
54 changed files with 1262 additions and 245 deletions

View File

@ -876,6 +876,22 @@
<li>m2: index into the array of stations</li>
<li>m3 bits 7..4: persistent random data for railway stations/waypoints and airports)</li>
<li>m3 bits 7..4: <a href="#OwnershipInfo">owner</a> of tram tracks (road stop)</li>
<li>m3 bits 3..2: ground type (road waypoints)
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>on bare land</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>on grass</td>
</tr>
<tr>
<td><tt>2</tt>&nbsp; </td>
<td>paved</td>
</tr>
</table>
</li>
<li>m4: custom station id; 0 means standard graphics</li>
<li>m4: <a href="#RoadType">Roadtype</a> for road stops</li>
<li>m5: graphics index (range from 0..255 for each station type):
@ -990,13 +1006,14 @@
</table>
</li>
<li>m6 bit 7: rail station / waypoint may have catenary pylons</li>
<li>m6 bit 6: rail station / waypoint may have catenary wires</li>
<li>m6 bits 5..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint)</li>
<li>m6 bits 6..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint, road waypoint)</li>
<li>m6 bit 2: pbs reservation state for railway stations/waypoints</li>
<li>m6 bit 1: rail station / waypoint may have catenary wires</li>
<li>m6 bit 0: rail station / waypoint is blocked</li>
<li>m7 bits 4..0: <a href="#OwnershipInfo">owner</a> of road (road stops)</li>
<li>m7: animation frame (railway stations/waypoints, airports)</li>
<li>m8 bit 15: Snow or desert present (road waypoints)</li>
<li>m8 bits 11..6: <a href="#TramType">Tramtype</a></li>
<li>m8 bits 5..0: <a href="#TrackType">track type</a> for railway stations/waypoints</li>
<li>m8 bits 5..0: custom road stop id; 0 means standard graphics</li>

View File

@ -181,14 +181,14 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td>
</tr>
<tr>
<td rowspan=7>5</td>
<td rowspan=8>5</td>
<td class="caption">rail station</td>
<td class="bits" rowspan=7><span class="free">O</span><span class="used" title="Water class">XX</span> <span class="used" title="Owner">XXXXX</span></td>
<td class="bits" rowspan=7><span class="pool" title="Station index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="bits" rowspan=8><span class="free">O</span><span class="used" title="Water class">XX</span> <span class="used" title="Owner">XXXXX</span></td>
<td class="bits" rowspan=8><span class="pool" title="Station index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="used" title="Random bits">XXXX</span> <span class="free">OOOO</span></td>
<td class="bits" rowspan=2><span class="used" title="Custom station specifications ID">XXXX XXXX</span></td>
<td class="bits"><span class="used" title="Graphics index">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="used" title="May have pylons">X</span><span class="used" title="May have wires">X</span><span class="used" title="Station type">XXX</span> <span class="used" title="Reserved track">X</span><span class="free">O</span><span class="used" title="Tile is blocked">X</span></td>
<td class="bits" rowspan=2><span class="used" title="May have pylons">X</span><span class="used" title="Station type">XXXX</span> <span class="used" title="Reserved track">X</span><span class="used" title="May have wires">X</span><span class="used" title="Tile is blocked">X</span></td>
<td class="bits" rowspan=2><span class="used" title="Animation frame">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OOOO OOOO OO</span><span class="used" title="Railway type">XX XXXX</span></td>
</tr>
@ -199,12 +199,17 @@ the array so you can quickly see what is used and what is not.
<tr>
<td class="caption">road stop</td>
<td class="bits"><span class="used" title="Owner of tram">XXXX</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="free">OO</span><span class="used" title="Roadtype for road stop">XX XXXX</span></td>
<td class="bits"><span class="usable" title="Graphics index">OOOO O</span><span class="used" title="Graphics index: 00 (exit towards NE), 01 (exit towards SE), 02 (exit towards SW), 03 (exit towards NW), 04 (drive through X), 05 (drive through Y)">XXX</span></td>
<td class="bits" rowspan=5><span class="free">OO</span><span class="used" title="Station type">XX X</span><span class="free">OOO</span></td>
<td class="bits"><span class="free">OOO</span><span class="used" title="Owner of road">X XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OO</span><span class="used" title="Roadtype for road stop">XX XXXX</span></td>
<td class="bits" rowspan=2><span class="usable" title="Graphics index">OOOO O</span><span class="used" title="Graphics index: 00 (exit towards NE), 01 (exit towards SE), 02 (exit towards SW), 03 (exit towards NW), 04 (drive through X), 05 (drive through Y)">XXX</span></td>
<td class="bits" rowspan=6><span class="free">O</span><span class="used" title="Station type">XXX X</span><span class="free">OOO</span></td>
<td class="bits" rowspan=2><span class="free">OOO</span><span class="used" title="Owner of road">X XXXX</span></td>
<td class="bits"><span class="free">OOOO</span> <span class="used" title="Tram type">XXXX XX</span> <span class="used" title="Custom road stops specifications ID">XXXXXX</span></td>
</tr>
<tr>
<td class="caption">road waypoint</td>
<td class="bits"><span class="used" title="Owner of tram">XXXX</span> <span class="used" title="Pavement type">XX</span><span class="free">OO</span></td>
<td class="bits"><span class="used" title="Snow/desert present">X</span><span class="free">OOO</span> <span class="used" title="Tram type">XXXX XX<span class="free">OO OOOO</span></td>
</tr>
<tr>
<td class="caption">airport</td>
<td class="bits"><span class="used" title="Random bits">XXXX</span> <span class="free">OOOO</span></td>

Binary file not shown.

View File

@ -1 +1 @@
4f03553f614a06d86dc06376db3353c7
8bc3926cb50e19747de498357417d973

View File

@ -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

View File

@ -98,3 +98,4 @@
#include "mono.nfo"
#include "tunnel_portals.nfo"
#include "palette.nfo"
#include "road_waypoints.nfo"

View File

@ -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 <http://www.gnu.org/licenses/>.
//
-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

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

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

View File

@ -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

View File

@ -4846,7 +4846,6 @@ static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, ByteR
uint32_t classid = buf->ReadDWord();
rs->cls_id = RoadStopClass::Allocate(BSWAP32(classid));
rs->spec_id = id + i;
break;
}
@ -4888,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
@ -6426,6 +6425,7 @@ static constexpr auto _action5_types = std::to_array<Action5Type>({
/* 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" },
});
/**

View File

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

View File

@ -308,25 +308,15 @@ bool Convert8bitBooleanCallback(const struct GRFFile *grffile, uint16_t cbid, ui
*/
template <size_t Tcnt>
struct GRFFilePropsBase {
GRFFilePropsBase() : local_id(0), grffile(nullptr)
{
/* The lack of some compilers to provide default constructors complying to the specs
* requires us to zero the stuff ourself. */
memset(spritegroup, 0, sizeof(spritegroup));
}
uint16_t local_id; ///< id defined by the grf file for this entity
const struct GRFFile *grffile; ///< grf file that introduced this entity
const struct SpriteGroup *spritegroup[Tcnt]; ///< pointer to the different sprites of the entity
uint16_t local_id = 0; ///< id defined by the grf file for this entity
const struct GRFFile *grffile = nullptr; ///< grf file that introduced this entity
std::array<const struct SpriteGroup *, Tcnt> spritegroup{}; ///< pointers to the different sprites of the entity
};
/** Data related to the handling of grf files. */
struct GRFFileProps : GRFFilePropsBase<1> {
/** Set all default data constructor for the props. */
GRFFileProps(uint16_t subst_id = 0) :
GRFFilePropsBase<1>(), subst_id(subst_id), override(subst_id)
{
}
constexpr GRFFileProps(uint16_t subst_id = 0) : subst_id(subst_id), override(subst_id) {}
uint16_t subst_id;
uint16_t override; ///< id of the entity been replaced by

View File

@ -53,7 +53,7 @@ const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, CargoID cargo, Eng
void SetCustomEngineSprites(EngineID engine, uint8_t cargo, const SpriteGroup *group)
{
Engine *e = Engine::Get(engine);
assert(cargo < lengthof(e->grf_prop.spritegroup));
assert(cargo < std::size(e->grf_prop.spritegroup));
if (e->grf_prop.spritegroup[cargo] != nullptr) {
GrfMsg(6, "SetCustomEngineSprites: engine {} cargo {} already has group -- replacing", engine, cargo);
@ -1062,7 +1062,7 @@ VehicleResolverObject::VehicleResolverObject(EngineID engine_type, const Vehicle
if (this->root_spritegroup == nullptr) {
const Engine *e = Engine::Get(engine_type);
CargoID cargo = v != nullptr ? v->cargo_type : SpriteGroupCargo::SG_PURCHASE;
assert(cargo < lengthof(e->grf_prop.spritegroup));
assert(cargo < std::size(e->grf_prop.spritegroup));
this->root_spritegroup = e->grf_prop.spritegroup[cargo] != nullptr ? e->grf_prop.spritegroup[cargo] : e->grf_prop.spritegroup[SpriteGroupCargo::SG_DEFAULT];
}
}

View File

@ -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);
}

View File

@ -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. */
@ -127,13 +129,12 @@ struct RoadStopSpec {
*/
GRFFilePropsBase<NUM_CARGO + 3> grf_prop;
RoadStopClassID cls_id; ///< The class to which this spec belongs.
int spec_id; ///< The ID of this spec inside the class.
StringID name; ///< Name of this stop
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

View File

@ -504,7 +504,7 @@ uint32_t Waypoint::GetNewGRFVariable(const ResolverObject &, uint8_t variable, [
/* virtual */ const SpriteGroup *StationResolverObject::ResolveReal(const RealSpriteGroup *group) const
{
if (this->station_scope.st == nullptr || this->station_scope.statspec->cls_id == STAT_CLASS_WAYP) {
if (this->station_scope.st == nullptr || !Station::IsExpected(this->station_scope.st)) {
return group->loading[0];
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)) {

View File

@ -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 */

View File

@ -16,6 +16,7 @@
#include "command_func.h"
#include "road_cmd.h"
#include "station_func.h"
#include "waypoint_func.h"
#include "window_func.h"
#include "vehicle_func.h"
#include "sound_func.h"
@ -33,6 +34,7 @@
#include "strings_func.h"
#include "core/geometry_func.hpp"
#include "station_cmd.h"
#include "waypoint_cmd.h"
#include "road_cmd.h"
#include "tunnelbridge_cmd.h"
#include "newgrf_roadstop.h"
@ -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<CMD_BUILD_ROAD_WAYPOINT>::Post(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT, tile, AXIS_X, 1, 1, ROADSTOP_CLASS_WAYP, 0, INVALID_STATION, false);
}
}
/**
* Callback for placing a bus station.
* @param tile Position to place the station.
@ -349,22 +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<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
} else {
this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT);
this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT : STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT);
this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION);
this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION);
}
@ -444,6 +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<CMD_REMOVE_FROM_ROAD_WAYPOINT>::Post(STR_ERROR_CAN_T_REMOVE_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile);
} else {
TileArea ta(start_tile, end_tile);
Axis axis = select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y;
bool adjacent = _ctrl_pressed;
uint16_t waypoint_type = _cur_waypoint_type;
auto proc = [=](bool test, StationID to_join) -> bool {
if (test) {
return Command<CMD_BUILD_ROAD_WAYPOINT>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_ROAD_WAYPOINT>()), ta.tile, axis, ta.w, ta.h, ROADSTOP_CLASS_WAYP, waypoint_type, INVALID_STATION, adjacent).Succeeded();
} else {
return Command<CMD_BUILD_ROAD_WAYPOINT>::Post(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, axis, ta.w, ta.h, ROADSTOP_CLASS_WAYP, waypoint_type, to_join, adjacent);
}
};
ShowSelectRoadWaypointIfNeeded(ta, proc);
}
}
break;
case DDSP_BUILD_BUSSTOP:
case DDSP_REMOVE_BUSSTOP:
if (this->IsWidgetLowered(WID_ROT_BUS_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_BUS, _cur_roadtype)) {
@ -749,6 +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<uint>;
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<NWidgetMatrix>(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<NWidgetMatrix>(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<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->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<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->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<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->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;

View File

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

View File

@ -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

View File

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

View File

@ -907,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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <class T>
CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st)
template <class T, class F>
CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID company, T **st, F filter)
{
ta.Expand(1);
@ -122,7 +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<CMD_LANDSCAPE_CLEAR>::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 <class T, StringID error_message>
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st)
template <class T, StringID error_message, class F>
CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st, F filter)
{
assert(*st == nullptr);
bool check_surrounding = true;
@ -1171,7 +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<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st);
return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST>(existing_station, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
}
/**
@ -1214,11 +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<Waypoint, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp);
if (is_road) {
return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_ROADWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp, [](const Waypoint *wp) -> bool { return HasBit(wp->waypoint_flags, WPF_ROAD); });
} else {
return FindJoiningBaseStation<Waypoint, STR_ERROR_MUST_REMOVE_RAILWAYPOINT_FIRST>(existing_waypoint, waypoint_to_join, adjacent, ta, wp, [](const Waypoint *wp) -> bool { return !HasBit(wp->waypoint_flags, WPF_ROAD); });
}
}
/**
@ -1605,6 +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<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st);
return FindJoiningBaseStation<Station, STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST>(existing_stop, station_to_join, adjacent, ta, st, [](const Station *) -> bool { return true; });
}
/**
@ -1884,15 +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<int>(ROAD_STOP_TRACKBIT_FACTOR));
}
Company::Get(wp->owner)->infrastructure.station--;
DirtyCompanyInfrastructureWindows(wp->owner);
DeleteAnimatedTile(tile);
uint specindex = GetCustomRoadStopSpecIndex(tile);
DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile.base());
DoClearSquare(tile);
wp->rect.AfterRemoveTile(wp, tile);
wp->RemoveRoadStopTileData(tile);
if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(wp, specindex);
if (replacement_spec_index < 0) {
MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
UpdateStationSignCoord(wp);
/* if we deleted the whole waypoint, delete the road facility. */
if (wp->road_waypoint_area.tile == INVALID_TILE) {
wp->facilities &= ~(FACIL_BUS_STOP | FACIL_TRUCK_STOP);
SetWindowWidgetDirty(WC_STATION_VIEW, wp->index, WID_SV_ROADVEHS);
wp->UpdateVirtCoord();
DeleteStationIfEmpty(wp);
}
}
}
return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(PR_CLEAR_STATION_TRUCK) : _price[PR_CLEAR_STATION_TRUCK]);
}
/**
* Remove a tile area of road stop or road waypoints
* @param flags operation to perform
* @param roadstop_area tile area of road stop or road waypoint tiles to remove
* @param station_type station type to remove
* @param remove_road Remove roads of drive-through stops?
* @return the cost of this operation or an error
*/
static CommandCost RemoveGenericRoadStop(DoCommandFlag flags, const TileArea &roadstop_area, StationType station_type, bool remove_road)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
bool had_success = false;
for (TileIndex cur_tile : roadstop_area) {
/* Make sure the specified tile is a road stop of the correct type */
if (!IsTileType(cur_tile, MP_STATION) || !IsAnyRoadStop(cur_tile) || GetStationType(cur_tile) != station_type) continue;
/* Save information on to-be-restored roads before the stop is removed. */
RoadBits road_bits = ROAD_NONE;
RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
if (IsDriveThroughStopTile(cur_tile)) {
for (RoadTramType rtt : _roadtramtypes) {
road_type[rtt] = GetRoadType(cur_tile, rtt);
if (road_type[rtt] == INVALID_ROADTYPE) continue;
road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
/* If we don't want to preserve our roads then restore only roads of others. */
if (remove_road && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
}
road_bits = AxisToRoadBits(DiagDirToAxis(GetRoadStopDir(cur_tile)));
}
CommandCost ret;
if (station_type == STATION_ROADWAYPOINT) {
ret = RemoveRoadWaypointStop(cur_tile, flags);
} else {
ret = RemoveRoadStop(cur_tile, flags);
}
if (ret.Failed()) {
last_error = ret;
continue;
}
cost.AddCost(ret);
had_success = true;
/* Restore roads. */
if ((flags & DC_EXEC) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
/* Update company infrastructure counts. */
int count = CountBits(road_bits);
UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_TRAM], count);
}
}
return had_success ? cost : last_error;
}
/**
* Remove bus or truck stops.
* @param flags Operation to perform.
@ -2241,50 +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<RoadStopDrawMode>(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<CMD_REMOVE_ROAD_STOP>::Do(DC_EXEC | DC_BANKRUPT, tile, 1, 1, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, false);
if (IsRoadWaypoint(tile)) {
Command<CMD_REMOVE_FROM_ROAD_WAYPOINT>::Do(DC_EXEC | DC_BANKRUPT, tile, tile);
} else {
Command<CMD_REMOVE_ROAD_STOP>::Do(DC_EXEC | DC_BANKRUPT, tile, 1, 1, (GetStationType(tile) == STATION_TRUCK) ? ROADSTOP_TRUCK : ROADSTOP_BUS, false);
}
assert(IsTileType(tile, MP_ROAD));
/* Change owner of tile and all roadtypes */
ChangeTileOwner(tile, old_owner, new_owner);
@ -4510,6 +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;

View File

@ -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 <bool ROAD, TileType TILE_TYPE>
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<false, MP_RAILWAY>;
using RoadWaypointTypeFilter = GenericWaypointTypeFilter<true, MP_ROAD>;
/**
* 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<T>(nullptr, true);
SetViewportCatchmentSpecializedStation<typename T::StationType>(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<T>(st, true);
typename T::StationType *st = T::StationType::GetByTile(location.tile);
if (st != nullptr && T::IsValidBaseStation(st)) {
SetViewportCatchmentSpecializedStation<typename T::StationType>(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<int>(0, x - max_c), std::max<int>(0, y - max_c)), TileXY(std::min<int>(Map::MaxX(), x + location.w + max_c), std::min<int>(Map::MaxY(), y + location.h + max_c)));
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<T>(adjacent, true);
SetViewportCatchmentSpecializedStation<typename T::StationType>(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<Station>();
FindStationsAroundSelection<StationTypeFilter>();
}
}
}
void CheckRedrawWaypointCoverage(const Window *)
template <typename T>
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<Waypoint>();
FindStationsAroundSelection<T>();
}
}
}
void CheckRedrawRailWaypointCoverage(const Window *)
{
CheckRedrawWaypointCoverage<RailWaypointTypeFilter>();
}
void CheckRedrawRoadWaypointCoverage(const Window *)
{
CheckRedrawWaypointCoverage<RoadWaypointTypeFilter>();
}
/**
* Draw small boxes of cargo amount and ratings data at the given
* coordinates. If amount exceeds 576 units, it is shown 'full', same
@ -2172,7 +2206,7 @@ static std::vector<StationID> _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 <class T>
static bool AddNearbyStation(TileIndex tile, void *user_data)
@ -2197,7 +2231,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()) {
@ -2214,10 +2248,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 <class T>
static const T *FindStationsNearby(TileArea ta, bool distant_join)
static const BaseStation *FindStationsNearby(TileArea ta, bool distant_join)
{
TileArea ctx = ta;
@ -2227,12 +2261,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});
@ -2275,7 +2309,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 <class T>
struct SelectStationWindow : Window {
@ -2290,7 +2324,7 @@ struct SelectStationWindow : Window {
{
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_JS_SCROLLBAR);
this->GetWidget<NWidgetCore>(WID_JS_CAPTION)->widget_data = T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
this->GetWidget<NWidgetCore>(WID_JS_CAPTION)->widget_data = T::IsWaypoint() ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
this->FinishInitNested(0);
this->OnInvalidateData(0);
@ -2299,7 +2333,7 @@ struct SelectStationWindow : Window {
void Close([[maybe_unused]] int data = 0) override
{
SetViewportCatchmentSpecializedStation<T>(nullptr, true);
SetViewportCatchmentSpecializedStation<typename T::StationType>(nullptr, true);
_thd.freeze = false;
this->Window::Close();
@ -2310,13 +2344,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;
@ -2334,12 +2368,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);
}
}
@ -2388,14 +2422,14 @@ struct SelectStationWindow : Window {
void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override
{
if (widget != WID_JS_PANEL) {
SetViewportCatchmentSpecializedStation<T>(nullptr, true);
SetViewportCatchmentSpecializedStation<typename T::StationType>(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<T>(st, true);
const typename T::StationType *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : T::StationType::Get(*it);
SetViewportCatchmentSpecializedStation<typename T::StationType>(st, true);
}
};
@ -2411,7 +2445,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 <class T>
@ -2438,7 +2472,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<T>(ta, false);
const BaseStation *st = FindStationsNearby<T>(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; }));
}
@ -2466,15 +2500,25 @@ void ShowSelectBaseStationIfNeeded(TileArea ta, StationPickerCmdProc&& proc)
*/
void ShowSelectStationIfNeeded(TileArea ta, StationPickerCmdProc proc)
{
ShowSelectBaseStationIfNeeded<Station>(ta, std::move(proc));
ShowSelectBaseStationIfNeeded<StationTypeFilter>(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<Waypoint>(ta, std::move(proc));
ShowSelectBaseStationIfNeeded<RailWaypointTypeFilter>(ta, std::move(proc));
}
/**
* Show the road waypoint selection window when needed. If not, build the waypoint.
* @param ta Area to build the waypoint in
* @param proc Function called to execute the build command.
*/
void ShowSelectRoadWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc)
{
ShowSelectBaseStationIfNeeded<RoadWaypointTypeFilter>(ta, std::move(proc));
}

View File

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

View File

@ -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);

View File

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

View File

@ -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

View File

@ -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,
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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