Add rail ground type support for rail custom bridge heads

See: #146
pull/174/head
Jonathan G Rennison 4 years ago
parent 1f5322de20
commit ea06a0670d

@ -1757,7 +1757,49 @@
</table>
</li>
<li>m7 bits 4..0: <a href="#OwnershipInfo">owner</a> of road</li>
<li>m7 bit 5 set = on snow or desert</li>
<li style="color: blue">m7 bits 7..5: ground type, values greater than 1 are only valid for rail custom bridge heads
<table>
<tr>
<td nowrap valign=top><tt>0</tt>&nbsp; </td>
<td align=left colspan=4>on grass, no fences</td>
</tr>
<tr>
<td nowrap valign=top><tt>1</tt>&nbsp; </td>
<td align=left>on snow or desert</td>
</tr>
<tr style="color: blue">
<td nowrap valign=top><tt>2</tt>&nbsp; </td>
<td align=left>on bare land</td>
</tr>
<tr style="color: blue">
<td nowrap valign=top><tt>3</tt>&nbsp; </td>
<td align=left>facing NE, SE, SW: fence on the NW side, facing NW: fence on the SE side</td>
</tr>
<tr style="color: blue">
<td nowrap valign=top><tt>4</tt>&nbsp; </td>
<td align=left>facing NE, SW: fence on the SE side, facing SE, NW: fence on the NE side</td>
</tr>
<tr style="color: blue">
<td nowrap valign=top><tt>5</tt>&nbsp; </td>
<td align=left>facing NE, SE, NW: fence on the SW side, facing SW: fence on the NE side</td>
</tr>
<tr style="color: blue">
<td nowrap valign=top><tt>6</tt>&nbsp; </td>
<td align=left>facing NE, SE: fence on the W side (track in the E corner), facing: SW, NW: fence on the E side (track in the W corner)</td>
</tr>
<tr style="color: blue">
<td nowrap valign=top><tt>7</tt>&nbsp; </td>
<td align=left>facing NE, NW: fence on the S side (track in the N corner), facing: SE, SW: fence on the N side (track in the S corner)</td>
</tr>
</table>
</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</li>
<li style="color: blue">m8 bits 11..6 = <a name="TrackType">secondary track type</a> for railway (used for bridge-bypassing track when two parallel tracks on custom bridge head)</li>

@ -362,7 +362,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="free">OO</span>XX XXXX</td>
<td class="bits">X<span class="free">OO</span>X XXXX</td>
<td class="bits"><span class="used_p">PP</span><span class="free">OO OO</span><span class="used_p">PP</span></td>
<td class="bits"><span class="free">OO</span>XX XXXX</td>
<td class="bits"><span class="used_p">PP</span>XX XXXX</td>
<td class="bits"><span class="free">OOOO</span> XXXX XXXX XXXX</td>
</tr>
<tr>

