diff options
author | peter1138 <peter1138@openttd.org> | 2006-04-28 20:04:57 +0000 |
---|---|---|
committer | peter1138 <peter1138@openttd.org> | 2006-04-28 20:04:57 +0000 |
commit | 7273bd490d1a1fddb61b0577ed1d03ebbbcc2ed6 (patch) | |
tree | 882c805b9f0ef4ddf347b28d39d09a7328d9f506 /newgrf_engine.c | |
parent | 376bda7007d55e3a7bd99126d147b462f795c989 (diff) | |
download | openttd-7273bd490d1a1fddb61b0577ed1d03ebbbcc2ed6.tar.xz |
(svn r4611) - NewGRF: introduce new vehicle sprite group resolver code. This also fixes some known bugs (e.g. the 'Standard Five' in UKRS is now the correct colour)
Diffstat (limited to 'newgrf_engine.c')
-rw-r--r-- | newgrf_engine.c | 552 |
1 files changed, 253 insertions, 299 deletions
diff --git a/newgrf_engine.c b/newgrf_engine.c index 06c0c11f1..6065f7ad7 100644 --- a/newgrf_engine.c +++ b/newgrf_engine.c @@ -2,16 +2,16 @@ #include "stdafx.h" #include "openttd.h" +#include "variables.h" #include "debug.h" #include "functions.h" -#include "string.h" -#include "strings.h" #include "engine.h" +#include "train.h" +#include "player.h" #include "newgrf_callbacks.h" #include "newgrf_engine.h" -#include "sprite.h" -#include "variables.h" -#include "train.h" +#include "newgrf_station.h" +#include "newgrf_spritegroup.h" // TODO: We don't support cargo-specific wagon overrides. Pretty exotic... ;-) --pasky @@ -147,10 +147,171 @@ static int MapOldSubType(const Vehicle *v) return 2; } -static int VehicleSpecificProperty(const Vehicle *v, byte var) { + +/* Vehicle Resolver Functions */ +static inline const Vehicle *GRV(const ResolverObject *object) +{ + return object->scope == VSG_SCOPE_SELF ? object->vehicle.self : object->vehicle.parent; +} + + +static uint32 VehicleGetRandomBits(const ResolverObject *object) +{ + return GRV(object) == NULL ? 0 : GRV(object)->random_bits; +} + + +static uint32 VehicleGetTriggers(const ResolverObject *object) +{ + return GRV(object) == NULL ? 0 : GRV(object)->waiting_triggers; +} + + +static void VehicleSetTriggers(const ResolverObject *object, int triggers) +{ + /* Evil cast to get around const-ness. This used to be achieved by an + * innocent looking function pointer cast... Currently I cannot see a + * way of avoiding this without removing consts deep within gui code. + */ + Vehicle *v = (Vehicle*)GRV(object); + + /* This function must only be called when processing triggers -- any + * other time is an error. */ + assert(object->trigger != 0); + + if (v != NULL) v->waiting_triggers = triggers; +} + + +static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, byte parameter) +{ + const Vehicle *v = GRV(object); + + if (v == NULL) { + /* Vehicle does not exist, so we're in a purchase list */ + switch (variable) { + case 0x43: return _current_player; /* Owner information */ + case 0x46: return 0; /* Motion counter */ + case 0xC4: return _cur_year; /* Build year */ + case 0xDA: return INVALID_VEHICLE; /* Next vehicle */ + default: return -1; + } + } + + /* Calculated vehicle parameters */ + switch (variable) { + case 0x40: /* Get length of consist */ + case 0x41: /* Get length of same consecutive wagons */ + if (v->type != VEH_Train) return 1; + + { + const Vehicle* u; + byte chain_before = 0; + byte chain_after = 0; + + for (u = GetFirstVehicleInChain(v); u != v; u = u->next) { + chain_before++; + if (variable == 0x41 && u->engine_type != v->engine_type) chain_before = 0; + } + + while (u->next != NULL && (variable == 0x40 || u->next->engine_type == v->engine_type)) { + chain_after++; + u = u->next; + } + + return chain_before | chain_after << 8 | (chain_before + chain_after) << 16; + } + + case 0x43: /* Player information */ + return v->owner; + + case 0x46: /* Motion counter */ + return 0; + } + + /* General vehicle properties */ + switch (variable - 0x80) { + case 0x00: return v->type; + case 0x01: return MapOldSubType(v); + case 0x04: return v->index; + case 0x05: return v->index & 0xFF; + case 0x0A: return PackOrder(&v->current_order); + case 0x0B: return PackOrder(&v->current_order) & 0xFF; + case 0x0C: return v->num_orders; + case 0x0D: return v->cur_order_index; + case 0x10: return v->load_unload_time_rem; + case 0x11: return v->load_unload_time_rem & 0xFF; + case 0x12: return v->date_of_last_service; + case 0x13: return v->date_of_last_service & 0xFF; + case 0x14: return v->service_interval; + case 0x15: return v->service_interval & 0xFF; + case 0x16: return v->last_station_visited; + case 0x17: return v->tick_counter; + case 0x18: return v->max_speed; + case 0x19: return v->max_speed & 0xFF; + case 0x1A: return v->x_pos; + case 0x1B: return v->x_pos & 0xFF; + case 0x1C: return v->y_pos; + case 0x1D: return v->y_pos & 0xFF; + case 0x1E: return v->z_pos; + case 0x1F: return v->direction; + case 0x28: return v->cur_image; + case 0x29: return v->cur_image & 0xFF; + case 0x32: return v->vehstatus; + case 0x33: return v->vehstatus; + case 0x34: return v->cur_speed; + case 0x35: return v->cur_speed & 0xFF; + case 0x36: return v->subspeed; + case 0x37: return v->acceleration; + case 0x39: return v->cargo_type; + case 0x3A: return v->cargo_cap; + case 0x3B: return v->cargo_cap & 0xFF; + case 0x3C: return v->cargo_count; + case 0x3D: return v->cargo_count & 0xFF; + case 0x3E: return v->cargo_source; + case 0x3F: return v->cargo_days; + case 0x40: return v->age; + case 0x41: return v->age & 0xFF; + case 0x42: return v->max_age; + case 0x43: return v->max_age & 0xFF; + case 0x44: return v->build_year; + case 0x45: return v->unitnumber; + case 0x46: return v->engine_type; + case 0x47: return v->engine_type & 0xFF; + case 0x48: return v->spritenum; + case 0x49: return v->day_counter; + case 0x4A: return v->breakdowns_since_last_service; + case 0x4B: return v->breakdown_ctr; + case 0x4C: return v->breakdown_delay; + case 0x4D: return v->breakdown_chance; + case 0x4E: return v->reliability; + case 0x4F: return v->reliability & 0xFF; + case 0x50: return v->reliability_spd_dec; + case 0x51: return v->reliability_spd_dec & 0xFF; + case 0x52: return v->profit_this_year; + case 0x53: return v->profit_this_year & 0xFFFFFF; + case 0x54: return v->profit_this_year & 0xFFFF; + case 0x55: return v->profit_this_year & 0xFF; + case 0x56: return v->profit_last_year; + case 0x57: return v->profit_last_year & 0xFF; + case 0x58: return v->profit_last_year; + case 0x59: return v->profit_last_year & 0xFF; + case 0x5A: return v->next == NULL ? INVALID_VEHICLE : v->next->index; + case 0x5C: return v->value; + case 0x5D: return v->value & 0xFFFFFF; + case 0x5E: return v->value & 0xFFFF; + case 0x5F: return v->value & 0xFF; + case 0x60: return v->string_id; + case 0x61: return v->string_id & 0xFF; + case 0x72: return 0; // XXX Refit cycle + case 0x7A: return v->random_bits; + case 0x7B: return v->waiting_triggers; + } + + /* Vehicle specific properties */ switch (v->type) { case VEH_Train: - switch (var) { + switch (variable - 0x80) { case 0x62: return v->u.rail.track; case 0x66: return v->u.rail.railtype; case 0x73: return v->u.rail.cached_veh_length; @@ -164,7 +325,7 @@ static int VehicleSpecificProperty(const Vehicle *v, byte var) { break; case VEH_Road: - switch (var) { + switch (variable - 0x80) { case 0x62: return v->u.road.state; case 0x64: return v->u.road.blocked_ctr; case 0x65: return v->u.road.blocked_ctr & 0xFF; @@ -176,7 +337,7 @@ static int VehicleSpecificProperty(const Vehicle *v, byte var) { break; case VEH_Aircraft: - switch (var) { + switch (variable - 0x80) { // case 0x62: XXX Need to convert from ottd to ttdp state case 0x63: return v->u.air.targetairport; // case 0x66: XXX @@ -184,211 +345,71 @@ static int VehicleSpecificProperty(const Vehicle *v, byte var) { break; } - DEBUG(grf, 1)("Unhandled vehicle property 0x%02X (var 0x%02X), type 0x%02X", var, var + 0x80, v->type); + DEBUG(grf, 1)("Unhandled vehicle property 0x%X, type 0x%X", variable, v->type); return -1; } -typedef SpriteGroup *(*resolve_callback)(const SpriteGroup *spritegroup, - const Vehicle *veh, uint16 callback_info, void *resolve_func); /* XXX data pointer used as function pointer */ -static const SpriteGroup* ResolveVehicleSpriteGroup(const SpriteGroup *spritegroup, - const Vehicle *veh, uint16 callback_info, resolve_callback resolve_func) +static uint32 VehicleResolveReal(const ResolverObject *object, uint num_loaded, uint num_loading, bool *in_motion) { - if (spritegroup == NULL) - return NULL; - - //debug("spgt %d", spritegroup->type); - switch (spritegroup->type) { - case SGT_REAL: - case SGT_CALLBACK: - return spritegroup; - - case SGT_DETERMINISTIC: { - const DeterministicSpriteGroup *dsg = &spritegroup->g.determ; - const SpriteGroup *target; - int value = -1; - // XXX Temporary support - byte variable = dsg->adjusts[0].variable; - - //debug("[%p] Having fun resolving variable %x", veh, variable); - if (variable == 0x0C) { - /* Callback ID */ - value = callback_info & 0xFF; - } else if (variable == 0x10) { - value = (callback_info >> 8) & 0xFF; - } else if ((variable >> 6) == 0) { - /* General property */ - value = GetDeterministicSpriteValue(variable); - } else { - /* Vehicle-specific property. */ - - if (veh == NULL) { - /* We are in a purchase list of something, - * and we are checking for something undefined. - * That means we should get the first target - * (NOT the default one). */ - if (dsg->num_ranges > 0) { - target = dsg->ranges[0].group; - } else { - target = dsg->default_group; - } - return resolve_func(target, NULL, callback_info, resolve_func); - } - - if (dsg->var_scope == VSG_SCOPE_PARENT) { - /* First engine in the vehicle chain */ - if (veh->type == VEH_Train) - veh = GetFirstVehicleInChain(veh); - } + const Vehicle *v = object->vehicle.self; + uint totalsets; + uint set; - if (variable == 0x40 || variable == 0x41) { - if (veh->type == VEH_Train) { - const Vehicle *u = GetFirstVehicleInChain(veh); - byte chain_before = 0, chain_after = 0; - - while (u != veh) { - chain_before++; - if (variable == 0x41 && u->engine_type != veh->engine_type) - chain_before = 0; - u = u->next; - } - while (u->next != NULL && (variable == 0x40 || u->next->engine_type == veh->engine_type)) { - chain_after++; - u = u->next; - }; - - value = chain_before | chain_after << 8 - | (chain_before + chain_after) << 16; - } else { - value = 1; /* 1 vehicle in the chain */ - } - - } else { - // TTDPatch runs on little-endian arch; - // Variable is 0x80 + offset in TTD's vehicle structure - switch (variable - 0x80) { -#define veh_prop(id_, value_) case (id_): value = (value_); break - veh_prop(0x00, veh->type); - veh_prop(0x01, MapOldSubType(veh)); - veh_prop(0x04, veh->index); - veh_prop(0x05, veh->index & 0xFF); - /* XXX? Is THIS right? */ - veh_prop(0x0A, PackOrder(&veh->current_order)); - veh_prop(0x0B, PackOrder(&veh->current_order) & 0xff); - veh_prop(0x0C, veh->num_orders); - veh_prop(0x0D, veh->cur_order_index); - veh_prop(0x10, veh->load_unload_time_rem); - veh_prop(0x11, veh->load_unload_time_rem & 0xFF); - veh_prop(0x12, veh->date_of_last_service); - veh_prop(0x13, veh->date_of_last_service & 0xFF); - veh_prop(0x14, veh->service_interval); - veh_prop(0x15, veh->service_interval & 0xFF); - veh_prop(0x16, veh->last_station_visited); - veh_prop(0x17, veh->tick_counter); - veh_prop(0x18, veh->max_speed); - veh_prop(0x19, veh->max_speed & 0xFF); - veh_prop(0x1A, veh->x_pos); - veh_prop(0x1B, veh->x_pos & 0xFF); - veh_prop(0x1C, veh->y_pos); - veh_prop(0x1D, veh->y_pos & 0xFF); - veh_prop(0x1E, veh->z_pos); - veh_prop(0x1F, veh->direction); - veh_prop(0x28, veh->cur_image); - veh_prop(0x29, veh->cur_image & 0xFF); - veh_prop(0x32, veh->vehstatus); - veh_prop(0x33, veh->vehstatus); - veh_prop(0x34, veh->cur_speed); - veh_prop(0x35, veh->cur_speed & 0xFF); - veh_prop(0x36, veh->subspeed); - veh_prop(0x37, veh->acceleration); - veh_prop(0x39, veh->cargo_type); - veh_prop(0x3A, veh->cargo_cap); - veh_prop(0x3B, veh->cargo_cap & 0xFF); - veh_prop(0x3C, veh->cargo_count); - veh_prop(0x3D, veh->cargo_count & 0xFF); - veh_prop(0x3E, veh->cargo_source); // Probably useless; so what - veh_prop(0x3F, veh->cargo_days); - veh_prop(0x40, veh->age); - veh_prop(0x41, veh->age & 0xFF); - veh_prop(0x42, veh->max_age); - veh_prop(0x43, veh->max_age & 0xFF); - veh_prop(0x44, veh->build_year); - veh_prop(0x45, veh->unitnumber); - veh_prop(0x46, veh->engine_type); - veh_prop(0x47, veh->engine_type & 0xFF); - veh_prop(0x48, veh->spritenum); - veh_prop(0x49, veh->day_counter); - veh_prop(0x4A, veh->breakdowns_since_last_service); - veh_prop(0x4B, veh->breakdown_ctr); - veh_prop(0x4C, veh->breakdown_delay); - veh_prop(0x4D, veh->breakdown_chance); - veh_prop(0x4E, veh->reliability); - veh_prop(0x4F, veh->reliability & 0xFF); - veh_prop(0x50, veh->reliability_spd_dec); - veh_prop(0x51, veh->reliability_spd_dec & 0xFF); - veh_prop(0x52, veh->profit_this_year); - veh_prop(0x53, veh->profit_this_year & 0xFFFFFF); - veh_prop(0x54, veh->profit_this_year & 0xFFFF); - veh_prop(0x55, veh->profit_this_year & 0xFF); - veh_prop(0x56, veh->profit_last_year); - veh_prop(0x57, veh->profit_last_year & 0xFF); - veh_prop(0x58, veh->profit_last_year); - veh_prop(0x59, veh->profit_last_year & 0xFF); - veh_prop(0x5A, veh->next == NULL ? INVALID_VEHICLE : veh->next->index); - veh_prop(0x5C, veh->value); - veh_prop(0x5D, veh->value & 0xFFFFFF); - veh_prop(0x5E, veh->value & 0xFFFF); - veh_prop(0x5F, veh->value & 0xFF); - veh_prop(0x60, veh->string_id); - veh_prop(0x61, veh->string_id & 0xFF); - - veh_prop(0x72, 0); // XXX Refit cycle currently unsupported - veh_prop(0x7A, veh->random_bits); - veh_prop(0x7B, veh->waiting_triggers); -#undef veh_prop - - // Handle vehicle specific properties. - default: value = VehicleSpecificProperty(veh, variable - 0x80); break; - } - } - } + if (v == NULL) { + *in_motion = false; + return 0; + } - target = value != -1 ? EvalDeterministicSpriteGroup(dsg, value) : dsg->default_group; - //debug("Resolved variable %x: %d, %p", dsg->variable, value, callback); - return resolve_func(target, veh, callback_info, resolve_func); - } + if (v->type == VEH_Train) { + *in_motion = GetFirstVehicleInChain(v)->current_order.type != OT_LOADING; + } else { + *in_motion = v->current_order.type != OT_LOADING; + } - case SGT_RANDOMIZED: { - const RandomizedSpriteGroup *rsg = &spritegroup->g.random; + totalsets = in_motion ? num_loaded : num_loading; - if (veh == NULL) { - /* Purchase list of something. Show the first one. */ - assert(rsg->num_groups > 0); - //debug("going for %p: %d", rsg->groups[0], rsg->groups[0].type); - return resolve_func(rsg->groups[0], NULL, callback_info, resolve_func); - } + if (v->cargo_count == v->cargo_cap || totalsets == 1) { + set = totalsets - 1; + } else if (v->cargo_count == 0 || totalsets == 2) { + set = 0; + } else { + set = v->cargo_count * (totalsets - 2) / max(1, v->cargo_cap) + 1; + } - if (rsg->var_scope == VSG_SCOPE_PARENT) { - /* First engine in the vehicle chain */ - if (veh->type == VEH_Train) - veh = GetFirstVehicleInChain(veh); - } + return set; +} - return resolve_func(EvalRandomizedSpriteGroup(rsg, veh->random_bits), veh, callback_info, resolve_func); - } - default: - error("I don't know how to handle such a spritegroup %d!", spritegroup->type); - return NULL; - } +static inline void NewVehicleResolver(ResolverObject *res, const Vehicle *v) +{ + res->GetRandomBits = &VehicleGetRandomBits; + res->GetTriggers = &VehicleGetTriggers; + res->SetTriggers = &VehicleSetTriggers; + res->GetVariable = &VehicleGetVariable; + res->ResolveReal = &VehicleResolveReal; + + res->vehicle.self = v; + res->vehicle.parent = (v != NULL && v->type == VEH_Train) ? GetFirstVehicleInChain(v) : NULL; + + res->callback = 0; + res->callback_param1 = 0; + res->callback_param2 = 0; + res->last_value = 0; + res->trigger = 0; + res->reseed = 0; } -static const SpriteGroup *GetVehicleSpriteGroup(EngineID engine, const Vehicle *v) + +SpriteID GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction direction) { const SpriteGroup *group; + ResolverObject object; CargoID cargo = GC_PURCHASE; + NewVehicleResolver(&object, v); + if (v != NULL) { cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; assert(cargo != GC_INVALID); @@ -402,76 +423,19 @@ static const SpriteGroup *GetVehicleSpriteGroup(EngineID engine, const Vehicle * if (overset != NULL) group = overset; } - return group; -} - -SpriteID GetCustomEngineSprite(EngineID engine, const Vehicle* v, Direction direction) -{ - const SpriteGroup *group; - const RealSpriteGroup *rsg; - CargoID cargo = GC_PURCHASE; - byte loaded = 0; - bool in_motion = 0; - int totalsets, spriteset; - - if (v != NULL) { - int capacity = v->cargo_cap; - - cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; - assert(cargo != GC_INVALID); - - if (capacity == 0) capacity = 1; - loaded = (v->cargo_count * 100) / capacity; - - if (v->type == VEH_Train) { - in_motion = GetFirstVehicleInChain(v)->current_order.type != OT_LOADING; - } else { - in_motion = v->current_order.type != OT_LOADING; - } - } - - group = GetVehicleSpriteGroup(engine, v); - group = ResolveVehicleSpriteGroup(group, v, 0, (resolve_callback) ResolveVehicleSpriteGroup); + group = Resolve(group, &object); - if (group == NULL && cargo != GC_DEFAULT) { + if ((group == NULL || group->type != SGT_RESULT) && cargo != GC_DEFAULT) { // This group is empty but perhaps there'll be a default one. - group = ResolveVehicleSpriteGroup(engine_custom_sprites[engine][GC_DEFAULT], v, 0, - (resolve_callback) ResolveVehicleSpriteGroup); - } - - if (group == NULL) - return 0; - - assert(group->type == SGT_REAL); - rsg = &group->g.real; - - // This group is empty. This function users should therefore - // look up the sprite number in _engine_original_sprites. - if (rsg->num_loaded == 0 || rsg->num_loading == 0) return 0; - - totalsets = in_motion ? rsg->num_loaded : rsg->num_loading; - - // My aim here is to make it possible to visually determine absolutely - // empty and totally full vehicles. --pasky - if (loaded == 100 || totalsets == 1) { // full - spriteset = totalsets - 1; - } else if (loaded == 0 || totalsets == 2) { // empty - spriteset = 0; - } else { // something inbetween - spriteset = loaded * (totalsets - 2) / 100 + 1; - // correct possible rounding errors - if (!spriteset) - spriteset = 1; - else if (spriteset == totalsets - 1) - spriteset--; + group = Resolve(engine_custom_sprites[engine][GC_DEFAULT], &object); } - group = in_motion ? rsg->loaded[spriteset] : rsg->loading[spriteset]; - if (group->type != SGT_RESULT) return 0; + if (group == NULL || group->type != SGT_RESULT) return 0; return group->g.result.sprite + (direction % group->g.result.num_sprites); } + /** * Check if a wagon is currently using a wagon override * @param v The wagon to check @@ -495,8 +459,14 @@ bool UsesWagonOverride(const Vehicle* v) uint16 GetVehicleCallback(byte callback, uint32 param1, uint32 param2, EngineID engine, const Vehicle *v) { const SpriteGroup *group; + ResolverObject object; CargoID cargo; - uint16 callback_info = callback | (param1 << 8); // XXX Temporary conversion between new and old format. + + NewVehicleResolver(&object, v); + + object.callback = callback; + object.callback_param1 = param1; + object.callback_param2 = param2; cargo = (v == NULL) ? GC_PURCHASE : _global_cargo_id[_opt.landscape][v->cargo_type]; @@ -508,12 +478,11 @@ uint16 GetVehicleCallback(byte callback, uint32 param1, uint32 param2, EngineID if (overset != NULL) group = overset; } - group = ResolveVehicleSpriteGroup(group, v, callback_info, (resolve_callback) ResolveVehicleSpriteGroup); + group = Resolve(group, &object); if ((group == NULL || group->type != SGT_CALLBACK) && cargo != GC_DEFAULT) { // This group is empty but perhaps there'll be a default one. - group = ResolveVehicleSpriteGroup(engine_custom_sprites[engine][GC_DEFAULT], v, callback_info, - (resolve_callback) ResolveVehicleSpriteGroup); + group = Resolve(engine_custom_sprites[engine][GC_DEFAULT], &object); } if (group == NULL || group->type != SGT_CALLBACK) @@ -523,55 +492,40 @@ uint16 GetVehicleCallback(byte callback, uint32 param1, uint32 param2, EngineID } - -// Global variables are evil, yes, but we would end up with horribly overblown -// calling convention otherwise and this should be 100% reentrant. -static byte _vsg_random_triggers; -static byte _vsg_bits_to_reseed; - -static const SpriteGroup *TriggerVehicleSpriteGroup(const SpriteGroup *spritegroup, - Vehicle *veh, uint16 callback_info, resolve_callback resolve_func) -{ - if (spritegroup == NULL) - return NULL; - - if (spritegroup->type == SGT_RANDOMIZED) { - _vsg_bits_to_reseed |= RandomizedSpriteGroupTriggeredBits( - &spritegroup->g.random, - _vsg_random_triggers, - &veh->waiting_triggers - ); - } - - return ResolveVehicleSpriteGroup(spritegroup, veh, callback_info, resolve_func); -} - static void DoTriggerVehicle(Vehicle *v, VehicleTrigger trigger, byte base_random_bits, bool first) { const SpriteGroup *group; - const RealSpriteGroup *rsg; + ResolverObject object; + CargoID cargo; byte new_random_bits; - _vsg_random_triggers = trigger; - _vsg_bits_to_reseed = 0; - group = TriggerVehicleSpriteGroup(GetVehicleSpriteGroup(v->engine_type, v), v, 0, - (resolve_callback) TriggerVehicleSpriteGroup); + /* We can't trigger a non-existent vehicle... */ + assert(v != NULL); - if (group == NULL && v->cargo_type != GC_DEFAULT) { - // This group turned out to be empty but perhaps there'll be a default one. - group = TriggerVehicleSpriteGroup(engine_custom_sprites[v->engine_type][GC_DEFAULT], v, 0, - (resolve_callback) TriggerVehicleSpriteGroup); + NewVehicleResolver(&object, v); + + object.trigger = trigger; + + cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; + group = engine_custom_sprites[v->engine_type][cargo]; + + if (v->type == VEH_Train) { + const SpriteGroup *overset = GetWagonOverrideSpriteSet(v->engine_type, v->u.rail.first_engine); + if (overset != NULL) group = overset; } - if (group == NULL) - return; + group = Resolve(group, &object); + if (group == NULL && v->cargo_type != GC_DEFAULT) { + // This group is empty but perhaps there'll be a default one. + group = Resolve(engine_custom_sprites[v->engine_type][GC_DEFAULT], &object); + } - assert(group->type == SGT_REAL); - rsg = &group->g.real; + /* Really return? */ + if (group == NULL) return; new_random_bits = Random(); - v->random_bits &= ~_vsg_bits_to_reseed; - v->random_bits |= (first ? new_random_bits : base_random_bits) & _vsg_bits_to_reseed; + v->random_bits &= ~object.reseed; + v->random_bits |= (first ? new_random_bits : base_random_bits) & object.reseed; switch (trigger) { case VEHICLE_TRIGGER_NEW_CARGO: |