diff --git a/docs/landscape.html b/docs/landscape.html
index deea8ab206..1cc907f967 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -146,10 +146,11 @@ m5 bits 7 and 6 set: railway depot / checkpoints
m1: owner of the depot / checkpoint
+m2: For waypoints, index into the array of waypoints.
m3 bits 0..3 = track type
m3 bit 4 = use custom sprite (valid only for the checkpoint)
m3 bit 6 = track on this tile is reserved by pbs
-m4 = custom station id
+m4 bits 0..3 = ground type, as per m2 bits 0..3 for railway tiles.
diff --git a/openttd.c b/openttd.c
index 97a918a3c5..e63685382b 100644
--- a/openttd.c
+++ b/openttd.c
@@ -1287,6 +1287,43 @@ bool AfterLoadGame(uint version)
}
}
+ /* In version 17, ground type is moved from m2 to m4 for depots and
+ * waypoints to make way for storing the index in m2. The custom graphics
+ * id which was stored in m4 is now saved as a grf/id reference in the
+ * waypoint struct. */
+ if (version < 0x1100) {
+ Waypoint *wp;
+
+ FOR_ALL_WAYPOINTS(wp) {
+ if (wp->xy != 0 && wp->deleted == 0) {
+ const StationSpec *spec = NULL;
+
+ if (HASBIT(_m[wp->xy].m3, 4))
+ spec = GetCustomStation(STAT_CLASS_WAYP, _m[wp->xy].m4 + 1);
+
+ if (spec != NULL) {
+ wp->stat_id = _m[wp->xy].m4 + 1;
+ wp->grfid = spec->grfid;
+ wp->localidx = spec->localidx;
+ } else {
+ // No custom graphics set, so set to default.
+ wp->stat_id = 0;
+ wp->grfid = 0;
+ wp->localidx = 0;
+ }
+
+ // Move ground type bits from m2 to m4.
+ _m[wp->xy].m4 = GB(_m[wp->xy].m2, 0, 4);
+ // Store waypoint index in the tile.
+ _m[wp->xy].m2 = wp->index;
+ }
+ }
+ } else {
+ /* As of version 17, we recalculate the custom graphic ID of waypoints
+ * from the GRF ID / station index. */
+ UpdateAllWaypointCustomGraphics();
+ }
+
FOR_ALL_PLAYERS(p) p->avail_railtypes = GetPlayerRailtypes(p->index);
return true;
diff --git a/rail_cmd.c b/rail_cmd.c
index 18e0a7481b..7a7bd68c8a 100644
--- a/rail_cmd.c
+++ b/rail_cmd.c
@@ -1493,7 +1493,8 @@ static void DrawTile_Track(TileInfo *ti)
if (IsRailWaypoint(ti->tile) && HASBIT(_m[ti->tile].m3, 4)) {
// look for customization
- const StationSpec *stat = GetCustomStation(STAT_CLASS_WAYP, _m[ti->tile].m4 + 1);
+ byte stat_id = GetWaypointByTile(ti->tile)->stat_id;
+ const StationSpec *stat = GetCustomStation(STAT_CLASS_WAYP, stat_id);
if (stat != NULL) {
DrawTileSeqStruct const *seq;
@@ -1535,7 +1536,7 @@ static void DrawTile_Track(TileInfo *ti)
// adjust ground tile for desert
// (don't adjust for arctic depots, because snow in depots looks weird)
// type >= 4 means waypoints
- if ((_m[ti->tile].m2 & RAIL_MAP2LO_GROUND_MASK) == RAIL_GROUND_ICE_DESERT && (_opt.landscape == LT_DESERT || type >= 4)) {
+ if ((_m[ti->tile].m4 & RAIL_MAP2LO_GROUND_MASK) == RAIL_GROUND_ICE_DESERT && (_opt.landscape == LT_DESERT || type >= 4)) {
if (image != SPR_FLAT_GRASS_TILE) {
image += rti->snow_offset; // tile with tracks
} else {
@@ -1974,7 +1975,7 @@ static void TileLoop_Track(TileIndex tile)
byte new_ground;
TrackBits rail;
- old_ground = GB(_m[tile].m2, 0, 4);
+ old_ground = _m[tile].m5 & RAIL_TYPE_SPECIAL ? GB(_m[tile].m4, 0, 4) : GB(_m[tile].m2, 0, 4);
switch (_opt.landscape) {
case LT_HILLY:
@@ -2045,7 +2046,11 @@ static void TileLoop_Track(TileIndex tile)
modify_me:;
/* tile changed? */
if (old_ground != new_ground) {
- SB(_m[tile].m2, 0, 4, new_ground);
+ if (_m[tile].m5 & RAIL_TYPE_SPECIAL) {
+ SB(_m[tile].m4, 0, 4, new_ground);
+ } else {
+ SB(_m[tile].m2, 0, 4, new_ground);
+ }
MarkTileDirtyByTile(tile);
}
}
diff --git a/saveload.c b/saveload.c
index 05bfb44753..7b3a00b80d 100644
--- a/saveload.c
+++ b/saveload.c
@@ -29,8 +29,8 @@
#include
enum {
- SAVEGAME_MAJOR_VERSION = 16,
- SAVEGAME_MINOR_VERSION = 1,
+ SAVEGAME_MAJOR_VERSION = 17,
+ SAVEGAME_MINOR_VERSION = 0,
SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION
};
diff --git a/waypoint.c b/waypoint.c
index 60b6c87354..53844a6096 100644
--- a/waypoint.c
+++ b/waypoint.c
@@ -63,19 +63,6 @@ Waypoint *AllocateWaypoint(void)
return NULL;
}
-/* Fetch a waypoint by tile */
-Waypoint *GetWaypointByTile(TileIndex tile)
-{
- Waypoint *wp;
-
- FOR_ALL_WAYPOINTS(wp) {
- if (wp->xy == tile)
- return wp;
- }
-
- return NULL;
-}
-
/* Update the sign for the waypoint */
void UpdateWaypointSign(Waypoint *wp)
{
@@ -151,6 +138,29 @@ static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile)
return best;
}
+/**
+ * Update waypoint graphics id against saved GRFID/localidx.
+ * This is to ensure the chosen graphics are correct if GRF files are changed.
+ */
+void UpdateAllWaypointCustomGraphics(void)
+{
+ Waypoint *wp;
+
+ FOR_ALL_WAYPOINTS(wp) {
+ uint i;
+
+ if (wp->grfid == 0) continue;
+
+ for (i = 0; i < GetNumCustomStations(STAT_CLASS_WAYP); i++) {
+ const StationSpec *spec = GetCustomStation(STAT_CLASS_WAYP, i);
+ if (spec != NULL && spec->grfid == wp->grfid && spec->localidx == wp->localidx) {
+ wp->stat_id = i;
+ break;
+ }
+ }
+ }
+}
+
/** Convert existing rail to waypoint. Eg build a waypoint station over
* piece of rail
* @param x,y coordinates where waypoint will be built
@@ -198,13 +208,26 @@ int32 CmdBuildTrainWaypoint(int x, int y, uint32 flags, uint32 p1, uint32 p2)
}
if (flags & DC_EXEC) {
+ const StationSpec *spec = NULL;
bool reserved = PBSTileReserved(tile) != 0;
- ModifyTile(tile, MP_MAP5, RAIL_TYPE_WAYPOINT | dir);
- if (p1 > 0) { // waypoint type 0 uses default graphics
- // custom graphics
- _m[tile].m3 |= 16;
- _m[tile].m4 = (p1 - 1) & 0xff;
+ ModifyTile(tile, MP_MAP2 | MP_MAP5, wp->index, RAIL_TYPE_WAYPOINT | dir);
+
+ if (GB(p1, 0, 8) < GetNumCustomStations(STAT_CLASS_WAYP))
+ spec = GetCustomStation(STAT_CLASS_WAYP, GB(p1, 0, 8));
+
+ if (spec != NULL) {
+ SETBIT(_m[tile].m3, 4);
+ wp->stat_id = GB(p1, 0, 8);
+ wp->grfid = spec->grfid;
+ wp->localidx = spec->localidx;
+ } else {
+ // Specified custom graphics do not exist, so use default.
+ CLRBIT(_m[tile].m3, 4);
+ wp->stat_id = 0;
+ wp->grfid = 0;
+ wp->localidx = 0;
}
+
if (reserved) {
PBSReserveTrack(tile, dir);
} else {
@@ -280,8 +303,8 @@ int32 RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove)
if (justremove) {
bool reserved = PBSTileReserved(tile) != 0;
- ModifyTile(tile, MP_MAP5, 1<