mirror of https://github.com/OpenTTD/OpenTTD
(svn r605) -newgrf: Framework for supporting variational spritegroups . Deterministic only at the moment, but random ones support shouldn't be that difficult now It doesn't do anything, but makes these actions actually possible (pasky).
parent
a348f74c65
commit
183c33931d
30
engine.c
30
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;
|
||||
}
|
||||
|
||||
|
|
84
grfspecial.c
84
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;
|
||||
|
|
|
@ -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];
|
||||
|
|
78
sprite.h
78
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
|
||||
|
|
11
station.h
11
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 */
|
||||
|
|
|
@ -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. */
|
||||
* accessor, using @stat. */
|
||||
rsg = TriviallyGetRSG(&spec->spritegroup[ctype]);
|
||||
|
||||
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;
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue