mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-01-29 02:12:11 +00:00
(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)
This commit is contained in:
parent
376bda7007
commit
7273bd490d
562
newgrf_engine.c
562
newgrf_engine.c
@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "openttd.h"
|
#include "openttd.h"
|
||||||
|
#include "variables.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "functions.h"
|
#include "functions.h"
|
||||||
#include "string.h"
|
|
||||||
#include "strings.h"
|
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
|
#include "train.h"
|
||||||
|
#include "player.h"
|
||||||
#include "newgrf_callbacks.h"
|
#include "newgrf_callbacks.h"
|
||||||
#include "newgrf_engine.h"
|
#include "newgrf_engine.h"
|
||||||
#include "sprite.h"
|
#include "newgrf_station.h"
|
||||||
#include "variables.h"
|
#include "newgrf_spritegroup.h"
|
||||||
#include "train.h"
|
|
||||||
|
|
||||||
// TODO: We don't support cargo-specific wagon overrides. Pretty exotic... ;-) --pasky
|
// TODO: We don't support cargo-specific wagon overrides. Pretty exotic... ;-) --pasky
|
||||||
|
|
||||||
@ -147,10 +147,171 @@ static int MapOldSubType(const Vehicle *v)
|
|||||||
return 2;
|
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) {
|
switch (v->type) {
|
||||||
case VEH_Train:
|
case VEH_Train:
|
||||||
switch (var) {
|
switch (variable - 0x80) {
|
||||||
case 0x62: return v->u.rail.track;
|
case 0x62: return v->u.rail.track;
|
||||||
case 0x66: return v->u.rail.railtype;
|
case 0x66: return v->u.rail.railtype;
|
||||||
case 0x73: return v->u.rail.cached_veh_length;
|
case 0x73: return v->u.rail.cached_veh_length;
|
||||||
@ -164,7 +325,7 @@ static int VehicleSpecificProperty(const Vehicle *v, byte var) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VEH_Road:
|
case VEH_Road:
|
||||||
switch (var) {
|
switch (variable - 0x80) {
|
||||||
case 0x62: return v->u.road.state;
|
case 0x62: return v->u.road.state;
|
||||||
case 0x64: return v->u.road.blocked_ctr;
|
case 0x64: return v->u.road.blocked_ctr;
|
||||||
case 0x65: return v->u.road.blocked_ctr & 0xFF;
|
case 0x65: return v->u.road.blocked_ctr & 0xFF;
|
||||||
@ -176,7 +337,7 @@ static int VehicleSpecificProperty(const Vehicle *v, byte var) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VEH_Aircraft:
|
case VEH_Aircraft:
|
||||||
switch (var) {
|
switch (variable - 0x80) {
|
||||||
// case 0x62: XXX Need to convert from ottd to ttdp state
|
// case 0x62: XXX Need to convert from ottd to ttdp state
|
||||||
case 0x63: return v->u.air.targetairport;
|
case 0x63: return v->u.air.targetairport;
|
||||||
// case 0x66: XXX
|
// case 0x66: XXX
|
||||||
@ -184,211 +345,71 @@ static int VehicleSpecificProperty(const Vehicle *v, byte var) {
|
|||||||
break;
|
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;
|
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,
|
static uint32 VehicleResolveReal(const ResolverObject *object, uint num_loaded, uint num_loading, bool *in_motion)
|
||||||
const Vehicle *veh, uint16 callback_info, resolve_callback resolve_func)
|
|
||||||
{
|
{
|
||||||
if (spritegroup == NULL)
|
const Vehicle *v = object->vehicle.self;
|
||||||
return NULL;
|
uint totalsets;
|
||||||
|
uint set;
|
||||||
|
|
||||||
//debug("spgt %d", spritegroup->type);
|
if (v == NULL) {
|
||||||
switch (spritegroup->type) {
|
*in_motion = false;
|
||||||
case SGT_REAL:
|
return 0;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
case SGT_RANDOMIZED: {
|
|
||||||
const RandomizedSpriteGroup *rsg = &spritegroup->g.random;
|
|
||||||
|
|
||||||
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 (rsg->var_scope == VSG_SCOPE_PARENT) {
|
|
||||||
/* First engine in the vehicle chain */
|
|
||||||
if (veh->type == VEH_Train)
|
|
||||||
veh = GetFirstVehicleInChain(veh);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (v->type == VEH_Train) {
|
||||||
|
*in_motion = GetFirstVehicleInChain(v)->current_order.type != OT_LOADING;
|
||||||
|
} else {
|
||||||
|
*in_motion = v->current_order.type != OT_LOADING;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalsets = in_motion ? num_loaded : num_loading;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const SpriteGroup *GetVehicleSpriteGroup(EngineID engine, const Vehicle *v)
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SpriteID GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction direction)
|
||||||
{
|
{
|
||||||
const SpriteGroup *group;
|
const SpriteGroup *group;
|
||||||
|
ResolverObject object;
|
||||||
CargoID cargo = GC_PURCHASE;
|
CargoID cargo = GC_PURCHASE;
|
||||||
|
|
||||||
|
NewVehicleResolver(&object, v);
|
||||||
|
|
||||||
if (v != NULL) {
|
if (v != NULL) {
|
||||||
cargo = _global_cargo_id[_opt.landscape][v->cargo_type];
|
cargo = _global_cargo_id[_opt.landscape][v->cargo_type];
|
||||||
assert(cargo != GC_INVALID);
|
assert(cargo != GC_INVALID);
|
||||||
@ -402,76 +423,19 @@ static const SpriteGroup *GetVehicleSpriteGroup(EngineID engine, const Vehicle *
|
|||||||
if (overset != NULL) group = overset;
|
if (overset != NULL) group = overset;
|
||||||
}
|
}
|
||||||
|
|
||||||
return group;
|
group = Resolve(group, &object);
|
||||||
}
|
|
||||||
|
|
||||||
SpriteID GetCustomEngineSprite(EngineID engine, const Vehicle* v, Direction direction)
|
if ((group == NULL || group->type != SGT_RESULT) && cargo != GC_DEFAULT) {
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (group == NULL && cargo != GC_DEFAULT) {
|
|
||||||
// This group is empty but perhaps there'll be a default one.
|
// This group is empty but perhaps there'll be a default one.
|
||||||
group = ResolveVehicleSpriteGroup(engine_custom_sprites[engine][GC_DEFAULT], v, 0,
|
group = Resolve(engine_custom_sprites[engine][GC_DEFAULT], &object);
|
||||||
(resolve_callback) ResolveVehicleSpriteGroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group == NULL)
|
if (group == NULL || group->type != SGT_RESULT) return 0;
|
||||||
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 = in_motion ? rsg->loaded[spriteset] : rsg->loading[spriteset];
|
|
||||||
if (group->type != SGT_RESULT) return 0;
|
|
||||||
|
|
||||||
return group->g.result.sprite + (direction % group->g.result.num_sprites);
|
return group->g.result.sprite + (direction % group->g.result.num_sprites);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a wagon is currently using a wagon override
|
* Check if a wagon is currently using a wagon override
|
||||||
* @param v The wagon to check
|
* @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)
|
uint16 GetVehicleCallback(byte callback, uint32 param1, uint32 param2, EngineID engine, const Vehicle *v)
|
||||||
{
|
{
|
||||||
const SpriteGroup *group;
|
const SpriteGroup *group;
|
||||||
|
ResolverObject object;
|
||||||
CargoID cargo;
|
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];
|
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;
|
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) {
|
if ((group == NULL || group->type != SGT_CALLBACK) && cargo != GC_DEFAULT) {
|
||||||
// This group is empty but perhaps there'll be a default one.
|
// This group is empty but perhaps there'll be a default one.
|
||||||
group = ResolveVehicleSpriteGroup(engine_custom_sprites[engine][GC_DEFAULT], v, callback_info,
|
group = Resolve(engine_custom_sprites[engine][GC_DEFAULT], &object);
|
||||||
(resolve_callback) ResolveVehicleSpriteGroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group == NULL || group->type != SGT_CALLBACK)
|
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)
|
static void DoTriggerVehicle(Vehicle *v, VehicleTrigger trigger, byte base_random_bits, bool first)
|
||||||
{
|
{
|
||||||
const SpriteGroup *group;
|
const SpriteGroup *group;
|
||||||
const RealSpriteGroup *rsg;
|
ResolverObject object;
|
||||||
|
CargoID cargo;
|
||||||
byte new_random_bits;
|
byte new_random_bits;
|
||||||
|
|
||||||
_vsg_random_triggers = trigger;
|
/* We can't trigger a non-existent vehicle... */
|
||||||
_vsg_bits_to_reseed = 0;
|
assert(v != NULL);
|
||||||
group = TriggerVehicleSpriteGroup(GetVehicleSpriteGroup(v->engine_type, v), v, 0,
|
|
||||||
(resolve_callback) TriggerVehicleSpriteGroup);
|
|
||||||
|
|
||||||
if (group == NULL && v->cargo_type != GC_DEFAULT) {
|
NewVehicleResolver(&object, v);
|
||||||
// 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,
|
object.trigger = trigger;
|
||||||
(resolve_callback) TriggerVehicleSpriteGroup);
|
|
||||||
|
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)
|
group = Resolve(group, &object);
|
||||||
return;
|
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);
|
/* Really return? */
|
||||||
rsg = &group->g.real;
|
if (group == NULL) return;
|
||||||
|
|
||||||
new_random_bits = Random();
|
new_random_bits = Random();
|
||||||
v->random_bits &= ~_vsg_bits_to_reseed;
|
v->random_bits &= ~object.reseed;
|
||||||
v->random_bits |= (first ? new_random_bits : base_random_bits) & _vsg_bits_to_reseed;
|
v->random_bits |= (first ? new_random_bits : base_random_bits) & object.reseed;
|
||||||
|
|
||||||
switch (trigger) {
|
switch (trigger) {
|
||||||
case VEHICLE_TRIGGER_NEW_CARGO:
|
case VEHICLE_TRIGGER_NEW_CARGO:
|
||||||
|
@ -71,7 +71,7 @@ void InitializeSpriteGroupPool(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const SpriteGroup *ResolveReal(const SpriteGroup *group, ResolverObject *object)
|
static inline const SpriteGroup *ResolveReal(const SpriteGroup *group, ResolverObject *object)
|
||||||
{
|
{
|
||||||
bool in_motion;
|
bool in_motion;
|
||||||
uint set;
|
uint set;
|
||||||
@ -84,7 +84,7 @@ static const SpriteGroup *ResolveReal(const SpriteGroup *group, ResolverObject *
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static uint32 GetVariable(const ResolverObject *object, byte variable, byte parameter)
|
static inline uint32 GetVariable(const ResolverObject *object, byte variable, byte parameter)
|
||||||
{
|
{
|
||||||
/* Return common variables */
|
/* Return common variables */
|
||||||
switch (variable) {
|
switch (variable) {
|
||||||
@ -112,7 +112,7 @@ static uint32 GetVariable(const ResolverObject *object, byte variable, byte para
|
|||||||
/* Evaluate an adjustment for a variable of the given size. This is a bit of
|
/* Evaluate an adjustment for a variable of the given size. This is a bit of
|
||||||
* an unwieldy macro, but it saves triplicating the code. */
|
* an unwieldy macro, but it saves triplicating the code. */
|
||||||
#define BUILD_EVAL_ADJUST(size, usize) \
|
#define BUILD_EVAL_ADJUST(size, usize) \
|
||||||
static size EvalAdjust_ ## size(const DeterministicSpriteGroupAdjust *adjust, size last_value, size value) \
|
static inline size EvalAdjust_ ## size(const DeterministicSpriteGroupAdjust *adjust, size last_value, size value) \
|
||||||
{ \
|
{ \
|
||||||
value >>= adjust->shift_num; \
|
value >>= adjust->shift_num; \
|
||||||
value &= adjust->and_mask; \
|
value &= adjust->and_mask; \
|
||||||
@ -150,7 +150,7 @@ BUILD_EVAL_ADJUST(int16, uint16)
|
|||||||
BUILD_EVAL_ADJUST(int32, uint32)
|
BUILD_EVAL_ADJUST(int32, uint32)
|
||||||
|
|
||||||
|
|
||||||
static const SpriteGroup *ResolveVariable(const SpriteGroup *group, ResolverObject *object)
|
static inline const SpriteGroup *ResolveVariable(const SpriteGroup *group, ResolverObject *object)
|
||||||
{
|
{
|
||||||
static SpriteGroup nvarzero;
|
static SpriteGroup nvarzero;
|
||||||
const SpriteGroup *target;
|
const SpriteGroup *target;
|
||||||
@ -196,7 +196,7 @@ static const SpriteGroup *ResolveVariable(const SpriteGroup *group, ResolverObje
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const SpriteGroup *ResolveRandom(const SpriteGroup *group, ResolverObject *object)
|
static inline const SpriteGroup *ResolveRandom(const SpriteGroup *group, ResolverObject *object)
|
||||||
{
|
{
|
||||||
byte mask;
|
byte mask;
|
||||||
byte index;
|
byte index;
|
||||||
|
Loading…
Reference in New Issue
Block a user