diff options
-rw-r--r-- | docs/landscape.html | 2 | ||||
-rw-r--r-- | engine.h | 6 | ||||
-rw-r--r-- | grfspecial.c | 377 | ||||
-rw-r--r-- | rail_cmd.c | 53 | ||||
-rw-r--r-- | rail_gui.c | 7 | ||||
-rw-r--r-- | station.h | 4 | ||||
-rw-r--r-- | station_cmd.c | 32 |
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>
@@ -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 = ∅ + 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 = ∅ + 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)); } @@ -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; |