mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r4583) - NewGRF: Revamp sprite group loading to support advanced varadjusts and variable size. These are not yet processed, however.
This commit is contained in:
parent
b81e6dd6a2
commit
2f01c7d990
226
newgrf.c
226
newgrf.c
|
@ -176,6 +176,17 @@ static uint32 grf_load_dword(byte **buf)
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32 grf_load_var(byte size, byte **buf)
|
||||||
|
{
|
||||||
|
switch (size) {
|
||||||
|
case 1: return grf_load_byte(buf);
|
||||||
|
case 2: return grf_load_word(buf);
|
||||||
|
case 4: return grf_load_dword(buf);
|
||||||
|
default:
|
||||||
|
NOT_REACHED();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static GRFFile *GetFileByGRFID(uint32 grfid)
|
static GRFFile *GetFileByGRFID(uint32 grfid)
|
||||||
{
|
{
|
||||||
|
@ -1247,12 +1258,12 @@ static SpriteGroup* NewCallBackResultSpriteGroup(uint16 value)
|
||||||
* @param sprites The number of sprites per set.
|
* @param sprites The number of sprites per set.
|
||||||
* @return A spritegroup representing the sprite number result.
|
* @return A spritegroup representing the sprite number result.
|
||||||
*/
|
*/
|
||||||
static SpriteGroup* NewResultSpriteGroup(uint16 value, byte sprites)
|
static SpriteGroup* NewResultSpriteGroup(SpriteID sprite, byte num_sprites)
|
||||||
{
|
{
|
||||||
SpriteGroup *group = AllocateSpriteGroup();
|
SpriteGroup *group = AllocateSpriteGroup();
|
||||||
group->type = SGT_RESULT;
|
group->type = SGT_RESULT;
|
||||||
group->g.result.result = value;
|
group->g.result.sprite = sprite;
|
||||||
group->g.result.sprites = sprites;
|
group->g.result.num_sprites = num_sprites;
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1384,42 +1395,78 @@ static void NewSpriteGroup(byte *buf, int len)
|
||||||
/* Deterministic Sprite Group */
|
/* Deterministic Sprite Group */
|
||||||
case 0x81: // Self scope, byte
|
case 0x81: // Self scope, byte
|
||||||
case 0x82: // Parent scope, byte
|
case 0x82: // Parent scope, byte
|
||||||
|
case 0x85: // Self scope, word
|
||||||
|
case 0x86: // Parent scope, word
|
||||||
|
case 0x89: // Self scope, dword
|
||||||
|
case 0x8A: // Parent scope, dword
|
||||||
{
|
{
|
||||||
DeterministicSpriteGroup *dg;
|
byte varadjust;
|
||||||
int i;
|
byte varsize;
|
||||||
|
uint i;
|
||||||
|
|
||||||
check_length(bufend - buf, 6, "NewSpriteGroup 0x81/0x82");
|
/* Check we can load the var size parameter */
|
||||||
|
check_length(bufend - buf, 1, "NewSpriteGroup (Deterministic) (1)");
|
||||||
|
|
||||||
group = AllocateSpriteGroup();
|
group = AllocateSpriteGroup();
|
||||||
group->type = SGT_DETERMINISTIC;
|
group->type = SGT_DETERMINISTIC;
|
||||||
dg = &group->g.determ;
|
group->g.determ.var_scope = HASBIT(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
|
||||||
|
|
||||||
dg->var_scope = type == 0x82 ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
|
switch (GB(type, 2, 2)) {
|
||||||
dg->variable = grf_load_byte(&buf);
|
case 0: group->g.determ.size = DSG_SIZE_BYTE; varsize = 1; break;
|
||||||
/* Variables 0x60 - 0x7F include an extra parameter */
|
case 1: group->g.determ.size = DSG_SIZE_WORD; varsize = 2; break;
|
||||||
if (IS_BYTE_INSIDE(dg->variable, 0x60, 0x80))
|
case 2: group->g.determ.size = DSG_SIZE_DWORD; varsize = 4; break;
|
||||||
dg->parameter = grf_load_byte(&buf);
|
default: NOT_REACHED(); break;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (groupid & 0x8000) means this is callback result. */
|
check_length(bufend - buf, 2 + (varsize * 3) + 2, "NewSpriteGroup (Deterministic) (2)");
|
||||||
|
|
||||||
dg->num_ranges = grf_load_byte(&buf);
|
/* Loop through the var adjusts. Unfortunately we don't know how many we have
|
||||||
dg->ranges = calloc(dg->num_ranges, sizeof(*dg->ranges));
|
* from the outset, so we shall have to keep reallocing. */
|
||||||
for (i = 0; i < dg->num_ranges; i++) {
|
do {
|
||||||
dg->ranges[i].group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
|
DeterministicSpriteGroupAdjust *adjust;
|
||||||
dg->ranges[i].low = grf_load_byte(&buf);
|
|
||||||
dg->ranges[i].high = grf_load_byte(&buf);
|
if (group->g.determ.num_adjusts > 0) {
|
||||||
|
check_length(bufend - buf, 2 + (varsize * 3) + 3, "NewSpriteGroup (Deterministic) (3)");
|
||||||
|
}
|
||||||
|
|
||||||
|
group->g.determ.num_adjusts++;
|
||||||
|
group->g.determ.adjusts = realloc(group->g.determ.adjusts, group->g.determ.num_adjusts * sizeof(*group->g.determ.adjusts));
|
||||||
|
|
||||||
|
adjust = &group->g.determ.adjusts[group->g.determ.num_adjusts - 1];
|
||||||
|
|
||||||
|
/* The first var adjust doesn't have an operation specified, so we set it to add. */
|
||||||
|
adjust->operation = group->g.determ.num_adjusts == 1 ? DSGA_OP_ADD : grf_load_byte(&buf);
|
||||||
|
adjust->variable = grf_load_byte(&buf);
|
||||||
|
adjust->parameter = IS_BYTE_INSIDE(adjust->variable, 0x60, 0x80) ? grf_load_byte(&buf) : 0;
|
||||||
|
|
||||||
|
varadjust = grf_load_byte(&buf);
|
||||||
|
adjust->shift_num = GB(varadjust, 0, 5);
|
||||||
|
adjust->type = GB(varadjust, 6, 2);
|
||||||
|
adjust->and_mask = grf_load_var(varsize, &buf);
|
||||||
|
|
||||||
|
if (adjust->type != DSGA_TYPE_NONE) {
|
||||||
|
adjust->add_val = grf_load_var(varsize, &buf);
|
||||||
|
adjust->divmod_val = grf_load_var(varsize, &buf);
|
||||||
|
} else {
|
||||||
|
adjust->add_val = 0;
|
||||||
|
adjust->divmod_val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue reading var adjusts while bit 5 is set. */
|
||||||
|
} while (HASBIT(varadjust, 5));
|
||||||
|
|
||||||
|
group->g.determ.num_ranges = grf_load_byte(&buf);
|
||||||
|
group->g.determ.ranges = calloc(group->g.determ.num_ranges, sizeof(*group->g.determ.ranges));
|
||||||
|
|
||||||
|
check_length(bufend - buf, 2 + (2 + 2 * varsize) * group->g.determ.num_ranges, "NewSpriteGroup (Deterministic)");
|
||||||
|
|
||||||
|
for (i = 0; i < group->g.determ.num_ranges; i++) {
|
||||||
|
group->g.determ.ranges[i].group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
|
||||||
|
group->g.determ.ranges[i].low = grf_load_var(varsize, &buf);
|
||||||
|
group->g.determ.ranges[i].high = grf_load_var(varsize, &buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
dg->default_group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
|
group->g.determ.default_group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1427,79 +1474,88 @@ static void NewSpriteGroup(byte *buf, int len)
|
||||||
case 0x80: // Self scope
|
case 0x80: // Self scope
|
||||||
case 0x83: // Parent scope
|
case 0x83: // Parent scope
|
||||||
{
|
{
|
||||||
RandomizedSpriteGroup *rg;
|
byte triggers;
|
||||||
int i;
|
uint i;
|
||||||
|
|
||||||
/* This stuff is getting actually evaluated in
|
check_length(bufend - buf, 7, "NewSpriteGroup (Randomized) (1)");
|
||||||
* EvalRandomizedSpriteGroup(). */
|
|
||||||
|
|
||||||
check_length(bufend - buf, 6, "NewSpriteGroup 0x80/0x83");
|
|
||||||
|
|
||||||
group = AllocateSpriteGroup();
|
group = AllocateSpriteGroup();
|
||||||
group->type = SGT_RANDOMIZED;
|
group->type = SGT_RANDOMIZED;
|
||||||
rg = &group->g.random;
|
group->g.random.var_scope = HASBIT(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
|
||||||
|
|
||||||
rg->var_scope = type == 0x83 ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
|
triggers = grf_load_byte(&buf);
|
||||||
|
group->g.random.triggers = GB(triggers, 0, 7);
|
||||||
|
group->g.random.cmp_mode = HASBIT(triggers, 7) ? RSG_CMP_ALL : RSG_CMP_ANY;
|
||||||
|
group->g.random.lowest_randbit = grf_load_byte(&buf);
|
||||||
|
group->g.random.num_groups = grf_load_byte(&buf);
|
||||||
|
group->g.random.groups = calloc(group->g.random.num_groups, sizeof(*group->g.random.groups));
|
||||||
|
|
||||||
rg->triggers = grf_load_byte(&buf);
|
check_length(bufend - buf, 2 * group->g.random.num_groups, "NewSpriteGroup (Randomized) (2)");
|
||||||
rg->cmp_mode = rg->triggers & 0x80;
|
|
||||||
rg->triggers &= 0x7F;
|
|
||||||
|
|
||||||
rg->lowest_randbit = grf_load_byte(&buf);
|
for (i = 0; i < group->g.random.num_groups; i++) {
|
||||||
rg->num_groups = grf_load_byte(&buf);
|
group->g.random.groups[i] = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
|
||||||
|
|
||||||
rg->groups = calloc(rg->num_groups, sizeof(*rg->groups));
|
|
||||||
for (i = 0; i < rg->num_groups; i++) {
|
|
||||||
rg->groups[i] = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Neither a variable or randomized sprite group... must be a real group */
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
RealSpriteGroup *rg;
|
switch (feature) {
|
||||||
byte num_loaded = type;
|
case GSF_TRAIN:
|
||||||
byte num_loading = grf_load_byte(&buf);
|
case GSF_ROAD:
|
||||||
uint i;
|
case GSF_SHIP:
|
||||||
|
case GSF_AIRCRAFT:
|
||||||
|
case GSF_STATION:
|
||||||
|
{
|
||||||
|
byte sprites = _cur_grffile->spriteset_numents;
|
||||||
|
byte num_loaded = type;
|
||||||
|
byte num_loading = grf_load_byte(&buf);
|
||||||
|
uint i;
|
||||||
|
|
||||||
if (_cur_grffile->spriteset_start == 0) {
|
if (_cur_grffile->spriteset_start == 0) {
|
||||||
grfmsg(GMS_ERROR, "NewSpriteGroup: No sprite set to work on! Skipping.");
|
grfmsg(GMS_ERROR, "NewSpriteGroup: No sprite set to work on! Skipping.");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cur_grffile->first_spriteset == 0) {
|
||||||
|
DEBUG(grf, 6) ("NewSpriteGroup: Setting 0x%X as first Sprite ID", _cur_grffile->spriteset_start);
|
||||||
|
_cur_grffile->first_spriteset = _cur_grffile->spriteset_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_length(bufend - buf, 2 * num_loaded + 2 * num_loading, "NewSpriteGroup (Real) (1)");
|
||||||
|
|
||||||
|
group = AllocateSpriteGroup();
|
||||||
|
group->type = SGT_REAL;
|
||||||
|
|
||||||
|
group->g.real.num_loaded = num_loaded;
|
||||||
|
group->g.real.num_loading = num_loading;
|
||||||
|
if (num_loaded > 0) group->g.real.loaded = calloc(num_loaded, sizeof(*group->g.real.loaded));
|
||||||
|
if (num_loading > 0) group->g.real.loading = calloc(num_loading, sizeof(*group->g.real.loading));
|
||||||
|
|
||||||
|
DEBUG(grf, 6) ("NewSpriteGroup: New SpriteGroup 0x%02X, %u views, %u loaded, %u loading",
|
||||||
|
setid, sprites, num_loaded, num_loading);
|
||||||
|
|
||||||
|
for (i = 0; i < num_loaded; i++) {
|
||||||
|
uint16 spriteid = grf_load_word(&buf);
|
||||||
|
group->g.real.loaded[i] = CreateGroupFromGroupID(setid, type, spriteid, sprites);
|
||||||
|
DEBUG(grf, 8) ("NewSpriteGroup: + rg->loaded[%i] = subset %u", i, spriteid);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < num_loading; i++) {
|
||||||
|
uint16 spriteid = grf_load_word(&buf);
|
||||||
|
group->g.real.loading[i] = CreateGroupFromGroupID(setid, type, spriteid, sprites);
|
||||||
|
DEBUG(grf, 8) ("NewSpriteGroup: + rg->loading[%i] = subset %u", i, spriteid);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading of Tile Layout and Production Callback groups would happen here */
|
||||||
|
default:
|
||||||
|
grfmsg(GMS_WARN, "NewSpriteGroup: Unsupported feature %d, skipping.", feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_cur_grffile->first_spriteset == 0)
|
|
||||||
_cur_grffile->first_spriteset = _cur_grffile->spriteset_start;
|
|
||||||
|
|
||||||
group = AllocateSpriteGroup();
|
|
||||||
group->type = SGT_REAL;
|
|
||||||
rg = &group->g.real;
|
|
||||||
|
|
||||||
rg->sprites_per_set = _cur_grffile->spriteset_numents;
|
|
||||||
rg->loaded_count = num_loaded;
|
|
||||||
rg->loading_count = num_loading;
|
|
||||||
|
|
||||||
rg->loaded = calloc(rg->loaded_count, sizeof(*rg->loaded));
|
|
||||||
rg->loading = calloc(rg->loading_count, sizeof(*rg->loading));
|
|
||||||
|
|
||||||
DEBUG(grf, 6) ("NewSpriteGroup: New SpriteGroup 0x%02hhx, %u views, %u loaded, %u loading, sprites %u - %u",
|
|
||||||
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 * (num_loaded + num_loading)) - _cur_grffile->sprite_offset);
|
|
||||||
|
|
||||||
for (i = 0; i < num_loaded; i++) {
|
|
||||||
uint16 spriteset_id = grf_load_word(&buf);
|
|
||||||
rg->loaded[i] = CreateGroupFromGroupID(setid, type, spriteset_id, rg->sprites_per_set);
|
|
||||||
DEBUG(grf, 8) ("NewSpriteGroup: + rg->loaded[%i] = %u (subset %u)", i, rg->loaded[i]->g.result.result, spriteset_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < num_loading; i++) {
|
|
||||||
uint16 spriteset_id = grf_load_word(&buf);
|
|
||||||
rg->loading[i] = CreateGroupFromGroupID(setid, type, spriteset_id, rg->sprites_per_set);
|
|
||||||
DEBUG(grf, 8) ("NewSpriteGroup: + rg->loading[%i] = %u (subset %u)", i, rg->loading[i]->g.result.result, spriteset_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,16 +208,18 @@ static const SpriteGroup* ResolveVehicleSpriteGroup(const SpriteGroup *spritegro
|
||||||
const DeterministicSpriteGroup *dsg = &spritegroup->g.determ;
|
const DeterministicSpriteGroup *dsg = &spritegroup->g.determ;
|
||||||
const SpriteGroup *target;
|
const SpriteGroup *target;
|
||||||
int value = -1;
|
int value = -1;
|
||||||
|
// XXX Temporary support
|
||||||
|
byte variable = dsg->adjusts[0].variable;
|
||||||
|
|
||||||
//debug("[%p] Having fun resolving variable %x", veh, dsg->variable);
|
//debug("[%p] Having fun resolving variable %x", veh, variable);
|
||||||
if (dsg->variable == 0x0C) {
|
if (variable == 0x0C) {
|
||||||
/* Callback ID */
|
/* Callback ID */
|
||||||
value = callback_info & 0xFF;
|
value = callback_info & 0xFF;
|
||||||
} else if (dsg->variable == 0x10) {
|
} else if (variable == 0x10) {
|
||||||
value = (callback_info >> 8) & 0xFF;
|
value = (callback_info >> 8) & 0xFF;
|
||||||
} else if ((dsg->variable >> 6) == 0) {
|
} else if ((variable >> 6) == 0) {
|
||||||
/* General property */
|
/* General property */
|
||||||
value = GetDeterministicSpriteValue(dsg->variable);
|
value = GetDeterministicSpriteValue(variable);
|
||||||
} else {
|
} else {
|
||||||
/* Vehicle-specific property. */
|
/* Vehicle-specific property. */
|
||||||
|
|
||||||
|
@ -240,18 +242,18 @@ static const SpriteGroup* ResolveVehicleSpriteGroup(const SpriteGroup *spritegro
|
||||||
veh = GetFirstVehicleInChain(veh);
|
veh = GetFirstVehicleInChain(veh);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dsg->variable == 0x40 || dsg->variable == 0x41) {
|
if (variable == 0x40 || variable == 0x41) {
|
||||||
if (veh->type == VEH_Train) {
|
if (veh->type == VEH_Train) {
|
||||||
const Vehicle *u = GetFirstVehicleInChain(veh);
|
const Vehicle *u = GetFirstVehicleInChain(veh);
|
||||||
byte chain_before = 0, chain_after = 0;
|
byte chain_before = 0, chain_after = 0;
|
||||||
|
|
||||||
while (u != veh) {
|
while (u != veh) {
|
||||||
chain_before++;
|
chain_before++;
|
||||||
if (dsg->variable == 0x41 && u->engine_type != veh->engine_type)
|
if (variable == 0x41 && u->engine_type != veh->engine_type)
|
||||||
chain_before = 0;
|
chain_before = 0;
|
||||||
u = u->next;
|
u = u->next;
|
||||||
}
|
}
|
||||||
while (u->next != NULL && (dsg->variable == 0x40 || u->next->engine_type == veh->engine_type)) {
|
while (u->next != NULL && (variable == 0x40 || u->next->engine_type == veh->engine_type)) {
|
||||||
chain_after++;
|
chain_after++;
|
||||||
u = u->next;
|
u = u->next;
|
||||||
};
|
};
|
||||||
|
@ -265,7 +267,7 @@ static const SpriteGroup* ResolveVehicleSpriteGroup(const SpriteGroup *spritegro
|
||||||
} else {
|
} else {
|
||||||
// TTDPatch runs on little-endian arch;
|
// TTDPatch runs on little-endian arch;
|
||||||
// Variable is 0x80 + offset in TTD's vehicle structure
|
// Variable is 0x80 + offset in TTD's vehicle structure
|
||||||
switch (dsg->variable - 0x80) {
|
switch (variable - 0x80) {
|
||||||
#define veh_prop(id_, value_) case (id_): value = (value_); break
|
#define veh_prop(id_, value_) case (id_): value = (value_); break
|
||||||
veh_prop(0x00, veh->type);
|
veh_prop(0x00, veh->type);
|
||||||
veh_prop(0x01, MapOldSubType(veh));
|
veh_prop(0x01, MapOldSubType(veh));
|
||||||
|
@ -347,7 +349,7 @@ static const SpriteGroup* ResolveVehicleSpriteGroup(const SpriteGroup *spritegro
|
||||||
#undef veh_prop
|
#undef veh_prop
|
||||||
|
|
||||||
// Handle vehicle specific properties.
|
// Handle vehicle specific properties.
|
||||||
default: value = VehicleSpecificProperty(veh, dsg->variable - 0x80); break;
|
default: value = VehicleSpecificProperty(veh, variable - 0x80); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,7 +413,6 @@ int GetCustomEngineSprite(EngineID engine, const Vehicle* v, Direction direction
|
||||||
byte loaded = 0;
|
byte loaded = 0;
|
||||||
bool in_motion = 0;
|
bool in_motion = 0;
|
||||||
int totalsets, spriteset;
|
int totalsets, spriteset;
|
||||||
int r;
|
|
||||||
|
|
||||||
if (v != NULL) {
|
if (v != NULL) {
|
||||||
int capacity = v->cargo_cap;
|
int capacity = v->cargo_cap;
|
||||||
|
@ -444,16 +445,11 @@ int GetCustomEngineSprite(EngineID engine, const Vehicle* v, Direction direction
|
||||||
assert(group->type == SGT_REAL);
|
assert(group->type == SGT_REAL);
|
||||||
rsg = &group->g.real;
|
rsg = &group->g.real;
|
||||||
|
|
||||||
if (!rsg->sprites_per_set) {
|
// This group is empty. This function users should therefore
|
||||||
// This group is empty. This function users should therefore
|
// look up the sprite number in _engine_original_sprites.
|
||||||
// look up the sprite number in _engine_original_sprites.
|
if (rsg->num_loaded == 0 || rsg->num_loading == 0) return 0;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(rsg->sprites_per_set <= 8);
|
totalsets = in_motion ? rsg->num_loaded : rsg->num_loading;
|
||||||
direction %= rsg->sprites_per_set;
|
|
||||||
|
|
||||||
totalsets = in_motion ? rsg->loaded_count : rsg->loading_count;
|
|
||||||
|
|
||||||
// My aim here is to make it possible to visually determine absolutely
|
// My aim here is to make it possible to visually determine absolutely
|
||||||
// empty and totally full vehicles. --pasky
|
// empty and totally full vehicles. --pasky
|
||||||
|
@ -470,8 +466,10 @@ int GetCustomEngineSprite(EngineID engine, const Vehicle* v, Direction direction
|
||||||
spriteset--;
|
spriteset--;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = (in_motion ? rsg->loaded[spriteset]->g.result.result : rsg->loading[spriteset]->g.result.result) + direction;
|
group = in_motion ? rsg->loaded[spriteset] : rsg->loading[spriteset];
|
||||||
return r;
|
if (group->type != SGT_RESULT) return 0;
|
||||||
|
|
||||||
|
return group->g.result.sprite + (direction % group->g.result.num_sprites);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,6 +29,7 @@ static void SpriteGroupPoolCleanBlock(uint start_item, uint end_item)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SGT_DETERMINISTIC:
|
case SGT_DETERMINISTIC:
|
||||||
|
free(group->g.determ.adjusts);
|
||||||
free(group->g.determ.ranges);
|
free(group->g.determ.ranges);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
|
|
||||||
typedef struct SpriteGroup SpriteGroup;
|
typedef struct SpriteGroup SpriteGroup;
|
||||||
|
|
||||||
typedef struct RealSpriteGroup {
|
|
||||||
byte sprites_per_set; // means number of directions - 4 or 8
|
|
||||||
|
|
||||||
|
/* 'Real' sprite groups contain a list of other result or callback sprite
|
||||||
|
* groups. */
|
||||||
|
typedef struct RealSpriteGroup {
|
||||||
// Loaded = in motion, loading = not moving
|
// Loaded = in motion, loading = not moving
|
||||||
// Each group contains several spritesets, for various loading stages
|
// Each group contains several spritesets, for various loading stages
|
||||||
|
|
||||||
|
@ -16,53 +17,81 @@ typedef struct RealSpriteGroup {
|
||||||
// with small amount of cargo whilst loading is for stations with a lot
|
// with small amount of cargo whilst loading is for stations with a lot
|
||||||
// of da stuff.
|
// of da stuff.
|
||||||
|
|
||||||
byte loaded_count; ///< Number of loaded groups
|
byte num_loaded; ///< Number of loaded groups
|
||||||
|
byte num_loading; ///< Number of loading groups
|
||||||
SpriteGroup **loaded; ///< List of loaded groups (can be SpriteIDs or Callback results)
|
SpriteGroup **loaded; ///< List of loaded groups (can be SpriteIDs or Callback results)
|
||||||
byte loading_count; ///< Number of loading groups
|
|
||||||
SpriteGroup **loading; ///< List of loading groups (can be SpriteIDs or Callback results)
|
SpriteGroup **loading; ///< List of loading groups (can be SpriteIDs or Callback results)
|
||||||
} RealSpriteGroup;
|
} RealSpriteGroup;
|
||||||
|
|
||||||
/* Shared by deterministic and random groups. */
|
/* Shared by deterministic and random groups. */
|
||||||
typedef enum VarSpriteGroupScope {
|
typedef enum VarSpriteGroupScopes {
|
||||||
VSG_SCOPE_SELF,
|
VSG_SCOPE_SELF,
|
||||||
// Engine of consists for vehicles, city for stations.
|
// Engine of consists for vehicles, city for stations.
|
||||||
VSG_SCOPE_PARENT,
|
VSG_SCOPE_PARENT,
|
||||||
} VarSpriteGroupScope;
|
} VarSpriteGroupScope;
|
||||||
|
|
||||||
typedef struct DeterministicSpriteGroupRanges DeterministicSpriteGroupRanges;
|
typedef enum DeterministicSpriteGroupSizes {
|
||||||
|
DSG_SIZE_BYTE,
|
||||||
|
DSG_SIZE_WORD,
|
||||||
|
DSG_SIZE_DWORD,
|
||||||
|
} DeterministicSpriteGroupSize;
|
||||||
|
|
||||||
typedef enum DeterministicSpriteGroupOperation {
|
typedef enum DeterministicSpriteGroupAdjustTypes {
|
||||||
DSG_OP_NONE,
|
DSGA_TYPE_NONE,
|
||||||
DSG_OP_DIV,
|
DSGA_TYPE_DIV,
|
||||||
DSG_OP_MOD,
|
DSGA_TYPE_MOD,
|
||||||
} DeterministicSpriteGroupOperation;
|
} DeterministicSpriteGroupAdjustType;
|
||||||
|
|
||||||
typedef struct DeterministicSpriteGroupRange DeterministicSpriteGroupRange;
|
typedef enum DeterministicSpriteGroupAdjustOperations {
|
||||||
|
DSGA_OP_ADD, // a + b
|
||||||
|
DSGA_OP_SUB, // a - b
|
||||||
|
DSGA_OP_SMIN, // (signed) min(a, b)
|
||||||
|
DSGA_OP_SMAX, // (signed) max(a, b)
|
||||||
|
DSGA_OP_UMIN, // (unsigned) min(a, b)
|
||||||
|
DSGA_OP_UMAX, // (unsigned) max(a, b)
|
||||||
|
DSGA_OP_SDIV, // (signed) a / b
|
||||||
|
DSGA_OP_SMOD, // (signed) a % b
|
||||||
|
DSGA_OP_UDIV, // (unsigned) a / b
|
||||||
|
DSGA_OP_UMOD, // (unsigned) a & b
|
||||||
|
DSGA_OP_MUL, // a * b
|
||||||
|
DSGA_OP_AND, // a & b
|
||||||
|
DSGA_OP_OR, // a | b
|
||||||
|
DSGA_OP_XOR, // a ^ b
|
||||||
|
} DeterministicSpriteGroupAdjustOperation;
|
||||||
|
|
||||||
typedef struct DeterministicSpriteGroup {
|
|
||||||
// Take this variable:
|
typedef struct DeterministicSpriteGroupAdjust {
|
||||||
VarSpriteGroupScope var_scope;
|
DeterministicSpriteGroupAdjustOperation operation;
|
||||||
|
DeterministicSpriteGroupAdjustType type;
|
||||||
byte variable;
|
byte variable;
|
||||||
byte parameter; ///< Used for variables between 0x60 and 0x7F inclusive.
|
byte parameter; ///< Used for variables between 0x60 and 0x7F inclusive.
|
||||||
|
|
||||||
// Do this with it:
|
|
||||||
byte shift_num;
|
byte shift_num;
|
||||||
byte and_mask;
|
uint32 and_mask;
|
||||||
|
uint32 add_val;
|
||||||
|
uint32 divmod_val;
|
||||||
|
} DeterministicSpriteGroupAdjust;
|
||||||
|
|
||||||
// Then do this with it:
|
|
||||||
DeterministicSpriteGroupOperation operation;
|
|
||||||
byte add_val;
|
|
||||||
byte divmod_val;
|
|
||||||
|
|
||||||
// And apply it to this:
|
typedef struct DeterministicSpriteGroupRange {
|
||||||
|
SpriteGroup *group;
|
||||||
|
uint32 low;
|
||||||
|
uint32 high;
|
||||||
|
} DeterministicSpriteGroupRange;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct DeterministicSpriteGroup {
|
||||||
|
VarSpriteGroupScope var_scope;
|
||||||
|
DeterministicSpriteGroupSize size;
|
||||||
|
byte num_adjusts;
|
||||||
byte num_ranges;
|
byte num_ranges;
|
||||||
|
DeterministicSpriteGroupAdjust *adjusts;
|
||||||
DeterministicSpriteGroupRange *ranges; // Dynamically allocated
|
DeterministicSpriteGroupRange *ranges; // Dynamically allocated
|
||||||
|
|
||||||
// Dynamically allocated, this is the sole owner
|
// Dynamically allocated, this is the sole owner
|
||||||
SpriteGroup *default_group;
|
SpriteGroup *default_group;
|
||||||
} DeterministicSpriteGroup;
|
} DeterministicSpriteGroup;
|
||||||
|
|
||||||
typedef enum RandomizedSpriteGroupCompareMode {
|
typedef enum RandomizedSpriteGroupCompareModes {
|
||||||
RSG_CMP_ANY,
|
RSG_CMP_ANY,
|
||||||
RSG_CMP_ALL,
|
RSG_CMP_ALL,
|
||||||
} RandomizedSpriteGroupCompareMode;
|
} RandomizedSpriteGroupCompareMode;
|
||||||
|
@ -83,15 +112,22 @@ typedef struct RandomizedSpriteGroup {
|
||||||
SpriteGroup **groups;
|
SpriteGroup **groups;
|
||||||
} RandomizedSpriteGroup;
|
} RandomizedSpriteGroup;
|
||||||
|
|
||||||
|
|
||||||
|
/* This contains a callback result. A failed callback has a value of
|
||||||
|
* CALLBACK_FAILED */
|
||||||
typedef struct CallbackResultSpriteGroup {
|
typedef struct CallbackResultSpriteGroup {
|
||||||
uint16 result;
|
uint16 result;
|
||||||
} CallbackResultSpriteGroup;
|
} CallbackResultSpriteGroup;
|
||||||
|
|
||||||
|
|
||||||
|
/* A result sprite group returns the first SpriteID and the number of
|
||||||
|
* sprites in the set */
|
||||||
typedef struct ResultSpriteGroup {
|
typedef struct ResultSpriteGroup {
|
||||||
uint16 result;
|
SpriteID sprite;
|
||||||
byte sprites;
|
byte num_sprites;
|
||||||
} ResultSpriteGroup;
|
} ResultSpriteGroup;
|
||||||
|
|
||||||
|
/* List of different sprite group types */
|
||||||
typedef enum SpriteGroupType {
|
typedef enum SpriteGroupType {
|
||||||
SGT_INVALID,
|
SGT_INVALID,
|
||||||
SGT_REAL,
|
SGT_REAL,
|
||||||
|
@ -101,6 +137,7 @@ typedef enum SpriteGroupType {
|
||||||
SGT_RESULT,
|
SGT_RESULT,
|
||||||
} SpriteGroupType;
|
} SpriteGroupType;
|
||||||
|
|
||||||
|
/* Common wrapper for all the different sprite group types */
|
||||||
struct SpriteGroup {
|
struct SpriteGroup {
|
||||||
SpriteGroupType type;
|
SpriteGroupType type;
|
||||||
|
|
||||||
|
@ -113,12 +150,6 @@ struct SpriteGroup {
|
||||||
} g;
|
} g;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DeterministicSpriteGroupRange {
|
|
||||||
SpriteGroup *group;
|
|
||||||
byte low;
|
|
||||||
byte high;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
SpriteGroup *AllocateSpriteGroup(void);
|
SpriteGroup *AllocateSpriteGroup(void);
|
||||||
void InitializeSpriteGroupPool(void);
|
void InitializeSpriteGroupPool(void);
|
||||||
|
|
|
@ -169,10 +169,11 @@ static const RealSpriteGroup *ResolveStationSpriteGroup(const SpriteGroup *spg,
|
||||||
const DeterministicSpriteGroup *dsg = &spg->g.determ;
|
const DeterministicSpriteGroup *dsg = &spg->g.determ;
|
||||||
SpriteGroup *target;
|
SpriteGroup *target;
|
||||||
int value = -1;
|
int value = -1;
|
||||||
|
byte variable = dsg->adjusts[0].variable;
|
||||||
|
|
||||||
if ((dsg->variable >> 6) == 0) {
|
if ((variable >> 6) == 0) {
|
||||||
/* General property */
|
/* General property */
|
||||||
value = GetDeterministicSpriteValue(dsg->variable);
|
value = GetDeterministicSpriteValue(variable);
|
||||||
} else {
|
} else {
|
||||||
if (st == NULL) {
|
if (st == NULL) {
|
||||||
/* We are in a build dialog of something,
|
/* We are in a build dialog of something,
|
||||||
|
@ -192,7 +193,7 @@ static const RealSpriteGroup *ResolveStationSpriteGroup(const SpriteGroup *spg,
|
||||||
/* TODO: Town structure. */
|
/* TODO: Town structure. */
|
||||||
|
|
||||||
} else /* VSG_SELF */ {
|
} else /* VSG_SELF */ {
|
||||||
if (dsg->variable == 0x40 || dsg->variable == 0x41) {
|
if (variable == 0x40 || variable == 0x41) {
|
||||||
/* FIXME: This is ad hoc only
|
/* FIXME: This is ad hoc only
|
||||||
* for waypoints. */
|
* for waypoints. */
|
||||||
value = 0x01010000;
|
value = 0x01010000;
|
||||||
|
@ -200,7 +201,7 @@ static const RealSpriteGroup *ResolveStationSpriteGroup(const SpriteGroup *spg,
|
||||||
/* TODO: Only small fraction done. */
|
/* TODO: Only small fraction done. */
|
||||||
// TTDPatch runs on little-endian arch;
|
// TTDPatch runs on little-endian arch;
|
||||||
// Variable is 0x70 + offset in the TTD's station structure
|
// Variable is 0x70 + offset in the TTD's station structure
|
||||||
switch (dsg->variable - 0x70) {
|
switch (variable - 0x70) {
|
||||||
case 0x80: value = st->facilities; break;
|
case 0x80: value = st->facilities; break;
|
||||||
case 0x81: value = st->airport_type; break;
|
case 0x81: value = st->airport_type; break;
|
||||||
case 0x82: value = st->truck_stops->status; break;
|
case 0x82: value = st->truck_stops->status; break;
|
||||||
|
@ -229,10 +230,8 @@ uint32 GetCustomStationRelocation(const StationSpec *spec, const Station *st, by
|
||||||
const RealSpriteGroup *rsg = ResolveStationSpriteGroup(spec->spritegroup[ctype], st);
|
const RealSpriteGroup *rsg = ResolveStationSpriteGroup(spec->spritegroup[ctype], st);
|
||||||
if (rsg == NULL) return 0;
|
if (rsg == NULL) return 0;
|
||||||
|
|
||||||
if (rsg->sprites_per_set != 0) {
|
if (rsg->num_loading != 0) return rsg->loading[0]->g.result.sprite;
|
||||||
if (rsg->loading_count != 0) return rsg->loading[0]->g.result.result;
|
if (rsg->num_loaded != 0) return rsg->loaded[0]->g.result.sprite;
|
||||||
if (rsg->loaded_count != 0) return rsg->loaded[0]->g.result.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG(grf, 6)("Custom station 0x%08x::0x%02x has no sprites associated.",
|
DEBUG(grf, 6)("Custom station 0x%08x::0x%02x has no sprites associated.",
|
||||||
spec->grfid, spec->localidx);
|
spec->grfid, spec->localidx);
|
||||||
|
|
22
sprite.c
22
sprite.c
|
@ -11,27 +11,27 @@ SpriteGroup *EvalDeterministicSpriteGroup(const DeterministicSpriteGroup *dsg, i
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
value >>= dsg->shift_num; // This should bring us to the byte range.
|
value >>= dsg->adjusts[0].shift_num; // This should bring us to the byte range.
|
||||||
value &= dsg->and_mask;
|
value &= dsg->adjusts[0].and_mask;
|
||||||
|
|
||||||
if (dsg->operation != DSG_OP_NONE)
|
if (dsg->adjusts[0].operation != DSGA_TYPE_NONE)
|
||||||
value += (signed char) dsg->add_val;
|
value += (signed char) dsg->adjusts[0].add_val;
|
||||||
|
|
||||||
switch (dsg->operation) {
|
switch (dsg->adjusts[0].type) {
|
||||||
case DSG_OP_DIV:
|
case DSGA_TYPE_DIV:
|
||||||
value /= (signed char) dsg->divmod_val;
|
value /= (signed char) dsg->adjusts[0].divmod_val;
|
||||||
break;
|
break;
|
||||||
case DSG_OP_MOD:
|
case DSGA_TYPE_MOD:
|
||||||
value %= (signed char) dsg->divmod_val;
|
value %= (signed char) dsg->adjusts[0].divmod_val;
|
||||||
break;
|
break;
|
||||||
case DSG_OP_NONE:
|
case DSGA_TYPE_NONE:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < dsg->num_ranges; i++) {
|
for (i = 0; i < dsg->num_ranges; i++) {
|
||||||
DeterministicSpriteGroupRange *range = &dsg->ranges[i];
|
DeterministicSpriteGroupRange *range = &dsg->ranges[i];
|
||||||
|
|
||||||
if (range->low <= value && value <= range->high)
|
if (range->low <= (uint32)value && (uint32)value <= range->high)
|
||||||
return range->group;
|
return range->group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue