summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine.c89
-rw-r--r--engine.h12
-rw-r--r--newgrf.c46
-rw-r--r--sprite.h6
-rw-r--r--train_cmd.c64
5 files changed, 158 insertions, 59 deletions
diff --git a/engine.c b/engine.c
index de1f4a089..543e1129f 100644
--- a/engine.c
+++ b/engine.c
@@ -317,16 +317,17 @@ void SetCustomEngineSprites(byte engine, byte cargo, SpriteGroup *group)
_engine_custom_sprites[engine][cargo] = *group;
}
-typedef RealSpriteGroup *(*resolve_callback)(SpriteGroup *spritegroup,
- const Vehicle *veh, void *callback); /* XXX data pointer used as function pointer */
+typedef SpriteGroup *(*resolve_callback)(SpriteGroup *spritegroup,
+ const Vehicle *veh, uint16 callback_info, void *resolve_func); /* XXX data pointer used as function pointer */
-static RealSpriteGroup* ResolveVehicleSpriteGroup(SpriteGroup *spritegroup,
- const Vehicle *veh, resolve_callback callback)
+static SpriteGroup* ResolveVehicleSpriteGroup(SpriteGroup *spritegroup,
+ const Vehicle *veh, uint16 callback_info, resolve_callback resolve_func)
{
//debug("spgt %d", spritegroup->type);
switch (spritegroup->type) {
case SGT_REAL:
- return &spritegroup->g.real;
+ case SGT_CALLBACK:
+ return spritegroup;
case SGT_DETERMINISTIC: {
DeterministicSpriteGroup *dsg = &spritegroup->g.determ;
@@ -334,8 +335,10 @@ static RealSpriteGroup* ResolveVehicleSpriteGroup(SpriteGroup *spritegroup,
int value = -1;
//debug("[%p] Having fun resolving variable %x", veh, dsg->variable);
-
- if ((dsg->variable >> 6) == 0) {
+ if (dsg->variable == 0x0C) {
+ /* Callback ID */
+ value = callback_info & 0xFF;
+ } else if ((dsg->variable >> 6) == 0) {
/* General property */
value = GetDeterministicSpriteValue(dsg->variable);
} else {
@@ -351,7 +354,7 @@ static RealSpriteGroup* ResolveVehicleSpriteGroup(SpriteGroup *spritegroup,
} else {
target = dsg->default_group;
}
- return callback(target, NULL, callback);
+ return resolve_func(target, NULL, callback_info, resolve_func);
}
if (dsg->var_scope == VSG_SCOPE_PARENT) {
@@ -472,7 +475,7 @@ static RealSpriteGroup* ResolveVehicleSpriteGroup(SpriteGroup *spritegroup,
target = value != -1 ? EvalDeterministicSpriteGroup(dsg, value) : dsg->default_group;
//debug("Resolved variable %x: %d, %p", dsg->variable, value, callback);
- return callback(target, veh, callback);
+ return resolve_func(target, veh, callback_info, resolve_func);
}
case SGT_RANDOMIZED: {
@@ -482,7 +485,7 @@ static RealSpriteGroup* ResolveVehicleSpriteGroup(SpriteGroup *spritegroup,
/* 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);
+ return resolve_func(&rsg->groups[0], NULL, callback_info, resolve_func);
}
if (rsg->var_scope == VSG_SCOPE_PARENT) {
@@ -491,7 +494,7 @@ static RealSpriteGroup* ResolveVehicleSpriteGroup(SpriteGroup *spritegroup,
veh = GetFirstVehicleInChain(veh);
}
- return callback(EvalRandomizedSpriteGroup(rsg, veh->random_bits), veh, callback);
+ return resolve_func(EvalRandomizedSpriteGroup(rsg, veh->random_bits), veh, callback_info, resolve_func);
}
default:
@@ -543,14 +546,17 @@ int GetCustomEngineSprite(byte engine, const Vehicle *v, byte direction)
}
group = GetVehicleSpriteGroup(engine, v);
- rsg = ResolveVehicleSpriteGroup(group, v, (resolve_callback) ResolveVehicleSpriteGroup);
+ group = ResolveVehicleSpriteGroup(group, v, 0, (resolve_callback) ResolveVehicleSpriteGroup);
- if (rsg->sprites_per_set == 0 && cargo != 29) { /* XXX magic number */
+ if (group->type == SGT_REAL && group->g.real.sprites_per_set == 0 && cargo != GC_DEFAULT) {
// This group is empty but perhaps there'll be a default one.
- rsg = ResolveVehicleSpriteGroup(&_engine_custom_sprites[engine][29], v,
+ group = ResolveVehicleSpriteGroup(&_engine_custom_sprites[engine][GC_DEFAULT], v, 0,
(resolve_callback) ResolveVehicleSpriteGroup);
}
+ assert(group->type == SGT_REAL);
+ rsg = &group->g.real;
+
if (!rsg->sprites_per_set) {
// This group is empty. This function users should therefore
// look up the sprite number in _engine_original_sprites.
@@ -582,6 +588,39 @@ int GetCustomEngineSprite(byte engine, const Vehicle *v, byte direction)
return r;
}
+/**
+ * Evaluates a newgrf callback
+ * @param callback_info info about which callback to evaluate
+ * (bit 0-7) = CallBack id of the callback to use, see CallBackId enum
+ * (bit 8-15) = Other info some callbacks need to have, callback specific, see CallBackId enum, not used yet
+ * @param engine Engine type of the vehicle to evaluate the callback for
+ * @param vehicle The vehicle to evaluate the callback for, NULL if it doesnt exist (yet)
+ * @return The value the callback returned, or CALLBACK_FAILED if it failed
+ */
+uint16 GetCallBackResult(uint16 callback_info, byte engine, const Vehicle *v)
+{
+ SpriteGroup *group;
+ byte cargo = GC_DEFAULT;
+
+ if (v != NULL)
+ cargo = _global_cargo_id[_opt.landscape][v->cargo_type];
+
+ group = &_engine_custom_sprites[engine][cargo];
+ group = ResolveVehicleSpriteGroup(group, v, callback_info, (resolve_callback) ResolveVehicleSpriteGroup);
+
+ if (group->type == SGT_REAL && group->g.real.sprites_per_set == 0 && 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);
+ }
+
+ if (group->type != SGT_CALLBACK)
+ return CALLBACK_FAILED;
+
+ return group->g.callback.result;
+}
+
+
// Global variables are evil, yes, but we would end up with horribly overblown
// calling convention otherwise and this should be 100% reentrant.
@@ -590,8 +629,8 @@ static byte _vsg_bits_to_reseed;
extern int _custom_sprites_base;
-static RealSpriteGroup *TriggerVehicleSpriteGroup(SpriteGroup *spritegroup,
- Vehicle *veh, resolve_callback callback)
+static SpriteGroup *TriggerVehicleSpriteGroup(SpriteGroup *spritegroup,
+ Vehicle *veh, uint16 callback_info, resolve_callback resolve_func)
{
if (spritegroup->type == SGT_RANDOMIZED) {
_vsg_bits_to_reseed |= RandomizedSpriteGroupTriggeredBits(
@@ -601,23 +640,29 @@ static RealSpriteGroup *TriggerVehicleSpriteGroup(SpriteGroup *spritegroup,
);
}
- return ResolveVehicleSpriteGroup(spritegroup, veh, callback);
+ return ResolveVehicleSpriteGroup(spritegroup, veh, callback_info, resolve_func);
}
static void DoTriggerVehicle(Vehicle *veh, VehicleTrigger trigger, byte base_random_bits, bool first)
{
+ SpriteGroup *group;
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 */
+ group = TriggerVehicleSpriteGroup(GetVehicleSpriteGroup(veh->engine_type, veh), veh, 0,
+ (resolve_callback) TriggerVehicleSpriteGroup);
+
+ if (group->type == SGT_REAL && group->g.real.sprites_per_set == 0 && veh->cargo_type != GC_DEFAULT) {
// 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);
+ group = TriggerVehicleSpriteGroup(&_engine_custom_sprites[veh->engine_type][GC_DEFAULT], veh, 0,
+ (resolve_callback) TriggerVehicleSpriteGroup);
}
+
+ assert(group->type == SGT_REAL);
+ rsg = &group->g.real;
+
new_random_bits = Random();
veh->random_bits &= ~_vsg_bits_to_reseed;
veh->random_bits |= (first ? new_random_bits : base_random_bits) & _vsg_bits_to_reseed;
diff --git a/engine.h b/engine.h
index b588478fe..b27b094c3 100644
--- a/engine.h
+++ b/engine.h
@@ -122,6 +122,17 @@ enum GlobalCargo {
NUM_GLOBAL_CID = 31
};
+// This enum only lists implemented callbacks
+enum CallbackID {
+ // Refit capacity, the passed vehicle needs to have its ->cargo_type set to
+ // the cargo we are refitting to, returns the new cargo capacity
+ CB_REFIT_CAP = 0x15,
+};
+
+enum {
+ CALLBACK_FAILED = 0xFFFF
+};
+
VARDEF const uint32 _default_refitmasks[NUM_VEHICLE_TYPES];
VARDEF const CargoID _global_cargo_id[NUM_LANDSCAPE][NUM_CARGO];
VARDEF const uint32 _landscape_global_cargo_mask[NUM_LANDSCAPE];
@@ -133,6 +144,7 @@ void SetWagonOverrideSprites(byte engine, struct SpriteGroup *group, byte *train
void SetCustomEngineSprites(byte engine, byte cargo, struct SpriteGroup *group);
// loaded is in percents, overriding_engine 0xffff is none
int GetCustomEngineSprite(byte engine, const Vehicle *v, byte direction);
+uint16 GetCallBackResult(uint16 callback_info, byte engine, const Vehicle *v);
#define GetCustomVehicleSprite(v, direction) GetCustomEngineSprite(v->engine_type, v, direction)
#define GetCustomVehicleIcon(et, direction) GetCustomEngineSprite(et, NULL, direction)
diff --git a/newgrf.c b/newgrf.c
index 225b2d10f..0815d8034 100644
--- a/newgrf.c
+++ b/newgrf.c
@@ -1080,6 +1080,28 @@ ignoring:
#undef FOR_EACH_OBJECT
+/**
+ * Creates a spritegroup representing a callback result
+ * @param value The value that was used to represent this callback result
+ * @return A spritegroup representing that callback result
+ */
+SpriteGroup NewCallBackResult(uint16 value)
+{
+ SpriteGroup group;
+
+ group.type = SGT_CALLBACK;
+
+ // Old style callback results have the highest byte 0xFF so signify it is a callback result
+ // New style ones only have the highest bit set (allows 15-bit results, instead of just 8)
+ if ((value >> 8) == 0xFF)
+ value &= 0xFF;
+ else
+ value &= ~0x8000;
+
+ group.g.callback.result = value;
+
+ return group;
+}
/* Action 0x01 */
static void NewSpriteSet(byte *buf, int len)
@@ -1183,37 +1205,43 @@ static void NewSpriteGroup(byte *buf, int len)
dg->divmod_val = grf_load_byte(&buf);
}
- /* (groupid & 0x8000) means this is callback result; we happily
- * ignore that for now. */
+ /* (groupid & 0x8000) means this is callback result. */
dg->num_ranges = grf_load_byte(&buf);
dg->ranges = calloc(dg->num_ranges, sizeof(*dg->ranges));
for (i = 0; i < dg->num_ranges; i++) {
groupid = grf_load_word(&buf);
- if (groupid & 0x8000 || groupid >= _cur_grffile->spritegroups_count) {
+ if (groupid & 0x8000) {
+ dg->ranges[i].group = NewCallBackResult(groupid);
+ } else if (groupid >= _cur_grffile->spritegroups_count) {
/* This doesn't exist for us. */
grf_load_word(&buf); // skip range
i--; dg->num_ranges--;
continue;
- }
+ } else {
/* XXX: If multiple surreal sets attach a surreal
* set this way, we are in trouble. */
- dg->ranges[i].group = _cur_grffile->spritegroups[groupid];
+ dg->ranges[i].group = _cur_grffile->spritegroups[groupid];
+ }
+
dg->ranges[i].low = grf_load_byte(&buf);
dg->ranges[i].high = grf_load_byte(&buf);
}
groupid = grf_load_word(&buf);
- if (groupid & 0x8000 || groupid >= _cur_grffile->spritegroups_count) {
+ if (groupid & 0x8000) {
+ dg->default_group = malloc(sizeof(*dg->default_group));
+ *dg->default_group = NewCallBackResult(groupid);
+ } else if (groupid >= _cur_grffile->spritegroups_count) {
/* This spritegroup stinks. */
free(dg->ranges), dg->ranges = NULL;
grfmsg(GMS_WARN, "NewSpriteGroup(%02x:0x%x): Default groupid %04x is cargo callback or unknown, ignoring spritegroup.", setid, numloaded, groupid);
return;
+ } else {
+ dg->default_group = malloc(sizeof(*dg->default_group));
+ memcpy(dg->default_group, &_cur_grffile->spritegroups[groupid], sizeof(*dg->default_group));
}
- dg->default_group = malloc(sizeof(*dg->default_group));
- memcpy(dg->default_group, &_cur_grffile->spritegroups[groupid], sizeof(*dg->default_group));
-
return;
} else if (numloaded == 0x80 || numloaded == 0x83) {
diff --git a/sprite.h b/sprite.h
index bbe797d32..18d75d6a2 100644
--- a/sprite.h
+++ b/sprite.h
@@ -107,10 +107,15 @@ typedef struct RandomizedSpriteGroup {
SpriteGroup *groups;
} RandomizedSpriteGroup;
+typedef struct CallbackResultSpriteGroup {
+ uint16 result;
+} CallbackResultSpriteGroup;
+
typedef enum SpriteGroupType {
SGT_REAL,
SGT_DETERMINISTIC,
SGT_RANDOMIZED,
+ SGT_CALLBACK,
} SpriteGroupType;
struct SpriteGroup {
@@ -120,6 +125,7 @@ struct SpriteGroup {
RealSpriteGroup real;
DeterministicSpriteGroup determ;
RandomizedSpriteGroup random;
+ CallbackResultSpriteGroup callback;
} g;
};
diff --git a/train_cmd.c b/train_cmd.c
index 25ec0c8e4..483eb7370 100644
--- a/train_cmd.c
+++ b/train_cmd.c
@@ -1318,10 +1318,6 @@ int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
cost = 0;
num = 0;
- // newgrf stuff can change graphics when refitting
- if (!(flags & DC_EXEC))
- InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
-
do {
/* XXX: We also refit all the attached wagons en-masse if they
* can be refitted. This is how TTDPatch does it. TODO: Have
@@ -1330,30 +1326,42 @@ int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
if (v->cargo_cap != 0) {
RailVehicleInfo *rvi = RailVehInfo(v->engine_type);
- uint16 amount = rvi->capacity;
- CargoID old_cid = rvi->cargo_type;
-
- /* the capacity depends on the cargo type, a rail vehicle
- * can carry twice as much mail/goods as normal cargo,
- * and four times as much passengers */
- (old_cid == CT_PASSENGERS) ||
- (amount <<= 1, old_cid == CT_MAIL || old_cid == CT_GOODS) ||
- (amount <<= 1, true);
- (new_cid == CT_PASSENGERS) ||
- (amount >>= 1, new_cid == CT_MAIL || new_cid == CT_GOODS) ||
- (amount >>= 1, true);
-
- if (new_cid != v->cargo_type)
- cost += (_price.build_railvehicle >> 8);
- num += amount;
- if (flags & DC_EXEC) {
- //autorefitted train cars wants to keep the cargo
- //it will be checked if the cargo is valid in CmdReplaceVehicle
- if (!(SkipStoppedInDepotCheck))
- v->cargo_count = 0;
- v->cargo_type = new_cid;
- v->cargo_cap = amount;
- InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
+ uint16 amount;
+ CargoID temp_cid = v->cargo_type;
+
+ /* Check the 'refit capacity' callback */
+ v->cargo_type = new_cid;
+ amount = GetCallBackResult(CB_REFIT_CAP, v->engine_type, v);
+ v->cargo_type = temp_cid;
+
+ if (amount == CALLBACK_FAILED) { // callback failed, use default
+ CargoID old_cid = rvi->cargo_type;
+ /* normally, the capacity depends on the cargo type, a rail vehicle
+ * can carry twice as much mail/goods as normal cargo,
+ * and four times as much passengers */
+ amount = rvi->capacity;
+ (old_cid == CT_PASSENGERS) ||
+ (amount <<= 1, old_cid == CT_MAIL || old_cid == CT_GOODS) ||
+ (amount <<= 1, true);
+ (new_cid == CT_PASSENGERS) ||
+ (amount >>= 1, new_cid == CT_MAIL || new_cid == CT_GOODS) ||
+ (amount >>= 1, true);
+ };
+
+ if (amount != 0) {
+ if (new_cid != v->cargo_type)
+ cost += (_price.build_railvehicle >> 8);
+ num += amount;
+ if (flags & DC_EXEC) {
+ //autorefitted train cars wants to keep the cargo
+ //it will be checked if the cargo is valid in CmdReplaceVehicle
+ if (!(SkipStoppedInDepotCheck))
+ v->cargo_count = 0;
+ v->cargo_type = new_cid;
+ v->cargo_cap = amount;
+ InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
+ InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
+ }
}
}
// SkipStoppedInDepotCheck is called by CmdReplace and it should only apply to the single car it is called for