@ -651,6 +651,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (flags & DC_EXEC) {
SubtractRailTunnelBridgeInfrastructure(tile, other_end);
SetCustomBridgeHeadTrackBits(tile, future);
SetTunnelBridgeGroundBits(tile, IsRailCustomBridgeHead(tile) ? 2 : 0);
if (secondary_piece) {
SetSecondaryRailType(tile, railtype);
}
@ -939,6 +940,7 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
}
SetCustomBridgeHeadTrackBits(tile, future);
SetTunnelBridgeGroundBits(tile, IsRailCustomBridgeHead(tile) ? 2 : 0);
AddRailTunnelBridgeInfrastructure(tile, other_end);
DirtyCompanyInfrastructureWindows(_current_company);
}
@ -2628,7 +2630,7 @@ static void DrawTrackFence_SW(const TileInfo *ti, SpriteID base_image, uint num_
* @param ti Tile drawing information.
* @param rti Rail type information.
*/
static void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti)
void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti, const RailGroundType rgt)
{
/* Base sprite for track fences.
* Note: Halftile slopes only have fences on the upper part. */
@ -2641,7 +2643,7 @@ static void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti)
assert(num_sprites > 0);
switch (GetRailGroundType(ti->tile)) {
switch (rgt) {
case RAIL_GROUND_FENCE_NW: DrawTrackFence_NW(ti, base_image, num_sprites); break;
case RAIL_GROUND_FENCE_SE: DrawTrackFence_SE(ti, base_image, num_sprites); break;
case RAIL_GROUND_FENCE_SENW: DrawTrackFence_NW(ti, base_image, num_sprites);
@ -2698,7 +2700,7 @@ static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInf
static RailGroundType GetRailOrBridgeGroundType(TileInfo *ti) {
if (IsTileType(ti->tile, MP_TUNNELBRIDGE)) {
return HasTunnelBridgeSnowOrDesert(ti->tile) ? RAIL_GROUND_ICE_DESERT : RAIL_GROUND_GRASS;
return GetTunnelBridgeGroundType(ti->tile);
} else {
return GetRailGroundType(ti->tile);
}
@ -3115,7 +3117,7 @@ static void DrawTile_Track(TileInfo *ti, DrawTileProcParams params)
DrawTrackBits(ti, rails);
if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti);
if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti, GetRailGroundType(ti->tile));
if (HasRailCatenaryDrawn(GetRailType(ti->tile), GetTileSecondaryRailTypeIfValid(ti->tile))) DrawRailCatenary(ti);
@ -3271,6 +3273,44 @@ static Foundation GetFoundation_Track(TileIndex tile, Slope tileh)
return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh);
}
RailGroundType RailTrackToFence(TileIndex tile, TrackBits rail)
{
Owner owner = GetTileOwner(tile);
byte fences = 0;
for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
static const TrackBits dir_to_trackbits[DIAGDIR_END] = {TRACK_BIT_3WAY_NE, TRACK_BIT_3WAY_SE, TRACK_BIT_3WAY_SW, TRACK_BIT_3WAY_NW};
/* Track bit on this edge => no fence. */
if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue;
TileIndex tile2 = tile + TileOffsByDiagDir(d);
/* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
if (!IsValidTile(tile2) || IsTileType(tile2, MP_HOUSE) || IsTileType(tile2, MP_INDUSTRY) ||
IsTileType(tile2, MP_ROAD) || (IsTileType(tile2, MP_OBJECT) && !IsObjectType(tile2, OBJECT_OWNED_LAND)) || IsTileType(tile2, MP_TUNNELBRIDGE) || !IsTileOwner(tile2, owner)) {
fences |= 1 << d;
}
}
RailGroundType new_ground;
switch (fences) {
case 0: new_ground = RAIL_GROUND_GRASS; break;
case (1 << DIAGDIR_NE): new_ground = RAIL_GROUND_FENCE_NE; break;
case (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_SE; break;
case (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_SW; break;
case (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_NW; break;
case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_NESW; break;
case (1 << DIAGDIR_SE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_SENW; break;
case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_VERT1; break;
case (1 << DIAGDIR_NE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
case (1 << DIAGDIR_SE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
case (1 << DIAGDIR_SW) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_VERT2; break;
default: NOT_REACHED();
}
return new_ground;
}
static void TileLoop_Track(TileIndex tile)
{
RailGroundType old_ground = GetRailGroundType(tile);
@ -3352,39 +3392,7 @@ static void TileLoop_Track(TileIndex tile)
if (IsPlainRail(tile) && old_ground != RAIL_GROUND_BARREN) { // wait until bottom is green
/* determine direction of fence */
TrackBits rail = GetTrackBits(tile);
Owner owner = GetTileOwner(tile);
byte fences = 0;
for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
static const TrackBits dir_to_trackbits[DIAGDIR_END] = {TRACK_BIT_3WAY_NE, TRACK_BIT_3WAY_SE, TRACK_BIT_3WAY_SW, TRACK_BIT_3WAY_NW};
/* Track bit on this edge => no fence. */
if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue;
TileIndex tile2 = tile + TileOffsByDiagDir(d);
/* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
if (!IsValidTile(tile2) || IsTileType(tile2, MP_HOUSE) || IsTileType(tile2, MP_INDUSTRY) ||
IsTileType(tile2, MP_ROAD) || (IsTileType(tile2, MP_OBJECT) && !IsObjectType(tile2, OBJECT_OWNED_LAND)) || IsTileType(tile2, MP_TUNNELBRIDGE) || !IsTileOwner(tile2, owner)) {
fences |= 1 << d;
}
}
switch (fences) {
case 0: break;
case (1 << DIAGDIR_NE): new_ground = RAIL_GROUND_FENCE_NE; break;
case (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_SE; break;
case (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_SW; break;
case (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_NW; break;
case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_NESW; break;
case (1 << DIAGDIR_SE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_SENW; break;
case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_VERT1; break;
case (1 << DIAGDIR_NE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
case (1 << DIAGDIR_SE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
case (1 << DIAGDIR_SW) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_VERT2; break;
default: NOT_REACHED();
}
new_ground = RailTrackToFence(tile, rail);
}
set_ground:

@ -588,6 +588,7 @@ static inline bool IsSnowRailGround(TileIndex t)
return GetRailGroundType(t) == RAIL_GROUND_ICE_DESERT;
}
RailGroundType GetTunnelBridgeGroundType(TileIndex tile);
static inline void MakeRailNormal(TileIndex t, Owner o, TrackBits b, RailType r)
{

@ -1523,6 +1523,13 @@ bool AfterLoadGame()
}
}
if (!SlXvIsFeaturePresent(XSLFI_CUSTOM_BRIDGE_HEADS, 3)) {
/* fence/ground type support for custom rail bridges */
for (TileIndex t = 0; t < map_size; t++) {
if (IsTileType(t, MP_TUNNELBRIDGE)) SB(_me[t].m7, 6, 2, 0);
}
}
/* Elrails got added in rev 24 */
if (IsSavegameVersionBefore(SLV_24)) {
RailType min_rail = RAILTYPE_ELECTRIC;

@ -87,7 +87,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_CARGO_TYPE_ORDERS, XSCF_NULL, 3, 3, "cargo_type_orders", nullptr, nullptr, "ORDX,VEOX" },
{ XSLFI_EXTENDED_GAMELOG, XSCF_NULL, 1, 1, "extended_gamelog", nullptr, nullptr, nullptr },
{ XSLFI_STATION_CATCHMENT_INC, XSCF_NULL, 1, 1, "station_catchment_inc", nullptr, nullptr, nullptr },
{ XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 2, 2, "custom_bridge_heads", nullptr, nullptr, nullptr },
{ XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 3, 3, "custom_bridge_heads", nullptr, nullptr, nullptr },
{ XSLFI_CHUNNEL, XSCF_NULL, 2, 2, "chunnel", nullptr, nullptr, "TUNN" },
{ XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 2, 2, "scheduled_dispatch", nullptr, nullptr, nullptr },
{ XSLFI_MORE_TOWN_GROWTH_RATES, XSCF_NULL, 1, 1, "more_town_growth_rates", nullptr, nullptr, nullptr },

