summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine.c30
-rw-r--r--grfspecial.c84
-rw-r--r--rail_cmd.c5
-rw-r--r--sprite.h78
-rw-r--r--station.h11
-rw-r--r--station_cmd.c36
6 files changed, 189 insertions, 55 deletions
diff --git a/engine.c b/engine.c
index c09799918..5be619c80 100644
--- a/engine.c
+++ b/engine.c
@@ -196,6 +196,9 @@ void SetWagonOverrideSprites(byte engine, struct SpriteGroup *group,
wos->overrides_count * sizeof(struct WagonOverride));
wo = &wos->overrides[wos->overrides_count - 1];
+ /* FIXME: If we are replacing an override, release original SpriteGroup
+ * to prevent leaks. But first we need to refcount the SpriteGroup.
+ * --pasky */
wo->group = *group;
wo->trains = trains;
wo->train_id = malloc(trains);
@@ -207,8 +210,10 @@ static struct SpriteGroup *GetWagonOverrideSpriteSet(byte engine, byte overridin
struct WagonOverrides *wos = &_engine_wagon_overrides[engine];
int i;
- // XXX: This could turn out to be a timesink on profiles. We could always just
- // dedicate 65535 bytes for an [engine][train] trampoline.
+ // XXX: This could turn out to be a timesink on profiles. We could
+ // always just dedicate 65535 bytes for an [engine][train] trampoline
+ // for O(1). Or O(logMlogN) and searching binary tree or smt. like
+ // that. --pasky
for (i = 0; i < wos->overrides_count; i++) {
struct WagonOverride *wo = &wos->overrides[i];
@@ -232,7 +237,9 @@ static struct SpriteGroup _engine_custom_sprites[256][NUM_CID];
void SetCustomEngineSprites(byte engine, byte cargo, struct SpriteGroup *group)
{
- assert(group->sprites_per_set == 4 || group->sprites_per_set == 8);
+ /* FIXME: If we are replacing an override, release original SpriteGroup
+ * to prevent leaks. But first we need to refcount the SpriteGroup.
+ * --pasky */
_engine_custom_sprites[engine][cargo] = *group;
}
@@ -240,6 +247,7 @@ int GetCustomEngineSprite(byte engine, uint16 overriding_engine, byte cargo,
byte loaded, byte in_motion, byte direction)
{
struct SpriteGroup *group = &_engine_custom_sprites[engine][cargo];
+ struct RealSpriteGroup *rsg;
int totalsets, spriteset;
int r;
@@ -250,22 +258,26 @@ int GetCustomEngineSprite(byte engine, uint16 overriding_engine, byte cargo,
if (overset) group = overset;
}
- if (!group->sprites_per_set && cargo != 29) {
+ /* TODO: Resolve surreal groups properly. --pasky */
+ rsg = TriviallyGetRSG(group);
+
+ if (!rsg->sprites_per_set && cargo != 29) {
// This group is empty but perhaps there'll be a default one.
- group = &_engine_custom_sprites[engine][29];
+ /* TODO: Resolve surreal groups properly. --pasky */
+ rsg = TriviallyGetRSG(&_engine_custom_sprites[engine][29]);
}
- if (!group->sprites_per_set) {
+ if (!rsg->sprites_per_set) {
// This group is empty. This function users should therefore
// look up the sprite number in _engine_original_sprites.
return 0;
}
direction %= 8;
- if (group->sprites_per_set == 4)
+ if (rsg->sprites_per_set == 4)
direction %= 4;
- totalsets = in_motion ? group->loaded_count : group->loading_count;
+ totalsets = in_motion ? rsg->loaded_count : rsg->loading_count;
// My aim here is to make it possible to visually determine absolutely
// empty and totally full vehicles. --pasky
@@ -282,7 +294,7 @@ int GetCustomEngineSprite(byte engine, uint16 overriding_engine, byte cargo,
spriteset--;
}
- r = (in_motion ? group->loaded[spriteset] : group->loading[spriteset]) + direction;
+ r = (in_motion ? rsg->loaded[spriteset] : rsg->loading[spriteset]) + direction;
return r;
}
diff --git a/grfspecial.c b/grfspecial.c
index dcf55a53e..3afda1d81 100644
--- a/grfspecial.c
+++ b/grfspecial.c
@@ -1055,6 +1055,7 @@ static void NewSpriteGroup(byte *buf, int len)
uint8 numloaded;
uint8 numloading;
struct SpriteGroup *group;
+ struct RealSpriteGroup *rg;
byte *loaded_ptr;
byte *loading_ptr;
int i;
@@ -1065,27 +1066,59 @@ static void NewSpriteGroup(byte *buf, int len)
numloaded = buf[3];
numloading = buf[4];
- if (numloaded == 0x81) {
- /* 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];
- uint8 nvar = buf[7];
- //uint32 val;
- uint16 def;
+ if (numloaded == 0x81 || numloaded == 0x82) {
+ struct DeterministicSpriteGroup *dg;
+ int i;
- grfmsg(GMS_WARN, "NewSpriteGroup(0x81): Unsupported variable %x. Using default cid.", var);
+ // Ok, this is gonna get a little wild, so hold your breath...
- //val = (0xff << shiftnum) & andmask;
+ /* This stuff is getting actually evaluated in
+ * EvalDeterministicSpriteGroup(). */
+
+ buf += 4; len -= 4;
+ check_length(len, 6, "NewSpriteGroup 0x81/0x82");
if (setid >= _cur_grffile->spritegroups_count) {
_cur_grffile->spritegroups_count = setid + 1;
_cur_grffile->spritegroups = realloc(_cur_grffile->spritegroups, _cur_grffile->spritegroups_count * sizeof(struct SpriteGroup));
}
- buf += 8 + nvar * 4;
- def = grf_load_word(&buf);
- _cur_grffile->spritegroups[setid] = _cur_grffile->spritegroups[def];
+
+ group = &_cur_grffile->spritegroups[setid];
+ memset(group, 0, sizeof(struct SpriteGroup));
+ group->type = SGT_DETERMINISTIC;
+ dg = &group->g.determ;
+
+ /* XXX: We don't free() anything, assuming that if there was
+ * some action here before, it got associated by action 3.
+ * We should perhaps keep some refcount? --pasky */
+
+ dg->var_scope = numloaded == 0x82 ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
+ dg->variable = grf_load_byte(&buf);
+
+ dg->shift_num = grf_load_byte(&buf);
+ dg->and_mask = grf_load_byte(&buf);
+ dg->operation = dg->shift_num >> 6; /* w00t */
+ dg->shift_num &= 0x3F;
+ if (dg->operation != DSG_OP_NONE) {
+ dg->add_val = grf_load_byte(&buf);
+ dg->divmod_val = grf_load_byte(&buf);
+ }
+
+ dg->num_ranges = grf_load_byte(&buf);
+ dg->ranges = calloc(dg->num_ranges, sizeof(*dg->ranges));
+ for (i = 0; i < dg->num_ranges; i++) {
+ uint16 setid = grf_load_word(&buf);
+
+ /* XXX: If multiple surreal sets attach a surreal
+ * set this way, we are in trouble. */
+ dg->ranges[i].group = _cur_grffile->spritegroups[setid];
+ dg->ranges[i].range_low = grf_load_byte(&buf);
+ dg->ranges[i].range_high = grf_load_byte(&buf);
+ }
+
+ dg->default_group = malloc(sizeof(*dg->default_group));
+ memcpy(dg->default_group, &_cur_grffile->spritegroups[grf_load_word(&buf)], sizeof(*dg->default_group));
+
return;
} else if (numloaded & 0x80) {
@@ -1124,25 +1157,28 @@ static void NewSpriteGroup(byte *buf, int len)
}
group = &_cur_grffile->spritegroups[setid];
memset(group, 0, sizeof(struct SpriteGroup));
- group->sprites_per_set = _cur_grffile->spriteset_numents;
- group->loaded_count = numloaded;
- group->loading_count = numloading;
+ group->type = SGT_REAL;
+ rg = &group->g.real;
+
+ rg->sprites_per_set = _cur_grffile->spriteset_numents;
+ rg->loaded_count = numloaded;
+ rg->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,
+ setid, rg->sprites_per_set, rg->loaded_count, rg->loading_count,
_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] = _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);
+ rg->loaded[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
+ DEBUG(grf, 8) ("NewSpriteGroup: + rg->loaded[%i] = %u (subset %u)", i, rg->loaded[i], spriteset_id);
}
for (i = 0; i < numloading; i++) {
uint16 spriteset_id = grf_load_word(&loading_ptr);
- 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);
+ rg->loading[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
+ DEBUG(grf, 8) ("NewSpriteGroup: + rg->loading[%i] = %u (subset %u)", i, rg->loading[i], spriteset_id);
}
}
@@ -1214,7 +1250,7 @@ static void NewVehicle_SpriteGroupMapping(byte *buf, int len)
continue;
}
- stat->relocation[1] = _cur_grffile->spritegroups[groupid];
+ stat->spritegroup[1] = _cur_grffile->spritegroups[groupid];
}
}
@@ -1232,7 +1268,7 @@ static void NewVehicle_SpriteGroupMapping(byte *buf, int len)
uint8 stid = buf[3 + i];
struct StationSpec *stat = &_cur_grffile->stations[stid];
- stat->relocation[0] = _cur_grffile->spritegroups[groupid];
+ stat->spritegroup[0] = _cur_grffile->spritegroups[groupid];
stat->grfid = _cur_grffile->grfid;
SetCustomStation(stid, stat);
stat->classid = 0;
diff --git a/rail_cmd.c b/rail_cmd.c
index 2406c800c..51e95f7f4 100644
--- a/rail_cmd.c
+++ b/rail_cmd.c
@@ -1551,7 +1551,8 @@ static void DrawTile_Track(TileInfo *ti)
DrawTileSeqStruct const *seq;
// emulate station tile - open with building
DrawTileSprites *cust = &stat->renderdata[2 + (m5 & 0x1)];
- uint32 relocation = GetCustomStationRelocation(stat, 0);
+ /* FIXME: NULL Station! --pasky */
+ uint32 relocation = GetCustomStationRelocation(stat, NULL, 0);
image = cust->ground_sprite;
if (image & 0x8000) image = (image & 0x7FFF) + tracktype_offs;
@@ -1633,7 +1634,7 @@ void DrawWaypointSprite(int x, int y, int stat_id)
assert(stat);
- relocation = GetCustomStationRelocation(stat, 1);
+ relocation = GetCustomStationRelocation(stat, NULL, 1);
// emulate station tile - open with building
// add 1 to get the other direction
cust = &stat->renderdata[2];
diff --git a/sprite.h b/sprite.h
index 5310d3bcb..13660a7d8 100644
--- a/sprite.h
+++ b/sprite.h
@@ -7,7 +7,7 @@
* depots or stations): */
typedef struct DrawTileSeqStruct {
- int8 delta_x;
+ int8 delta_x; // 0x80 is sequence terminator
int8 delta_y;
int8 delta_z;
byte width,height;
@@ -20,12 +20,16 @@ typedef struct DrawTileSprites {
DrawTileSeqStruct const *seq;
} DrawTileSprites;
+// Iterate through all DrawTileSeqStructs in DrawTileSprites.
#define foreach_draw_tile_seq(idx, list) for (idx = list; ((byte) idx->delta_x) != 0x80; idx++)
/* This is for custom sprites: */
-struct SpriteGroup {
+
+struct SpriteGroup;
+
+struct RealSpriteGroup {
// 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
@@ -43,4 +47,74 @@ struct SpriteGroup {
uint16 loading[16]; // sprite ids
};
+/* Shared by deterministic and random groups. */
+enum VarSpriteGroupScope {
+ VSG_SCOPE_SELF,
+ // Engine of consists for vehicles, city for stations.
+ VSG_SCOPE_PARENT,
+};
+
+struct DeterministicSpriteGroupRanges;
+
+struct DeterministicSpriteGroup {
+ // Take this variable:
+ enum VarSpriteGroupScope var_scope;
+ byte variable;
+
+ // Do this with it:
+ byte shift_num;
+ byte and_mask;
+
+ // Then do this with it:
+ enum DeterministicSpriteGroupOperation {
+ DSG_OP_NONE,
+ DSG_OP_DIV,
+ DSG_OP_MOD,
+ } operation;
+ byte add_val;
+ byte divmod_val;
+
+ // And apply it to this:
+ byte num_ranges;
+ struct DeterministicSpriteGroupRanges *ranges; // Dynamically allocated
+
+ // Dynamically allocated, this is the sole owner
+ struct SpriteGroup *default_group;
+};
+
+struct SpriteGroup {
+ enum SpriteGroupType {
+ SGT_REAL,
+ SGT_DETERMINISTIC,
+ SGT_RANDOM, /* TODO */
+ } type;
+
+ union {
+ struct RealSpriteGroup real;
+ struct DeterministicSpriteGroup determ;
+ } g;
+};
+
+struct DeterministicSpriteGroupRanges {
+ struct SpriteGroup group;
+ byte range_low;
+ byte range_high;
+};
+
+/* This is a temporary helper for SpriteGroup users not supporting variational
+ * sprite groups yet - it just traverses those cowardly, always taking the
+ * default choice until it hits a real sprite group, returning it. */
+static struct RealSpriteGroup *TriviallyGetRSG(struct SpriteGroup *sg);
+
+
+
+/**** Inline functions ****/
+
+static INLINE struct RealSpriteGroup *TriviallyGetRSG(struct SpriteGroup *sg)
+{
+ if (sg->type == SGT_REAL)
+ return &sg->g.real;
+ return TriviallyGetRSG(sg->g.determ.default_group);
+}
+
#endif
diff --git a/station.h b/station.h
index 1ebe87be6..7d926b0e6 100644
--- a/station.h
+++ b/station.h
@@ -102,9 +102,9 @@ struct StationSpec {
byte tiles;
DrawTileSprites renderdata[8];
- /* Sprite offsets for renderdata->seq->image. relocation[0] is default
- * whilst relocation[1] is "CID_PURCHASE". */
- struct SpriteGroup relocation[2];
+ /* Sprite offsets for renderdata->seq->image. spritegroup[0] is default
+ * whilst spritegroup[1] is "CID_PURCHASE". */
+ struct SpriteGroup spritegroup[2];
};
/* Here, @stid is local per-GRFFile station index. If spec->localidx is not yet
@@ -115,7 +115,10 @@ void SetCustomStation(byte stid, struct StationSpec *spec);
/* Here, @stid is global station index (in continous range 0..GetCustomStationsCount())
* (lookup is therefore very fast as we do this very frequently). */
struct StationSpec *GetCustomStation(uint32 classid, byte stid);
-uint32 GetCustomStationRelocation(struct StationSpec *spec, byte ctype);
+/* Get sprite offset for a given custom station and station structure (may be
+ * NULL if ctype is set - that means we are in a build dialog). The station
+ * structure is used for variational sprite groups. */
+uint32 GetCustomStationRelocation(struct StationSpec *spec, struct Station *stat, byte ctype);
int GetCustomStationsCount(uint32 classid);
#endif /* STATION_H */
diff --git a/station_cmd.c b/station_cmd.c
index 40a4137df..da83d893e 100644
--- a/station_cmd.c
+++ b/station_cmd.c
@@ -977,6 +977,9 @@ void SetCustomStation(byte local_stid, struct StationSpec *spec)
if (_waypoint_data[i].grfid == spec->grfid
&& _waypoint_data[i].localidx == local_stid + 1) {
stid = i;
+ /* FIXME: Release original SpriteGroup to
+ * prevent leaks. But first we need to
+ * refcount the SpriteGroup. --pasky */
break;
}
}
@@ -1003,25 +1006,30 @@ struct StationSpec *GetCustomStation(uint32 classid, byte stid)
return &_waypoint_data[stid];
}
-uint32 GetCustomStationRelocation(struct StationSpec *spec, byte ctype)
+uint32 GetCustomStationRelocation(struct StationSpec *spec, struct Station *stat, byte ctype)
{
+ struct RealSpriteGroup *rsg;
+
assert(spec->classid == 'WAYP');
/* In the future, variational spritegroups will kick in through this
- * accessor. */
-
- if (spec->relocation[ctype].loading_count != 0) {
- return spec->relocation[ctype].loading[0];
- } else if (spec->relocation[ctype].loading_count != 0) {
- return spec->relocation[ctype].loaded[0];
- } else {
- error("Custom station 0x%08x::0x%02x has no sprites associated.",
- spec->grfid, spec->localidx);
- /* This is what gets subscribed of dtss->image in grfspecial.c,
- * so it's probably kinda "default offset". Try to use it as
- * emergency measure. */
- return 0x42D;
+ * accessor, using @stat. */
+ rsg = TriviallyGetRSG(&spec->spritegroup[ctype]);
+
+ if (rsg->sprites_per_set != 0) {
+ if (rsg->loading_count != 0) {
+ return rsg->loading[0];
+ } else if (rsg->loading_count != 0) {
+ return rsg->loaded[0];
+ }
}
+
+ error("Custom station 0x%08x::0x%02x has no sprites associated.",
+ spec->grfid, spec->localidx);
+ /* This is what gets subscribed of dtss->image in grfspecial.c,
+ * so it's probably kinda "default offset". Try to use it as
+ * emergency measure. */
+ return 0x42D;
}
int GetCustomStationsCount(uint32 classid)