summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/landscape.html2
-rw-r--r--engine.h6
-rw-r--r--grfspecial.c377
-rw-r--r--rail_cmd.c53
-rw-r--r--rail_gui.c7
-rw-r--r--station.h4
-rw-r--r--station_cmd.c32
7 files changed, 383 insertions, 98 deletions
diff --git a/docs/landscape.html b/docs/landscape.html
index 512cab60c..18ec83780 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -135,6 +135,8 @@ map5 bits 7 and 6 set: railway depot / checkpoints
<br>
<li>map_owner: <a href="#OwnershipInfo">owner</a> of the depot / checkpoint</li>
<li>map3_lo bits 0..3 = <a href="#TrackType">track type</a></li>
+<li>map3_lo bit 4 = use custom sprite (valid only for the checkpoint)</li>
+<li>map3_hi = custom station id</li>
</ul>
</td></tr>
diff --git a/engine.h b/engine.h
index 928e5fcd7..757d5f85a 100644
--- a/engine.h
+++ b/engine.h
@@ -84,8 +84,14 @@ struct SpriteGroup {
// XXX: Would anyone ever need more than 16 spritesets? Maybe we should
// use even less, now we take whole 8kb for custom sprites table, oh my!
byte sprites_per_set; // means number of directions - 4 or 8
+
// Loaded = in motion, loading = not moving
// Each group contains several spritesets, for various loading stages
+
+ // XXX: For stations the meaning is different - loaded is for stations
+ // with small amount of cargo whilst loading is for stations with a lot
+ // of da stuff.
+
byte loaded_count;
uint16 loaded[16]; // sprite ids
byte loading_count;
diff --git a/grfspecial.c b/grfspecial.c
index 812d414c2..cf5d73fbb 100644
--- a/grfspecial.c
+++ b/grfspecial.c
@@ -6,6 +6,7 @@
#include "gfx.h"
#include "fileio.h"
#include "engine.h"
+#include "station.h"
/* TTDPatch extended GRF format codec
* (c) Petr Baudis 2004 (GPL'd)
@@ -26,6 +27,29 @@ struct GRFFile {
uint16 flags;
uint16 sprite_offset;
struct GRFFile *next;
+
+ /* A sprite group contains all sprites of a given vehicle (or multiple
+ * vehicles) when carrying given cargo. It consists of several sprite
+ * sets. Group ids are refered as "cargo id"s by TTDPatch
+ * documentation, contributing to the global confusion.
+ *
+ * A sprite set contains all sprites of a given vehicle carrying given
+ * cargo at a given *stage* - that is usually its load stage. Ie. you
+ * can have a spriteset for an empty wagon, wagon full of coal,
+ * half-filled wagon etc. Each spriteset contains eight sprites (one
+ * per direction) or four sprites if the vehicle is symmetric. */
+
+ int spriteset_start;
+ int spriteset_numsets;
+ int spriteset_numents;
+ int spriteset_feature;
+
+ int spritesset_count;
+ struct SpriteGroup *spritesset;
+
+ uint32 statinfo_classid[256];
+ byte statinfo_tiles[256];
+ DrawTileSprites statinfo_renderdata[256][8];
};
static struct GRFFile *_cur_grffile, *_first_grffile;
@@ -664,8 +688,178 @@ static bool AircraftVehicleChangeInfo(uint engine, int numinfo, int prop, byte *
return ret;
}
+static bool StationChangeInfo(uint stid, int numinfo, int prop, byte **bufp, int len)
+{
+ byte *buf = *bufp;
+ int i;
+ int ret = 0;
+
+ /* This is one single huge TODO. It doesn't handle anything more than
+ * just waypoints for now. */
+
+ //printf("sci %d %d [0x%02x]\n", stid, numinfo, prop);
+ switch (prop) {
+ case 0x08:
+ { /* Class ID */
+ FOR_EACH_ENGINE {
+ /* classid, for a change, is always little-endian */
+ _cur_grffile->statinfo_classid[stid+i] = *(buf++) << 24;
+ _cur_grffile->statinfo_classid[stid+i] |= *(buf++) << 16;
+ _cur_grffile->statinfo_classid[stid+i] |= *(buf++) << 8;
+ _cur_grffile->statinfo_classid[stid+i] |= *(buf++);
+ }
+ break;
+ }
+ case 0x09:
+ { /* Define sprite layout */
+ FOR_EACH_ENGINE {
+ int t;
+ _cur_grffile->statinfo_tiles[stid+i] = grf_load_byte(&buf);
+ for (t = 0; t < _cur_grffile->statinfo_tiles[stid+i]; t++) {
+ DrawTileSprites *dts = &_cur_grffile->statinfo_renderdata[stid+i][t];
+ int seq_count = 0;
+
+ if (t >= 8) {
+ grfmsg(GMS_WARN, "StationChangeInfo: Sprite %d>=8, skipping.", t);
+ grf_load_dword(&buf); // at least something
+ continue;
+ }
+
+ dts->ground_sprite = grf_load_dword(&buf);
+ if (!dts->ground_sprite) {
+ static const DrawTileSeqStruct empty = {0x80};
+ dts->seq = &empty;
+ continue;
+ }
+
+ dts->seq = NULL;
+ while (buf < *bufp + len) {
+ DrawTileSeqStruct *dtss;
+
+ // no relative bounding box support
+ dts->seq = realloc((void*)dts->seq, ++seq_count * sizeof(DrawTileSeqStruct));
+ dtss = (DrawTileSeqStruct*) &dts->seq[seq_count - 1];
+
+ dtss->delta_x = grf_load_byte(&buf);
+ if ((byte) dtss->delta_x == 0x80) break;
+ dtss->delta_y = grf_load_byte(&buf);
+ dtss->delta_z = grf_load_byte(&buf);
+ dtss->width = grf_load_byte(&buf);
+ dtss->height = grf_load_byte(&buf);
+ dtss->unk = grf_load_byte(&buf);
+ dtss->image = grf_load_dword(&buf) - 0x42d;
+ }
+ }
+ }
+ break;
+ }
+ case 0x0a:
+ { /* Copy sprite layout */
+ FOR_EACH_ENGINE {
+ byte src = grf_load_byte(&buf);
+ int t;
+ _cur_grffile->statinfo_tiles[stid+i] = _cur_grffile->statinfo_tiles[src];
+ for (t = 0; t < _cur_grffile->statinfo_tiles[stid+i]; t++) {
+ DrawTileSprites *dts = &_cur_grffile->statinfo_renderdata[stid+i][t];
+ DrawTileSprites *sdts = &_cur_grffile->statinfo_renderdata[src][t];
+ DrawTileSeqStruct const *sdtss = sdts->seq;
+ int seq_count = 0;
+
+ dts->ground_sprite = sdts->ground_sprite;
+ if (!dts->ground_sprite) {
+ static const DrawTileSeqStruct empty = {0x80};
+ dts->seq = &empty;
+ continue;
+ }
+
+ dts->seq = NULL;
+ while (1) {
+ DrawTileSeqStruct *dtss;
+
+ // no relative bounding box support
+ dts->seq = realloc((void*)dts->seq, ++seq_count * sizeof(DrawTileSeqStruct));
+ dtss = (DrawTileSeqStruct*) &dts->seq[seq_count - 1];
+ *dtss = *sdtss;
+ if ((byte) dtss->delta_x == 0x80) break;
+ sdtss++;
+ }
+ }
+ }
+ break;
+ }
+ case 0x0b:
+ { /* Callback */
+ /* TODO */
+ FOR_EACH_ENGINE {
+ grf_load_byte(&buf);
+ }
+ ret = 1;
+ break;
+ }
+ case 0x0c:
+ { /* Platforms number */
+ /* TODO */
+ FOR_EACH_ENGINE {
+ grf_load_byte(&buf);
+ }
+ ret = 1;
+ break;
+ }
+ case 0x0d:
+ { /* Platforms length */
+ /* TODO */
+ FOR_EACH_ENGINE {
+ grf_load_byte(&buf);
+ }
+ ret = 1;
+ break;
+ }
+ case 0x0e:
+ { /* Define custom layout */
+ /* TODO */
+ FOR_EACH_ENGINE {
+ while (buf < *bufp + len) {
+ byte length = grf_load_byte(&buf);
+ byte number = grf_load_byte(&buf);
+ int k = length * number;
+
+ if (!length && !number) break;
+ while (k--) grf_load_byte(&buf);
+ }
+ }
+ ret = 1;
+ break;
+ }
+ case 0x0f:
+ { /* Copy custom layout */
+ /* TODO */
+ FOR_EACH_ENGINE {
+ grf_load_byte(&buf);
+ }
+ ret = 1;
+ break;
+ }
+ case 0x10:
+ { /* Little/lots cargo threshold */
+ /* TODO */
+ FOR_EACH_ENGINE {
+ grf_load_word(&buf);
+ }
+ ret = 1;
+ break;
+ }
+ default:
+ ret = 1;
+ break;
+ }
+
+ *bufp = buf;
+ return ret;
+}
+
#undef shift_buf
+
/* Action 0x00 */
static void VehicleChangeInfo(byte *buf, int len)
{
@@ -690,7 +884,7 @@ static void VehicleChangeInfo(byte *buf, int len)
/* GSF_ROAD */ RoadVehicleChangeInfo,
/* GSF_SHIP */ ShipVehicleChangeInfo,
/* GSF_AIRCRAFT */ AircraftVehicleChangeInfo,
- /* GSF_STATION */ NULL,
+ /* GSF_STATION */ StationChangeInfo,
};
uint8 feature;
@@ -708,18 +902,18 @@ static void VehicleChangeInfo(byte *buf, int len)
DEBUG(grf, 6) ("VehicleChangeInfo: Feature %d, %d properties, to apply to %d+%d",
feature, numprops, engine, numinfo);
- if (feature == GSF_STATION) {
- grfmsg(GMS_WARN, "VehicleChangeInfo: Stations unsupported, skipping.");
- return;
- }
-
- ei = &_engine_info[engine + _vehshifts[feature]];
+ if (feature != GSF_STATION)
+ ei = &_engine_info[engine + _vehshifts[feature]];
buf += 5;
while (numprops-- && buf < bufend) {
uint8 prop = grf_load_byte(&buf);
+ if (feature == 4)
+ // stations don't share those common properties
+ goto run_handler;
+
switch (prop) {
case 0x00: { /* Introduction date */
FOR_EACH_ENGINE {
@@ -771,6 +965,7 @@ static void VehicleChangeInfo(byte *buf, int len)
goto ignoring;
}
default: {
+run_handler:
if (handler[feature](engine, numinfo, prop, &buf, bufend - buf)) {
ignoring:
grfmsg(GMS_NOTICE, "VehicleChangeInfo: Ignoring property %x (not implemented).", prop);
@@ -783,25 +978,6 @@ ignoring:
}
-/* A sprite group contains all sprites of a given vehicle (or multiple
- * vehicles) when carrying given cargo. It consists of several sprite sets.
- * Group ids are refered as "cargo id"s by TTDPatch documentation,
- * contributing to the global confusion.
- *
- * A sprite set contains all sprites of a given vehicle carrying given cargo at
- * a given *stage* - that is usually its load stage. Ie. you can have a
- * spriteset for an empty wagon, wagon full of coal, half-filled wagon etc.
- * Each spriteset contains eight sprites (one per direction) or four sprites if
- * the vehicle is symmetric. */
-
-static int _spriteset_start;
-static int _spriteset_numsets;
-static int _spriteset_numents;
-static int _spriteset_feature;
-
-static int _spritesset_count;
-static struct SpriteGroup *_spritesset;
-
/* Action 0x01 */
static void NewSpriteSet(byte *buf, int len)
{
@@ -823,16 +999,10 @@ static void NewSpriteSet(byte *buf, int len)
check_length(len, 4, "NewSpriteSet");
feature = buf[1];
- if (feature == GSF_STATION) {
- _spriteset_start = 0;
- grfmsg(GMS_WARN, "NewSpriteSet: Stations unsupported, skipping.");
- return;
- }
-
- _spriteset_start = _cur_spriteid + 1;
- _spriteset_feature = feature;
- _spriteset_numsets = buf[2];
- _spriteset_numents = buf[3];
+ _cur_grffile->spriteset_start = _cur_spriteid + 1;
+ _cur_grffile->spriteset_feature = feature;
+ _cur_grffile->spriteset_numsets = buf[2];
+ _cur_grffile->spriteset_numents = buf[3];
}
/* Action 0x02 */
@@ -850,12 +1020,13 @@ static void NewSpriteGroup(byte *buf, int len)
* otherwise it specifies a number of entries, the exact
* meaning depends on the feature
* V feature-specific-data (huge mess, don't even look it up --pasky) */
- /* TODO: Only trains supported now. No 0x80-types (ugh). */
+ /* TODO: No 0x80-types (ugh). */
/* TODO: Also, empty sprites aren't handled for now. Need to investigate
* the "opacity" rules for these, that is which sprite to fall back to
* when. --pasky */
uint8 feature;
uint8 setid;
+ /* XXX: For stations, these two are "little cargo" and "lotsa cargo" sets. */
uint8 numloaded;
uint8 numloading;
struct SpriteGroup *group;
@@ -869,19 +1040,9 @@ static void NewSpriteGroup(byte *buf, int len)
numloaded = buf[3];
numloading = buf[4];
- if (feature == GSF_STATION) {
- grfmsg(GMS_WARN, "NewSpriteGroup: Stations unsupported, skipping.");
- return;
- }
-
if (numloaded == 0x81) {
- // XXX: This is _VERY_ ad hoc just to handle Dm3. And that is
- // a semi-futile ask because the great Patchman himself says
- // this is just buggy. It dereferences last (first) byte of
- // a schedule list pointer of the vehicle and if it's 0xff
- // it uses group 01, otherwise it uses group 00. Now
- // if _you_ understand _that_... We just assume it is never
- // 0xff and therefore go for group 00. --pasky
+ /* XXX: This just goes for the default superset for now,
+ * straight and safe. --pasky */
uint8 var = buf[4];
//uint8 shiftnum = buf[5];
//uint8 andmask = buf[6];
@@ -893,14 +1054,13 @@ static void NewSpriteGroup(byte *buf, int len)
//val = (0xff << shiftnum) & andmask;
- //Go for the default.
- if (setid >= _spritesset_count) {
- _spritesset_count = setid + 1;
- _spritesset = realloc(_spritesset, _spritesset_count * sizeof(struct SpriteGroup));
+ if (setid >= _cur_grffile->spritesset_count) {
+ _cur_grffile->spritesset_count = setid + 1;
+ _cur_grffile->spritesset = realloc(_cur_grffile->spritesset, _cur_grffile->spritesset_count * sizeof(struct SpriteGroup));
}
buf += 8 + nvar * 4;
def = grf_load_word(&buf);
- _spritesset[setid] = _spritesset[def];
+ _cur_grffile->spritesset[setid] = _cur_grffile->spritesset[def];
return;
} else if (numloaded & 0x80) {
@@ -908,14 +1068,14 @@ static void NewSpriteGroup(byte *buf, int len)
return;
}
- if (!_spriteset_start) {
+ if (!_cur_grffile->spriteset_start) {
grfmsg(GMS_ERROR, "NewSpriteGroup: No sprite set to work on! Skipping.");
return;
}
- if (_spriteset_feature != feature) {
- grfmsg(GMS_ERROR, "NewSpriteGroup: Group feature %x doesn't match set feature %x! Skipping.", feature, _spriteset_feature);
- return;
+ if (_cur_grffile->spriteset_feature != feature) {
+ grfmsg(GMS_ERROR, "NewSpriteGroup: Group feature %x doesn't match set feature %x! Playing it risky and trying to use it anyway.", feature, _cur_grffile->spriteset_feature);
+ // return; // XXX: we can't because of MB's newstats.grf --pasky
}
check_length(bufend - buf, 5, "NewSpriteGroup");
@@ -933,30 +1093,30 @@ static void NewSpriteGroup(byte *buf, int len)
numloading = 16;
}
- if (setid >= _spritesset_count) {
- _spritesset_count = setid + 1;
- _spritesset = realloc(_spritesset, _spritesset_count * sizeof(struct SpriteGroup));
+ if (setid >= _cur_grffile->spritesset_count) {
+ _cur_grffile->spritesset_count = setid + 1;
+ _cur_grffile->spritesset = realloc(_cur_grffile->spritesset, _cur_grffile->spritesset_count * sizeof(struct SpriteGroup));
}
- group = &_spritesset[setid];
+ group = &_cur_grffile->spritesset[setid];
memset(group, 0, sizeof(struct SpriteGroup));
- group->sprites_per_set = _spriteset_numents;
+ group->sprites_per_set = _cur_grffile->spriteset_numents;
group->loaded_count = numloaded;
group->loading_count = numloading;
DEBUG(grf, 6) ("NewSpriteGroup: New SpriteGroup 0x%02hhx, %u views, %u loaded, %u loading, sprites %u - %u",
setid, group->sprites_per_set, group->loaded_count, group->loading_count,
- _spriteset_start - _cur_grffile->sprite_offset,
- _spriteset_start + (_spriteset_numents * (numloaded + numloading)) - _cur_grffile->sprite_offset);
+ _cur_grffile->spriteset_start - _cur_grffile->sprite_offset,
+ _cur_grffile->spriteset_start + (_cur_grffile->spriteset_numents * (numloaded + numloading)) - _cur_grffile->sprite_offset);
for (i = 0; i < numloaded; i++) {
uint16 spriteset_id = grf_load_word(&loaded_ptr);
- group->loaded[i] = _spriteset_start + spriteset_id * _spriteset_numents;
+ group->loaded[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
DEBUG(grf, 8) ("NewSpriteGroup: + group->loaded[%i] = %u (subset %u)", i, group->loaded[i], spriteset_id);
}
for (i = 0; i < numloading; i++) {
uint16 spriteset_id = grf_load_word(&loading_ptr);
- group->loading[i] = _spriteset_start + spriteset_id * _spriteset_numents;
+ group->loading[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
DEBUG(grf, 8) ("NewSpriteGroup: + group->loading[%i] = %u (subset %u)", i, group->loading[i], spriteset_id);
}
}
@@ -1000,11 +1160,43 @@ static void NewVehicle_SpriteGroupMapping(byte *buf, int len)
DEBUG(grf, 6) ("VehicleMapSpriteGroup: Feature %d, %d ids, %d cids, wagon override %d.",
feature, idcount, cidcount, wagover);
+
if (feature == GSF_STATION) {
- grfmsg(GMS_WARN, "VehicleMapSpriteGroup: Stations unsupported, skipping.");
+ // We do things differently for stations.
+
+ /* XXX: Currently we don't support cargo-specific images, so
+ * we go straight to the defaults. */
+ byte *bp = buf + 4 + idcount + cidcount * 3;
+ uint16 groupid = grf_load_word(&bp);
+
+ for (i = 0; i < idcount; i++) {
+ uint8 stid = buf[3 + i];
+ int j;
+
+ if (groupid >= _cur_grffile->spritesset_count) {
+ grfmsg(GMS_WARN, "VehicleMapSpriteGroup: Spriteset %x out of range %x, skipping.",
+ groupid, _cur_grffile->spritesset_count);
+ return;
+ }
+
+ // relocate sprite indexes based on spriteset locations
+ for (j = 0; j < _cur_grffile->statinfo_tiles[stid]; j++) {
+ DrawTileSeqStruct *seq;
+
+ foreach_draw_tile_seq(seq, (DrawTileSeqStruct*) _cur_grffile->statinfo_renderdata[stid][j].seq) {
+ seq->image += _cur_grffile->spritesset[groupid].loading[0];
+ }
+ }
+ /* FIXME: This means several GRF files defining new stations
+ * will override each other, but the stid should be GRF-specific
+ * instead! --pasky */
+ SetCustomStation(_cur_grffile->statinfo_classid[stid], stid, _cur_grffile->statinfo_renderdata[stid], _cur_grffile->statinfo_tiles[stid]);
+ _cur_grffile->statinfo_classid[stid] = 0;
+ }
return;
}
+
/* If ``n-id'' (or ``idcount'') is zero, this is a ``feature
* callback''. I have no idea how this works, so we will ignore it for
* now. --octo */
@@ -1017,7 +1209,7 @@ static void NewVehicle_SpriteGroupMapping(byte *buf, int len)
// 03 00 01 19 01 00 00 00 00 - this is missing one 00 at the end,
// what should we exactly do with that? --pasky
- if (!_spriteset_start || !_spritesset) {
+ if (!_cur_grffile->spriteset_start || !_cur_grffile->spritesset) {
grfmsg(GMS_WARN, "VehicleMapSpriteGroup: No sprite set to work on! Skipping.");
return;
}
@@ -1056,8 +1248,8 @@ static void NewVehicle_SpriteGroupMapping(byte *buf, int len)
DEBUG(grf, 8) ("VehicleMapSpriteGroup: * [%d] Cargo type %x, group id %x", c, ctype, groupid);
- if (groupid >= _spritesset_count) {
- grfmsg(GMS_WARN, "VehicleMapSpriteGroup: Spriteset %x out of range %x, skipping.", groupid, _spritesset_count);
+ if (groupid >= _cur_grffile->spritesset_count) {
+ grfmsg(GMS_WARN, "VehicleMapSpriteGroup: Spriteset %x out of range %x, skipping.", groupid, _cur_grffile->spritesset_count);
return;
}
@@ -1066,9 +1258,9 @@ static void NewVehicle_SpriteGroupMapping(byte *buf, int len)
if (wagover) {
// TODO: No multiple cargo types per vehicle yet. --pasky
- SetWagonOverrideSprites(engine, &_spritesset[groupid], last_engines, last_engines_count);
+ SetWagonOverrideSprites(engine, &_cur_grffile->spritesset[groupid], last_engines, last_engines_count);
} else {
- SetCustomEngineSprites(engine, ctype, &_spritesset[groupid]);
+ SetCustomEngineSprites(engine, ctype, &_cur_grffile->spritesset[groupid]);
last_engines[i] = engine;
}
}
@@ -1084,16 +1276,16 @@ static void NewVehicle_SpriteGroupMapping(byte *buf, int len)
uint8 engine = buf[3 + i] + _vehshifts[feature];
// Don't tell me you don't love duplicated code!
- if (groupid >= _spritesset_count) {
- grfmsg(GMS_WARN, "VehicleMapSpriteGroup: Spriteset %x out of range %x, skipping.", groupid, _spritesset_count);
+ if (groupid >= _cur_grffile->spritesset_count) {
+ grfmsg(GMS_WARN, "VehicleMapSpriteGroup: Spriteset %x out of range %x, skipping.", groupid, _cur_grffile->spritesset_count);
return;
}
if (wagover) {
// TODO: No multiple cargo types per vehicle yet. --pasky
- SetWagonOverrideSprites(engine, &_spritesset[groupid], last_engines, last_engines_count);
+ SetWagonOverrideSprites(engine, &_cur_grffile->spritesset[groupid], last_engines, last_engines_count);
} else {
- SetCustomEngineSprites(engine, CID_DEFAULT, &_spritesset[groupid]);
+ SetCustomEngineSprites(engine, CID_DEFAULT, &_cur_grffile->spritesset[groupid]);
last_engines[i] = engine;
}
}
@@ -1116,6 +1308,7 @@ static void VehicleNewName(byte *buf, int len)
* (completely new scenarios changing all graphics and logically also
* factory names etc). We should then also support all languages (by
* name), not only the original four ones. --pasky */
+ /* TODO: Support for custom station class/type names. */
uint8 feature;
uint8 lang;
@@ -1577,16 +1770,13 @@ void InitNewGRFFile(const char *filename, int sprite_offset)
{
struct GRFFile *newfile;
- newfile = malloc(sizeof(struct GRFFile));
+ newfile = calloc(1, sizeof(struct GRFFile));
if (newfile == NULL)
error ("Out of memory");
newfile->filename = strdup(filename);
- newfile->grfid = 0;
- newfile->flags = 0x0000;
newfile->sprite_offset = sprite_offset;
- newfile->next = NULL;
if (_first_grffile == NULL) {
_cur_grffile = newfile;
@@ -1645,15 +1835,21 @@ void DecodeSpecialSprite(const char *filename, int num, int spriteid, int stage)
action = buf[0];
- /* XXX: Action 0x03 is temporarily processed together with actions 0x01
- * and 0x02 before it is fixed to be reentrant (probably storing the
- * group information in {struct GRFFile}). --pasky */
+ /* XXX: There is a difference between staged loading in TTDPatch and
+ * here. In TTDPatch, for some reason actions 1 and 2 are carried out
+ * during stage 0, whilst action 3 is carried out during stage 1 (to
+ * "resolve" cargo IDs... wtf). This is a little problem, because cargo
+ * IDs are valid only within a given set (action 1) block, and may be
+ * overwritten after action 3 associates them. But overwriting happens
+ * in an earlier stage than associating, so... We just process actions
+ * 1 and 2 in stage 1 now, let's hope that won't get us into problems.
+ * --pasky */
if (stage == 0) {
- /* During initialization, actions 0, 3, 4, 5 and 7 are ignored. */
+ /* During initialization, actions 0, 1, 2, 3, 4, 5 and 7 are ignored. */
- if ((action == 0x00) /*|| (action == 0x03)*/ || (action == 0x04)
- || (action == 0x05) || (action == 0x07)) {
+ if ((action == 0x00) || (action == 0x01) || (action == 0x02) || (action == 0x03)
+ || (action == 0x04) || (action == 0x05) || (action == 0x07)) {
DEBUG (grf, 7) ("DecodeSpecialSprite: Action: %x, Stage 0, Skipped", action);
/* Do nothing. */
@@ -1672,7 +1868,7 @@ void DecodeSpecialSprite(const char *filename, int num, int spriteid, int stage)
* considered active if its action 8 has been processed, i.e. its
* action 8 hasn't been skipped using an action 7.
*
- * During activation, only actions 0, 3, 4, 5, 7, 8, 9 and 0A are
+ * During activation, only actions 0, 1, 2, 3, 4, 5, 7, 8, 9 and 0A are
* carried out. All others are ignored, because they only need to be
* processed once at initialization. */
@@ -1686,8 +1882,9 @@ void DecodeSpecialSprite(const char *filename, int num, int spriteid, int stage)
DEBUG (grf, 7) ("DecodeSpecialSprite: Action: %x, Stage 1, Not activated", action);
/* Do nothing. */
- } else if ((action == 0x00) /*|| (action == 0x03)*/ || (action == 0x04) || (action == 0x05)
- || (action == 0x07) || (action == 0x08) || (action == 0x09) || (action == 0x0A)) {
+ } else if ((action == 0x00) || (action == 0x01) || (action == 0x02) || (action == 0x03)
+ || (action == 0x04) || (action == 0x05) || (action == 0x07) || (action == 0x08)
+ || (action == 0x09) || (action == 0x0A)) {
DEBUG (grf, 7) ("DecodeSpecialSprite: Action: %x, Stage 1", action);
handlers[action](buf, num);
diff --git a/rail_cmd.c b/rail_cmd.c
index 18194af21..5a79829be 100644
--- a/rail_cmd.c
+++ b/rail_cmd.c
@@ -6,6 +6,7 @@
#include "pathfind.h"
#include "town.h"
#include "sound.h"
+#include "station.h"
void ShowTrainDepotWindow(uint tile);
@@ -734,6 +735,11 @@ int32 CmdBuildTrainCheckpoint(int x, int y, uint32 flags, uint32 p1, uint32 p2)
if (flags & DC_EXEC) {
ModifyTile(tile, MP_MAP5, RAIL_TYPE_CHECKPOINT | dir);
+ if (p1 & 0x100) {
+ // custom graphics
+ _map3_lo[tile] |= 16;
+ _map3_hi[tile] = p1 & 0xff;
+ }
cp->deleted = 0;
cp->xy = tile;
@@ -790,6 +796,8 @@ static int32 RemoveTrainCheckpoint(uint tile, uint32 flags, bool justremove)
if (justremove) {
ModifyTile(tile, MP_MAP5, 1<<direction);
+ _map3_lo[tile] &= ~16;
+ _map3_hi[tile] = 0;
} else {
DoClearSquare(tile);
SetSignalsOnBothDir(tile, direction);
@@ -1405,6 +1413,19 @@ DetailedTrackProc * const _detailed_track_proc[16] = {
DetTrackDrawProc_Null,
};
+static void DrawSpecialBuilding(uint32 image, uint32 tracktype_offs,
+ TileInfo *ti,
+ byte x, byte y, byte z,
+ byte xsize, byte ysize, byte zsize)
+{
+ if (image & 0x8000)
+ image |= _drawtile_track_palette;
+ image += tracktype_offs;
+ if (!(_display_opt & DO_TRANS_BUILDINGS)) // show transparent depots
+ image = (image & 0x3FFF) | 0x3224000;
+ AddSortableSpriteToDraw(image, ti->x + x, ti->y + y, xsize, ysize, zsize, ti->z + z);
+}
+
static void DrawTile_Track(TileInfo *ti)
{
uint32 tracktype_offs, image;
@@ -1521,6 +1542,28 @@ static void DrawTile_Track(TileInfo *ti)
if (ti->tileh != 0) { DrawFoundation(ti, ti->tileh); }
+ if (!IS_RAIL_DEPOT(m5) && IS_RAIL_CHECKPOINT(m5) && _map3_lo[ti->tile]&16) {
+ // look for customization
+ DrawTileSprites *cust = GetCustomStation('WAYP', _map3_hi[ti->tile]);
+
+ if (cust) {
+ DrawTileSeqStruct const *seq;
+
+ cust = &cust[2 + (m5 & 0x1)]; // emulate station tile - open with building
+
+ image = cust->ground_sprite;
+ if (image & 0x8000) image = (image & 0x7FFF) + tracktype_offs;
+ DrawGroundSprite(image);
+
+ foreach_draw_tile_seq(seq, cust->seq) {
+ DrawSpecialBuilding(seq->image|0x8000, 0, ti,
+ seq->delta_x, seq->delta_y, seq->delta_z,
+ seq->width, seq->height, seq->unk);
+ }
+ return;
+ }
+ }
+
s = _track_depot_layout_table[type];
image = *(const uint16*)s;
@@ -1541,13 +1584,9 @@ static void DrawTile_Track(TileInfo *ti)
drss = (const DrawTrackSeqStruct*)(s + sizeof(uint16));
while ((image=drss->image) != 0) {
- if (image & 0x8000)
- image |= _drawtile_track_palette;
- image += (type<4)?tracktype_offs:0;
- if (!(_display_opt & DO_TRANS_BUILDINGS)) // show transparent depots
- image = (image & 0x3FFF) | 0x3224000;
- AddSortableSpriteToDraw(image, ti->x | drss->subcoord_x,
- ti->y | drss->subcoord_y, drss->width, drss->height, 0x17, ti->z);
+ DrawSpecialBuilding(image, type < 4 ? tracktype_offs : 0, ti,
+ drss->subcoord_x, drss->subcoord_y, 0,
+ drss->width, drss->height, 0x17);
drss++;
}
}
diff --git a/rail_gui.c b/rail_gui.c
index 880153d27..cf106f57f 100644
--- a/rail_gui.c
+++ b/rail_gui.c
@@ -114,7 +114,12 @@ static void PlaceRail_Depot(uint tile)
static void PlaceRail_Checkpoint(uint tile)
{
if (!_remove_button_clicked) {
- DoCommandP(tile, 0, 0, CcPlaySound1E, CMD_BUILD_TRAIN_CHECKPOINT | CMD_MSG(STR_CANT_BUILD_TRAIN_CHECKPOINT));
+ /* TODO: We need a graphics selector. In the meantime we use the first
+ * custom station ID which works ok with newstats.grf (if you add it
+ * to openttd.cfg you want custom checkpoints) and if you don't have
+ * any custom station graphics it will fall back to the railstation
+ * sprites anyway. --pasky */
+ DoCommandP(tile, 0x100, 0, CcPlaySound1E, CMD_BUILD_TRAIN_CHECKPOINT | CMD_MSG(STR_CANT_BUILD_TRAIN_CHECKPOINT));
} else {
DoCommandP(tile, 0, 0, CcPlaySound1E, CMD_REMOVE_TRAIN_CHECKPOINT | CMD_MSG(STR_CANT_REMOVE_TRAIN_CHECKPOINT));
}
diff --git a/station.h b/station.h
index da5c4d6bb..10271785f 100644
--- a/station.h
+++ b/station.h
@@ -107,4 +107,8 @@ typedef struct DrawTileSprites {
#define foreach_draw_tile_seq(idx, list) for (idx = list; ((byte) idx->delta_x) != 0x80; idx++)
+void SetCustomStation(uint32 classid, byte stid, DrawTileSprites *data, byte tiles);
+DrawTileSprites *GetCustomStation(uint32 classid, byte stid);
+int GetCustomStationsCount(uint32 classid);
+
#endif /* STATION_H */
diff --git a/station_cmd.c b/station_cmd.c
index c28c2587e..8e0b99ae6 100644
--- a/station_cmd.c
+++ b/station_cmd.c
@@ -957,6 +957,38 @@ uint GetStationPlatforms(Station *st, uint tile)
}
+/* TODO: Multiple classes! */
+/* FIXME: Also, we should actually allocate the station id (but
+ * SetCustomStation() needs to be able to override an existing custom station
+ * as well) on our own. This would also prevent possible weirdness if some GRF
+ * file used non-contignuous station ids. --pasky */
+
+static int _waypoint_highest_id = -1;
+static DrawTileSprites _waypoint_data[256][8];
+
+void SetCustomStation(uint32 classid, byte stid, DrawTileSprites *data, byte tiles)
+{
+ assert(classid == 'WAYP');
+ if (stid > _waypoint_highest_id)
+ _waypoint_highest_id = stid;
+ memcpy(_waypoint_data[stid], data, sizeof(DrawTileSprites) * tiles);
+}
+
+DrawTileSprites *GetCustomStation(uint32 classid, byte stid)
+{
+ assert(classid == 'WAYP');
+ if (stid > _waypoint_highest_id || !_waypoint_data || !_waypoint_data[stid])
+ return NULL;
+ return _waypoint_data[stid];
+}
+
+int GetCustomStationsCount(uint32 classid)
+{
+ assert(classid == 'WAYP');
+ return _waypoint_highest_id + 1;
+}
+
+
static int32 RemoveRailroadStation(Station *st, TileIndex tile, uint32 flags)
{
int w,h;