@ -1805,7 +1805,12 @@ static void DrawTile_TunnelBridge(TileInfo *ti, DrawTileProcParams params)
return;
}
if (transport_type == TRANSPORT_RAIL && IsRailCustomBridgeHead(ti->tile)) {
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
DrawTrackBits(ti, GetCustomBridgeHeadTrackBits(ti->tile));
if (HasBit(_display_opt, DO_FULL_DETAIL)) {
extern void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti, const RailGroundType rgt);
DrawTrackDetails(ti, rti, GetTunnelBridgeGroundType(ti->tile));
}
if (HasRailCatenaryDrawn(GetRailType(ti->tile), GetTileSecondaryRailTypeIfValid(ti->tile))) {
DrawRailCatenary(ti);
}
@ -1816,7 +1821,6 @@ static void DrawTile_TunnelBridge(TileInfo *ti, DrawTileProcParams params)
DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC;
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
Track t = FindFirstTrack(GetAcrossTunnelBridgeTrackBits(ti->tile));
auto draw_signals = [&](uint position, SignalOffsets image, DiagDirection towards) {
@ -2293,33 +2297,133 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td)
}
}
static const RailGroundType _tunnel_bridge_fence_table[4][5] = {
{ // DIAGDIR_NE
RAIL_GROUND_FENCE_NW,
RAIL_GROUND_FENCE_SE,
RAIL_GROUND_FENCE_SW,
RAIL_GROUND_FENCE_VERT2,
RAIL_GROUND_FENCE_HORIZ1,
},
{ // DIAGDIR_SE
RAIL_GROUND_FENCE_NW,
RAIL_GROUND_FENCE_NE,
RAIL_GROUND_FENCE_SW,
RAIL_GROUND_FENCE_VERT2,
RAIL_GROUND_FENCE_HORIZ2,
},
{ // DIAGDIR_SW
RAIL_GROUND_FENCE_NW,
RAIL_GROUND_FENCE_SE,
RAIL_GROUND_FENCE_NE,
RAIL_GROUND_FENCE_VERT1,
RAIL_GROUND_FENCE_HORIZ2,
},
{ // DIAGDIR_NW
RAIL_GROUND_FENCE_SE,
RAIL_GROUND_FENCE_NE,
RAIL_GROUND_FENCE_SW,
RAIL_GROUND_FENCE_VERT1,
RAIL_GROUND_FENCE_HORIZ1,
},
};
RailGroundType GetTunnelBridgeGroundType(TileIndex tile)
{
uint8 ground_bits = GetTunnelBridgeGroundBits(tile);
if (ground_bits == 0) return RAIL_GROUND_GRASS;
if (ground_bits == 1) return RAIL_GROUND_ICE_DESERT;
if (ground_bits == 2) return RAIL_GROUND_BARREN;
return _tunnel_bridge_fence_table[GetTunnelBridgeDirection(tile)][ground_bits - 3];
}
static uint8 MapTunnelBridgeGroundTypeBits(TileIndex tile, RailGroundType type)
{
uint8 ground_bits;
switch (type) {
case RAIL_GROUND_BARREN:
ground_bits = 2;
break;
case RAIL_GROUND_GRASS:
ground_bits = 0;
break;
case RAIL_GROUND_FENCE_NW:
ground_bits = 3;
break;
case RAIL_GROUND_FENCE_SE:
ground_bits = GetTunnelBridgeDirection(tile) == DIAGDIR_NW ? 3 : 4;
break;
case RAIL_GROUND_FENCE_NE:
ground_bits = GetTunnelBridgeDirection(tile) == DIAGDIR_SW ? 5 : 4;
break;
case RAIL_GROUND_FENCE_SW:
ground_bits = 5;
break;
case RAIL_GROUND_FENCE_VERT1:
case RAIL_GROUND_FENCE_VERT2:
ground_bits = 6;
break;
case RAIL_GROUND_FENCE_HORIZ1:
case RAIL_GROUND_FENCE_HORIZ2:
ground_bits = 7;
break;
case RAIL_GROUND_ICE_DESERT:
ground_bits = 1;
break;
default:
NOT_REACHED();
}
return ground_bits;
}
static void TileLoop_TunnelBridge(TileIndex tile)
{
bool snow_or_desert = HasTunnelBridgeSnowOrDesert(tile);
const uint8 old_ground_bits = GetTunnelBridgeGroundBits(tile);
bool snow_or_desert = false;
switch (_settings_game.game_creation.landscape) {
case LT_ARCTIC: {
/* As long as we do not have a snow density, we want to use the density
* from the entry edge. For tunnels this is the lowest point for bridges the highest point.
* (Independent of foundations) */
int z = IsBridge(tile) ? GetTileMaxZ(tile) : GetTileZ(tile);
if (snow_or_desert != (z > GetSnowLine())) {
SetTunnelBridgeSnowOrDesert(tile, !snow_or_desert);
MarkTileDirtyByTile(tile);
}
snow_or_desert = (z > GetSnowLine());
break;
}
case LT_TROPIC:
if (GetTropicZone(tile) == TROPICZONE_DESERT && !snow_or_desert) {
SetTunnelBridgeSnowOrDesert(tile, true);
MarkTileDirtyByTile(tile);
}
snow_or_desert = (GetTropicZone(tile) == TROPICZONE_DESERT);
break;
default:
break;
}
RailGroundType new_ground;
if (snow_or_desert) {
new_ground = RAIL_GROUND_ICE_DESERT;
} else {
new_ground = RAIL_GROUND_GRASS;
if (IsRailCustomBridgeHeadTile(tile) && old_ground_bits != 2) { // wait until bottom is green
/* determine direction of fence */
TrackBits rail = GetCustomBridgeHeadTrackBits(tile);
extern RailGroundType RailTrackToFence(TileIndex tile, TrackBits rail);
new_ground = RailTrackToFence(tile, rail);
}
}
uint8 ground_bits = MapTunnelBridgeGroundTypeBits(tile, new_ground);
if (ground_bits != old_ground_bits) {
SetTunnelBridgeGroundBits(tile, ground_bits);
MarkTileDirtyByTile(tile);
}
}
static bool ClickTile_TunnelBridge(TileIndex tile)

