summaryrefslogtreecommitdiff
path: root/engine.c
diff options
context:
space:
mode:
authortron <tron@openttd.org>2004-11-17 08:52:47 +0000
committertron <tron@openttd.org>2004-11-17 08:52:47 +0000
commit13f0b6c0cf754184cfef8645e709f10240da5f98 (patch)
tree71bd164389e546b1a8a5c1c879b57c1574cb95d2 /engine.c
parent0086bb9d0678876e2e3e9b4002a0f44af2c45ee4 (diff)
downloadopenttd-13f0b6c0cf754184cfef8645e709f10240da5f98.tar.xz
(svn r654) Hopefully complete support for randomized variational spritegroups (i.e. the cars transporter in DBSetXL gets different cars each time) (pasky)
Diffstat (limited to 'engine.c')
-rw-r--r--engine.c152
1 files changed, 136 insertions, 16 deletions
diff --git a/engine.c b/engine.c
index fdc9eda01..23916de88 100644
--- a/engine.c
+++ b/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)