diff options
author | celestar <celestar@openttd.org> | 2004-11-14 09:07:15 +0000 |
---|---|---|
committer | celestar <celestar@openttd.org> | 2004-11-14 09:07:15 +0000 |
commit | eb50427670871854931383f8bf331ddaea3ebc45 (patch) | |
tree | 42723b432782d90b954356a8da01ddcb5f40da1d /grfspecial.c | |
parent | a9a852a4d61465ba33113239a9264ec39ef4c652 (diff) | |
download | openttd-eb50427670871854931383f8bf331ddaea3ebc45.tar.xz |
(svn r581) -newgrf: Basic support for new stations - only waypoints supported now and only
the first custom one can be placed (no selector GUI, coming soon). This
also moves some global variables to {struct GRFFile} and reorders which
actions are processed in what stage, to get it all working together --
(pasky)
Diffstat (limited to 'grfspecial.c')
-rw-r--r-- | grfspecial.c | 377 |
1 files changed, 287 insertions, 90 deletions
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); |