mirror of https://github.com/OpenTTD/OpenTTD
(svn r654) Hopefully complete support for randomized variational spritegroups (i.e. the cars transporter in DBSetXL gets different cars each time) (pasky)
parent
0086bb9d06
commit
13f0b6c0cf
|
@ -1148,6 +1148,8 @@ static void AircraftEnterHangar(Vehicle *v)
|
|||
|
||||
MaybeRenewVehicle(v, EstimateAircraftCost(v->engine_type));
|
||||
|
||||
TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
|
||||
|
||||
if ((v->next_order & OT_MASK) == OT_GOTO_DEPOT) {
|
||||
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
||||
|
||||
|
|
21
economy.c
21
economy.c
|
@ -13,6 +13,7 @@
|
|||
#include "town.h"
|
||||
#include "network.h"
|
||||
#include "sound.h"
|
||||
#include "engine.h"
|
||||
|
||||
void UpdatePlayerHouse(Player *p, uint score)
|
||||
{
|
||||
|
@ -1210,6 +1211,7 @@ int LoadUnloadVehicle(Vehicle *v)
|
|||
int t;
|
||||
uint count, cap;
|
||||
byte old_player;
|
||||
bool completely_empty = true;
|
||||
|
||||
assert((v->next_order&0x1F) == OT_LOADING);
|
||||
|
||||
|
@ -1253,6 +1255,9 @@ int LoadUnloadVehicle(Vehicle *v)
|
|||
result |= 2;
|
||||
v->cargo_count = 0;
|
||||
}
|
||||
|
||||
if (v->cargo_count != 0)
|
||||
completely_empty = false;
|
||||
}
|
||||
|
||||
/* don't pick up goods that we unloaded */
|
||||
|
@ -1272,6 +1277,18 @@ int LoadUnloadVehicle(Vehicle *v)
|
|||
// has capacity for it, load it on the vehicle.
|
||||
if ((count=ge->waiting_acceptance & 0xFFF) != 0 &&
|
||||
(cap = v->cargo_cap - v->cargo_count) != 0) {
|
||||
if (v->cargo_count == 0)
|
||||
TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
|
||||
|
||||
/* TODO: Regarding this, when we do gradual loading, we
|
||||
* should first unload all vehicles and then start
|
||||
* loading them. Since this will cause
|
||||
* VEHICLE_TRIGGER_EMPTY to be called at the time when
|
||||
* the whole vehicle chain is really totally empty, the
|
||||
* @completely_empty assignment can then be safely
|
||||
* removed; that's how TTDPatch behaves too. --pasky */
|
||||
completely_empty = false;
|
||||
|
||||
if (cap > count) cap = count;
|
||||
v->cargo_count += cap;
|
||||
ge->waiting_acceptance -= cap;
|
||||
|
@ -1304,6 +1321,10 @@ next_vehicle:;
|
|||
|
||||
v->load_unload_time_rem = unloading_time;
|
||||
|
||||
if (completely_empty) {
|
||||
TriggerVehicle(v, VEHICLE_TRIGGER_EMPTY);
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
||||
|
||||
|
|
152
engine.c
152
engine.c
|
@ -243,9 +243,15 @@ void SetCustomEngineSprites(byte engine, byte cargo, struct SpriteGroup *group)
|
|||
_engine_custom_sprites[engine][cargo] = *group;
|
||||
}
|
||||
|
||||
typedef struct RealSpriteGroup *(*resolve_callback)(struct SpriteGroup *spritegroup,
|
||||
struct Vehicle *veh,
|
||||
void *callback); /* XXX data pointer used as function pointer */
|
||||
|
||||
static struct RealSpriteGroup *
|
||||
ResolveVehicleSpriteGroup(struct SpriteGroup *spritegroup, struct Vehicle *veh)
|
||||
ResolveVehicleSpriteGroup(struct SpriteGroup *spritegroup, struct Vehicle *veh,
|
||||
resolve_callback callback)
|
||||
{
|
||||
//debug("spgt %d", spritegroup->type);
|
||||
switch (spritegroup->type) {
|
||||
case SGT_REAL:
|
||||
return &spritegroup->g.real;
|
||||
|
@ -274,7 +280,7 @@ ResolveVehicleSpriteGroup(struct SpriteGroup *spritegroup, struct Vehicle *veh)
|
|||
} else {
|
||||
target = dsg->default_group;
|
||||
}
|
||||
return ResolveVehicleSpriteGroup(target, NULL);
|
||||
return callback(target, NULL, callback);
|
||||
}
|
||||
|
||||
if (dsg->var_scope == VSG_SCOPE_PARENT) {
|
||||
|
@ -393,33 +399,44 @@ ResolveVehicleSpriteGroup(struct SpriteGroup *spritegroup, struct Vehicle *veh)
|
|||
}
|
||||
|
||||
target = value != -1 ? EvalDeterministicSpriteGroup(dsg, value) : dsg->default_group;
|
||||
//debug("Resolved variable %x: %d", dsg->variable, value);
|
||||
return ResolveVehicleSpriteGroup(target, veh);
|
||||
//debug("Resolved variable %x: %d, %p", dsg->variable, value, callback);
|
||||
return callback(target, veh, callback);
|
||||
}
|
||||
|
||||
case SGT_RANDOMIZED: {
|
||||
struct 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 callback(&rsg->groups[0], NULL, callback);
|
||||
}
|
||||
|
||||
if (rsg->var_scope == VSG_SCOPE_PARENT) {
|
||||
/* First engine in the vehicle chain */
|
||||
if (veh->type == VEH_Train)
|
||||
veh = GetFirstVehicleInChain(veh);
|
||||
}
|
||||
|
||||
return callback(EvalRandomizedSpriteGroup(rsg, veh->random_bits), veh, callback);
|
||||
}
|
||||
|
||||
default:
|
||||
case SGT_RANDOM:
|
||||
error("I don't know how to handle random spritegroups yet!");
|
||||
error("I don't know how to handle such a spritegroup %d!", spritegroup->type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int GetCustomEngineSprite(byte engine, Vehicle *v, byte direction)
|
||||
static struct SpriteGroup *GetVehicleSpriteGroup(byte engine, Vehicle *v)
|
||||
{
|
||||
struct SpriteGroup *group;
|
||||
struct RealSpriteGroup *rsg;
|
||||
uint16 overriding_engine = -1;
|
||||
byte cargo = CID_PURCHASE;
|
||||
byte loaded = 0;
|
||||
byte in_motion = 0;
|
||||
int totalsets, spriteset;
|
||||
int r;
|
||||
|
||||
if (v != NULL) {
|
||||
overriding_engine = v->type == VEH_Train ? v->u.rail.first_engine : -1;
|
||||
cargo = _global_cargo_id[_opt.landscape][v->cargo_type];
|
||||
loaded = ((v->cargo_count + 1) * 100) / (v->cargo_cap + 1);
|
||||
in_motion = !!v->cur_speed;
|
||||
}
|
||||
|
||||
group = &_engine_custom_sprites[engine][cargo];
|
||||
|
@ -431,11 +448,35 @@ int GetCustomEngineSprite(byte engine, Vehicle *v, byte direction)
|
|||
if (overset) group = overset;
|
||||
}
|
||||
|
||||
rsg = ResolveVehicleSpriteGroup(group, v);
|
||||
return group;
|
||||
}
|
||||
|
||||
int GetCustomEngineSprite(byte engine, Vehicle *v, byte direction)
|
||||
{
|
||||
struct SpriteGroup *group;
|
||||
struct RealSpriteGroup *rsg;
|
||||
byte cargo = CID_PURCHASE;
|
||||
byte loaded = 0;
|
||||
bool in_motion = 0;
|
||||
int totalsets, spriteset;
|
||||
int r;
|
||||
|
||||
if (v != NULL) {
|
||||
int capacity = v->cargo_cap;
|
||||
|
||||
cargo = _global_cargo_id[_opt.landscape][v->cargo_type];
|
||||
if (capacity == 0) capacity = 1;
|
||||
loaded = (v->cargo_count * 100) / capacity;
|
||||
in_motion = (v->cur_speed != 0);
|
||||
}
|
||||
|
||||
group = GetVehicleSpriteGroup(engine, v);
|
||||
rsg = ResolveVehicleSpriteGroup(group, v, (resolve_callback) ResolveVehicleSpriteGroup);
|
||||
|
||||
if (rsg->sprites_per_set == 0 && cargo != 29) { /* XXX magic number */
|
||||
// This group is empty but perhaps there'll be a default one.
|
||||
rsg = ResolveVehicleSpriteGroup(&_engine_custom_sprites[engine][29], v);
|
||||
rsg = ResolveVehicleSpriteGroup(&_engine_custom_sprites[engine][29], v,
|
||||
(resolve_callback) ResolveVehicleSpriteGroup);
|
||||
}
|
||||
|
||||
if (!rsg->sprites_per_set) {
|
||||
|
@ -470,6 +511,85 @@ int GetCustomEngineSprite(byte engine, Vehicle *v, byte direction)
|
|||
}
|
||||
|
||||
|
||||
// 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;
|
||||
|
||||
extern int _custom_sprites_base;
|
||||
|
||||
static struct RealSpriteGroup *
|
||||
TriggerVehicleSpriteGroup(struct SpriteGroup *spritegroup, struct Vehicle *veh,
|
||||
resolve_callback callback)
|
||||
{
|
||||
if (spritegroup->type == SGT_RANDOMIZED)
|
||||
_vsg_bits_to_reseed |= RandomizedSpriteGroupTriggeredBits(&spritegroup->g.random,
|
||||
_vsg_random_triggers,
|
||||
&veh->waiting_triggers);
|
||||
|
||||
return ResolveVehicleSpriteGroup(spritegroup, veh, callback);
|
||||
}
|
||||
|
||||
static void DoTriggerVehicle(Vehicle *veh, enum VehicleTrigger trigger, byte base_random_bits, bool first)
|
||||
{
|
||||
struct RealSpriteGroup *rsg;
|
||||
byte new_random_bits;
|
||||
|
||||
_vsg_random_triggers = trigger;
|
||||
_vsg_bits_to_reseed = 0;
|
||||
rsg = TriggerVehicleSpriteGroup(GetVehicleSpriteGroup(veh->engine_type, veh), veh,
|
||||
(resolve_callback) TriggerVehicleSpriteGroup);
|
||||
if (rsg->sprites_per_set == 0 && veh->cargo_type != 29) { /* XXX magic number */
|
||||
// This group turned out to be empty but perhaps there'll be a default one.
|
||||
rsg = TriggerVehicleSpriteGroup(&_engine_custom_sprites[veh->engine_type][29], veh,
|
||||
(resolve_callback) TriggerVehicleSpriteGroup);
|
||||
}
|
||||
veh->random_bits &= ~_vsg_bits_to_reseed;
|
||||
veh->random_bits |= (first ? (new_random_bits = Random()) : base_random_bits) & _vsg_bits_to_reseed;
|
||||
|
||||
switch (trigger) {
|
||||
case VEHICLE_TRIGGER_NEW_CARGO:
|
||||
/* All vehicles in chain get ANY_NEW_CARGO trigger now.
|
||||
* So we call it for the first one and they will recurse. */
|
||||
/* Indexing part of vehicle random bits needs to be
|
||||
* same for all triggered vehicles in the chain (to get
|
||||
* all the random-cargo wagons carry the same cargo,
|
||||
* i.e.), so we give them all the NEW_CARGO triggered
|
||||
* vehicle's portion of random bits. */
|
||||
assert(first);
|
||||
DoTriggerVehicle(GetFirstVehicleInChain(veh), VEHICLE_TRIGGER_ANY_NEW_CARGO, new_random_bits, false);
|
||||
break;
|
||||
case VEHICLE_TRIGGER_DEPOT:
|
||||
/* We now trigger the next vehicle in chain recursively.
|
||||
* The random bits portions may be different for each
|
||||
* vehicle in chain. */
|
||||
if (veh->next != NULL)
|
||||
DoTriggerVehicle(veh->next, trigger, 0, true);
|
||||
break;
|
||||
case VEHICLE_TRIGGER_EMPTY:
|
||||
/* We now trigger the next vehicle in chain
|
||||
* recursively. The random bits portions must be same
|
||||
* for each vehicle in chain, so we give them all
|
||||
* first chained vehicle's portion of random bits. */
|
||||
if (veh->next != NULL)
|
||||
DoTriggerVehicle(veh->next, trigger, first ? new_random_bits : base_random_bits, false);
|
||||
break;
|
||||
case VEHICLE_TRIGGER_ANY_NEW_CARGO:
|
||||
/* Now pass the trigger recursively to the next vehicle
|
||||
* in chain. */
|
||||
assert(!first);
|
||||
if (veh->next != NULL)
|
||||
DoTriggerVehicle(veh->next, VEHICLE_TRIGGER_ANY_NEW_CARGO, base_random_bits, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerVehicle(Vehicle *veh, enum VehicleTrigger trigger)
|
||||
{
|
||||
DoTriggerVehicle(veh, trigger, 0, true);
|
||||
}
|
||||
|
||||
|
||||
static char *_engine_custom_names[256];
|
||||
|
||||
void SetCustomEngineName(int engine, char *name)
|
||||
|
|
11
engine.h
11
engine.h
|
@ -101,6 +101,17 @@ int GetCustomEngineSprite(byte engine, Vehicle *v, byte direction);
|
|||
#define GetCustomVehicleSprite(v, direction) GetCustomEngineSprite(v->engine_type, v, direction)
|
||||
#define GetCustomVehicleIcon(et, direction) GetCustomEngineSprite(et, NULL, direction)
|
||||
|
||||
enum VehicleTrigger {
|
||||
VEHICLE_TRIGGER_NEW_CARGO = 1,
|
||||
// Externally triggered only for the first vehicle in chain
|
||||
VEHICLE_TRIGGER_DEPOT = 2,
|
||||
// Externally triggered only for the first vehicle in chain, only if whole chain is empty
|
||||
VEHICLE_TRIGGER_EMPTY = 4,
|
||||
// Not triggered externally (called for the whole chain if we got NEW_CARGO)
|
||||
VEHICLE_TRIGGER_ANY_NEW_CARGO = 8,
|
||||
};
|
||||
void TriggerVehicle(Vehicle *veh, enum VehicleTrigger trigger);
|
||||
|
||||
void SetCustomEngineName(int engine, char *name);
|
||||
StringID GetCustomEngineName(int engine);
|
||||
|
||||
|
|
44
grfspecial.c
44
grfspecial.c
|
@ -1175,8 +1175,48 @@ static void NewSpriteGroup(byte *buf, int len)
|
|||
|
||||
return;
|
||||
|
||||
} else if (numloaded & 0x80) {
|
||||
grfmsg(GMS_WARN, "NewSpriteGroup(0x%x): Unsupported special group.", numloaded);
|
||||
} else if (numloaded == 0x80 || numloaded == 0x83) {
|
||||
struct RandomizedSpriteGroup *rg;
|
||||
int i;
|
||||
|
||||
/* This stuff is getting actually evaluated in
|
||||
* EvalRandomizedSpriteGroup(). */
|
||||
|
||||
buf += 4;
|
||||
len -= 4;
|
||||
check_length(len, 6, "NewSpriteGroup 0x80/0x83");
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
group = &_cur_grffile->spritegroups[setid];
|
||||
memset(group, 0, sizeof(*group));
|
||||
group->type = SGT_RANDOMIZED;
|
||||
rg = &group->g.random;
|
||||
|
||||
/* 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 */
|
||||
|
||||
rg->var_scope = numloaded == 0x83 ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
|
||||
|
||||
rg->triggers = grf_load_byte(&buf);
|
||||
rg->cmp_mode = rg->triggers & 0x80;
|
||||
rg->triggers &= 0x7F;
|
||||
|
||||
rg->lowest_randbit = grf_load_byte(&buf);
|
||||
rg->num_groups = grf_load_byte(&buf);
|
||||
|
||||
rg->groups = calloc(rg->num_groups, sizeof(*rg->groups));
|
||||
for (i = 0; i < rg->num_groups; i++) {
|
||||
uint16 groupid = grf_load_word(&buf);
|
||||
/* XXX: If multiple surreal sets attach a surreal
|
||||
* set this way, we are in trouble. */
|
||||
rg->groups[i] = _cur_grffile->spritegroups[groupid];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1366,6 +1366,7 @@ void RoadVehEnterDepot(Vehicle *v)
|
|||
|
||||
MaybeRenewVehicle(v, EstimateRoadVehCost(v->engine_type));
|
||||
|
||||
TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
|
||||
|
||||
if ((v->next_order&OT_MASK) == OT_GOTO_DEPOT) {
|
||||
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
||||
|
|
|
@ -392,6 +392,8 @@ static void ShipEnterDepot(Vehicle *v)
|
|||
|
||||
MaybeRenewVehicle(v, EstimateShipCost(v->engine_type));
|
||||
|
||||
TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
|
||||
|
||||
if ((v->next_order&OT_MASK) == OT_GOTO_DEPOT) {
|
||||
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
||||
|
||||
|
|
37
sprite.c
37
sprite.c
|
@ -60,3 +60,40 @@ int GetDeterministicSpriteValue(byte var)
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
struct SpriteGroup *
|
||||
EvalRandomizedSpriteGroup(struct RandomizedSpriteGroup *rsg, byte random_bits)
|
||||
{
|
||||
byte mask;
|
||||
byte index;
|
||||
|
||||
/* Noone likes mangling with bits, but you don't get around it here.
|
||||
* Sorry. --pasky */
|
||||
// rsg->num_groups is always power of 2
|
||||
mask = (rsg->num_groups - 1) << rsg->lowest_randbit;
|
||||
index = (random_bits & mask) >> rsg->lowest_randbit;
|
||||
assert(index < rsg->num_groups);
|
||||
return &rsg->groups[index];
|
||||
}
|
||||
|
||||
byte RandomizedSpriteGroupTriggeredBits(struct RandomizedSpriteGroup *rsg, byte triggers,
|
||||
byte *waiting_triggers)
|
||||
{
|
||||
byte match = rsg->triggers & (*waiting_triggers | triggers);
|
||||
bool res;
|
||||
|
||||
if (rsg->cmp_mode == RSG_CMP_ANY) {
|
||||
res = (match != 0);
|
||||
} else { /* RSG_CMP_ALL */
|
||||
res = (match == rsg->triggers);
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
*waiting_triggers |= triggers;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*waiting_triggers &= ~match;
|
||||
|
||||
return (rsg->num_groups - 1) << rsg->lowest_randbit;
|
||||
}
|
||||
|
|
31
sprite.h
31
sprite.h
|
@ -82,16 +82,36 @@ struct DeterministicSpriteGroup {
|
|||
struct SpriteGroup *default_group;
|
||||
};
|
||||
|
||||
struct RandomizedSpriteGroup {
|
||||
// Take this object:
|
||||
enum VarSpriteGroupScope var_scope;
|
||||
|
||||
// Check for these triggers:
|
||||
enum RandomizedSpriteGroupCompareMode {
|
||||
RSG_CMP_ANY,
|
||||
RSG_CMP_ALL,
|
||||
} cmp_mode;
|
||||
byte triggers;
|
||||
|
||||
// Look for this in the per-object randomized bitmask:
|
||||
byte lowest_randbit;
|
||||
byte num_groups; // must be power of 2
|
||||
|
||||
// Take the group with appropriate index:
|
||||
struct SpriteGroup *groups;
|
||||
};
|
||||
|
||||
struct SpriteGroup {
|
||||
enum SpriteGroupType {
|
||||
SGT_REAL,
|
||||
SGT_DETERMINISTIC,
|
||||
SGT_RANDOM, /* TODO */
|
||||
SGT_RANDOMIZED,
|
||||
} type;
|
||||
|
||||
union {
|
||||
struct RealSpriteGroup real;
|
||||
struct DeterministicSpriteGroup determ;
|
||||
struct RandomizedSpriteGroup random;
|
||||
} g;
|
||||
};
|
||||
|
||||
|
@ -108,4 +128,13 @@ struct SpriteGroup *EvalDeterministicSpriteGroup(struct DeterministicSpriteGroup
|
|||
/* Get value of a common deterministic SpriteGroup variable. */
|
||||
int GetDeterministicSpriteValue(byte var);
|
||||
|
||||
/* This takes randomized bitmask (probably associated with
|
||||
* vehicle/station/whatever) and chooses corresponding SpriteGroup
|
||||
* accordingly to the given RandomizedSpriteGroup. */
|
||||
struct SpriteGroup *EvalRandomizedSpriteGroup(struct RandomizedSpriteGroup *rsg, byte random_bits);
|
||||
/* Triggers given RandomizedSpriteGroup with given bitmask and returns and-mask
|
||||
* of random bits to be reseeded, or zero if there were no triggers matched
|
||||
* (then they are |ed to @waiting_triggers instead). */
|
||||
byte RandomizedSpriteGroupTriggeredBits(struct RandomizedSpriteGroup *rsg, byte triggers, byte *waiting_triggers);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1082,7 +1082,7 @@ ResolveStationSpriteGroup(struct SpriteGroup *spritegroup, struct Station *stat)
|
|||
}
|
||||
|
||||
default:
|
||||
case SGT_RANDOM:
|
||||
case SGT_RANDOMIZED:
|
||||
error("I don't know how to handle random spritegroups yet!");
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -2544,6 +2544,8 @@ void TrainEnterDepot(Vehicle *v, uint tile)
|
|||
|
||||
MaybeRenewVehicle(v, EstimateTrainCost(&_rail_vehicle_info[v->engine_type]));
|
||||
|
||||
TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
|
||||
|
||||
if ((v->next_order&OT_MASK) == OT_GOTO_DEPOT) {
|
||||
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
||||
|
||||
|
|
11
vehicle.c
11
vehicle.c
|
@ -164,6 +164,7 @@ static Vehicle *InitializeVehicle(Vehicle *v)
|
|||
v->next = NULL;
|
||||
v->next_hash = 0xffff;
|
||||
v->string_id = 0;
|
||||
v->random_bits = RandomRange(256);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
@ -1548,6 +1549,7 @@ const byte _common_veh_desc[] = {
|
|||
SLE_VAR(Vehicle,x_offs, SLE_INT8),
|
||||
SLE_VAR(Vehicle,y_offs, SLE_INT8),
|
||||
SLE_VAR(Vehicle,engine_type, SLE_UINT16),
|
||||
|
||||
SLE_VAR(Vehicle,max_speed, SLE_UINT16),
|
||||
SLE_VAR(Vehicle,cur_speed, SLE_UINT16),
|
||||
SLE_VAR(Vehicle,subspeed, SLE_UINT8),
|
||||
|
@ -1590,8 +1592,13 @@ const byte _common_veh_desc[] = {
|
|||
SLE_VAR(Vehicle,profit_last_year, SLE_INT32),
|
||||
SLE_VAR(Vehicle,value, SLE_UINT32),
|
||||
|
||||
// reserve extra space in savegame here. (currently 16 bytes)
|
||||
SLE_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 2, 2, 255),
|
||||
SLE_VAR(Vehicle,random_bits, SLE_UINT8),
|
||||
SLE_VAR(Vehicle,waiting_triggers, SLE_UINT8),
|
||||
|
||||
// reserve extra space in savegame here. (currently 14 bytes)
|
||||
SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 2, 2, 255), /* 2 */
|
||||
SLE_CONDARR(NullStruct,null,SLE_FILE_U16 | SLE_VAR_NULL, 2, 2, 255), /* 4 */
|
||||
SLE_CONDARR(NullStruct,null,SLE_FILE_U32 | SLE_VAR_NULL, 2, 2, 255), /* 8 */
|
||||
|
||||
SLE_END()
|
||||
};
|
||||
|
|
|
@ -107,10 +107,10 @@ struct Vehicle {
|
|||
byte z_pos;
|
||||
byte direction; // facing
|
||||
|
||||
uint16 cur_image; // sprite number for this vehicle
|
||||
byte spritenum; // currently displayed sprite index
|
||||
// 0xfd == custom sprite, 0xfe == custom second head sprite
|
||||
// 0xff == reserved for another custom sprite
|
||||
uint16 cur_image; // sprite number for this vehicle
|
||||
byte sprite_width;// width of vehicle sprite
|
||||
byte sprite_height;// height of vehicle sprite
|
||||
byte z_height; // z-height of vehicle sprite
|
||||
|
@ -118,6 +118,12 @@ struct Vehicle {
|
|||
int8 y_offs; // y offset for vehicle sprite
|
||||
uint16 engine_type;
|
||||
|
||||
// for randomized variational spritegroups
|
||||
// bitmask used to resolve them; parts of it get reseeded when triggers
|
||||
// of corresponding spritegroups get matched
|
||||
byte random_bits;
|
||||
byte waiting_triggers; // triggers to be yet matched
|
||||
|
||||
uint16 max_speed; // maximum speed
|
||||
uint16 cur_speed; // current speed
|
||||
byte subspeed; // fractional speed
|
||||
|
|
Loading…
Reference in New Issue