@ -47,6 +47,12 @@ static inline TransportType GetTunnelBridgeTransportType(TileIndex t)
return (TransportType)GB(_m[t].m5, 2, 2);
}
static inline uint8 GetTunnelBridgeGroundBits(TileIndex t)
{
assert_tile(IsTileType(t, MP_TUNNELBRIDGE), t);
return GB(_me[t].m7, 5, 3);
}
/**
* Tunnel: Is this tunnel entrance in a snowy or desert area?
* Bridge: Does the bridge ramp lie in a snow or desert area?
@ -56,11 +62,9 @@ static inline TransportType GetTunnelBridgeTransportType(TileIndex t)
*/
static inline bool HasTunnelBridgeSnowOrDesert(TileIndex t)
{
assert_tile(IsTileType(t, MP_TUNNELBRIDGE), t);
return HasBit(_me[t].m7, 5);
return GetTunnelBridgeGroundBits(t) == 1;
}
/**
* Is this a rail bridge or tunnel?
* @param t the tile that might be a rail bridge or tunnel
@ -73,6 +77,12 @@ static inline bool IsRailTunnelBridgeTile(TileIndex t)
return IsTileType(t, MP_TUNNELBRIDGE) && (tt == TRANSPORT_RAIL);
}
static inline void SetTunnelBridgeGroundBits(TileIndex t, uint8 bits)
{
assert_tile(IsTileType(t, MP_TUNNELBRIDGE), t);
SB(_me[t].m7, 5, 3, bits);
}
/**
* Tunnel: Places this tunnel entrance in a snowy or desert area, or takes it out of there.
* Bridge: Sets whether the bridge ramp lies in a snow or desert area.
@ -83,8 +93,7 @@ static inline bool IsRailTunnelBridgeTile(TileIndex t)
*/
static inline void SetTunnelBridgeSnowOrDesert(TileIndex t, bool snow_or_desert)
{
assert_tile(IsTileType(t, MP_TUNNELBRIDGE), t);
SB(_me[t].m7, 5, 1, snow_or_desert);
SetTunnelBridgeGroundBits(t, snow_or_desert ? 1 : 0);
}
/**

Loading…
Cancel
Save