From b88904f345cf14a3e02313b50d81148ee9bdf6d8 Mon Sep 17 00:00:00 2001 From: peter1138 Date: Fri, 3 Feb 2006 12:55:21 +0000 Subject: (svn r3524) - Split newgrf features from engine.[ch] into newgrf_engine.[ch], and add the new files to project files. --- Makefile | 1 + aircraft_cmd.c | 1 + aircraft_gui.c | 1 + economy.c | 1 + engine.c | 583 +------------------------------------------------------ engine.h | 59 ------ engine_gui.c | 1 + newgrf.c | 1 + newgrf_engine.c | 593 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ newgrf_engine.h | 73 +++++++ openttd.dsp | 12 ++ openttd.vcproj | 6 + roadveh_cmd.c | 1 + roadveh_gui.c | 1 + ship_cmd.c | 1 + ship_gui.c | 1 + train_cmd.c | 1 + train_gui.c | 1 + vehicle_gui.c | 1 + 19 files changed, 698 insertions(+), 641 deletions(-) create mode 100644 newgrf_engine.c create mode 100644 newgrf_engine.h diff --git a/Makefile b/Makefile index 431381bb1..016258796 100644 --- a/Makefile +++ b/Makefile @@ -653,6 +653,7 @@ SRCS += network_gui.c SRCS += network_server.c SRCS += network_udp.c SRCS += newgrf.c +SRCS += newgrf_engine.c SRCS += news_gui.c SRCS += npf.c SRCS += oldloader.c diff --git a/aircraft_cmd.c b/aircraft_cmd.c index 71aa8006b..631166ec7 100644 --- a/aircraft_cmd.c +++ b/aircraft_cmd.c @@ -18,6 +18,7 @@ #include "airport.h" #include "vehicle_gui.h" #include "table/sprites.h" +#include "newgrf_engine.h" static bool AirportMove(Vehicle *v, const AirportFTAClass *Airport); static bool AirportSetBlocks(Vehicle *v, AirportFTA *current_pos, const AirportFTAClass *Airport); diff --git a/aircraft_gui.c b/aircraft_gui.c index 3898f9040..607fa3d2e 100644 --- a/aircraft_gui.c +++ b/aircraft_gui.c @@ -19,6 +19,7 @@ #include "player.h" #include "depot.h" #include "vehicle_gui.h" +#include "newgrf_engine.h" /** * Draw the purchase info details of an aircraft at a given location. diff --git a/economy.c b/economy.c index 69db3633b..5ad3eaba9 100644 --- a/economy.c +++ b/economy.c @@ -27,6 +27,7 @@ #include "vehicle_gui.h" #include "ai/ai.h" #include "train.h" +#include "newgrf_engine.h" // Score info const ScoreInfo _score_info[] = { diff --git a/engine.c b/engine.c index 0ed802637..93704ae8f 100644 --- a/engine.c +++ b/engine.c @@ -4,8 +4,6 @@ #include "openttd.h" #include "debug.h" #include "functions.h" -#include "string.h" -#include "strings.h" #include "table/strings.h" #include "engine.h" #include "table/engines.h" @@ -18,6 +16,7 @@ #include "sprite.h" #include "variables.h" #include "train.h" +#include "newgrf_engine.h" EngineInfo _engine_info[TOTAL_NUM_ENGINES]; RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES]; @@ -257,586 +256,6 @@ void StartupEngines(void) AdjustAvailAircraft(); } -// TODO: We don't support cargo-specific wagon overrides. Pretty exotic... ;-) --pasky - -typedef struct WagonOverride { - byte *train_id; - int trains; - SpriteGroup *group; -} WagonOverride; - -typedef struct WagonOverrides { - int overrides_count; - WagonOverride *overrides; -} WagonOverrides; - -static WagonOverrides _engine_wagon_overrides[TOTAL_NUM_ENGINES]; - -void SetWagonOverrideSprites(EngineID engine, SpriteGroup *group, byte *train_id, - int trains) -{ - WagonOverrides *wos; - WagonOverride *wo; - - wos = &_engine_wagon_overrides[engine]; - wos->overrides_count++; - wos->overrides = realloc(wos->overrides, - wos->overrides_count * sizeof(*wos->overrides)); - - wo = &wos->overrides[wos->overrides_count - 1]; - /* FIXME: If we are replacing an override, release original SpriteGroup - * to prevent leaks. But first we need to refcount the SpriteGroup. - * --pasky */ - wo->group = group; - group->ref_count++; - wo->trains = trains; - wo->train_id = malloc(trains); - memcpy(wo->train_id, train_id, trains); -} - -static const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, byte overriding_engine) -{ - const WagonOverrides *wos = &_engine_wagon_overrides[engine]; - int i; - - // XXX: This could turn out to be a timesink on profiles. We could - // always just dedicate 65535 bytes for an [engine][train] trampoline - // for O(1). Or O(logMlogN) and searching binary tree or smt. like - // that. --pasky - - for (i = 0; i < wos->overrides_count; i++) { - const WagonOverride *wo = &wos->overrides[i]; - int j; - - for (j = 0; j < wo->trains; j++) { - if (wo->train_id[j] == overriding_engine) - return wo->group; - } - } - return NULL; -} - -/** - * Unload all wagon override sprite groups. - */ -void UnloadWagonOverrides(void) -{ - WagonOverrides *wos; - WagonOverride *wo; - EngineID engine; - int i; - - for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) { - wos = &_engine_wagon_overrides[engine]; - for (i = 0; i < wos->overrides_count; i++) { - wo = &wos->overrides[i]; - UnloadSpriteGroup(&wo->group); - free(wo->train_id); - } - free(wos->overrides); - wos->overrides_count = 0; - wos->overrides = NULL; - } -} - -// 0 - 28 are cargos, 29 is default, 30 is the advert (purchase list) -// (It isn't and shouldn't be like this in the GRF files since new cargo types -// may appear in future - however it's more convenient to store it like this in -// memory. --pasky) -static SpriteGroup *engine_custom_sprites[TOTAL_NUM_ENGINES][NUM_GLOBAL_CID]; - -void SetCustomEngineSprites(EngineID engine, byte cargo, SpriteGroup *group) -{ - if (engine_custom_sprites[engine][cargo] != NULL) { - DEBUG(grf, 6)("SetCustomEngineSprites: engine `%d' cargo `%d' already has group -- removing.", engine, cargo); - UnloadSpriteGroup(&engine_custom_sprites[engine][cargo]); - } - engine_custom_sprites[engine][cargo] = group; - group->ref_count++; -} - -/** - * Unload all engine sprite groups. - */ -void UnloadCustomEngineSprites(void) -{ - EngineID engine; - CargoID cargo; - - for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) { - for (cargo = 0; cargo < NUM_GLOBAL_CID; cargo++) { - if (engine_custom_sprites[engine][cargo] != NULL) { - DEBUG(grf, 6)("UnloadCustomEngineSprites: Unloading group for engine `%d' cargo `%d'.", engine, cargo); - UnloadSpriteGroup(&engine_custom_sprites[engine][cargo]); - } - } - } -} - -static int MapOldSubType(const Vehicle *v) -{ - if (v->type != VEH_Train) return v->subtype; - if (IsTrainEngine(v)) return 0; - if (IsFreeWagon(v)) return 4; - return 2; -} - -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) -{ - 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; - - //debug("[%p] Having fun resolving variable %x", veh, dsg->variable); - if (dsg->variable == 0x0C) { - /* Callback ID */ - value = callback_info & 0xFF; - } else if (dsg->variable == 0x10) { - value = (callback_info >> 8) & 0xFF; - } else if ((dsg->variable >> 6) == 0) { - /* General property */ - value = GetDeterministicSpriteValue(dsg->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 (dsg->variable == 0x40 || dsg->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 (dsg->variable == 0x41 && u->engine_type != veh->engine_type) - chain_before = 0; - u = u->next; - } - while (u->next != NULL && (dsg->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 (dsg->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(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); - /* 00h..07h=sub image? 40h=in tunnel; actually some kind of status - * aircraft: >=13h when in flight - * train, ship: 80h=in depot - * rv: 0feh=in depot */ - /* TODO veh_prop(0x62, veh->???); */ - - /* TODO: The rest is per-vehicle, I hope no GRF file looks so far. - * But they won't let us have an easy ride so surely *some* GRF - * file does. So someone needs to do this too. --pasky */ - -#undef veh_prop - } - } - } - - 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; - } -} - -static const SpriteGroup *GetVehicleSpriteGroup(EngineID engine, const Vehicle *v) -{ - const SpriteGroup *group; - byte cargo = GC_PURCHASE; - - if (v != NULL) { - cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; - assert(cargo != GC_INVALID); - } - - group = engine_custom_sprites[engine][cargo]; - - if (v != NULL && v->type == VEH_Train) { - const SpriteGroup *overset = GetWagonOverrideSpriteSet(engine, v->u.rail.first_engine); - - if (overset != NULL) group = overset; - } - - return group; -} - -int GetCustomEngineSprite(EngineID engine, const Vehicle *v, byte direction) -{ - const SpriteGroup *group; - const RealSpriteGroup *rsg; - byte cargo = GC_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]; - 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. - 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; - - if (!rsg->sprites_per_set) { - // This group is empty. This function users should therefore - // look up the sprite number in _engine_original_sprites. - return 0; - } - - assert(rsg->sprites_per_set <= 8); - direction %= rsg->sprites_per_set; - - totalsets = in_motion ? rsg->loaded_count : rsg->loading_count; - - // 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--; - } - - r = (in_motion ? rsg->loaded[spriteset]->g.result.result : rsg->loading[spriteset]->g.result.result) + direction; - return r; -} - -/** - * Check if a wagon is currently using a wagon override - * @param v The wagon to check - * @return true if it is using an override, false otherwise - */ -bool UsesWagonOverride(const Vehicle* v) -{ - assert(v->type == VEH_Train); - return GetWagonOverrideSpriteSet(v->engine_type, v->u.rail.first_engine) != NULL; -} - -/** - * 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, EngineID engine, const Vehicle *v) -{ - const SpriteGroup *group; - byte cargo = GC_DEFAULT; - - if (v != NULL) - cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; - - group = engine_custom_sprites[engine][cargo]; - - if (v != NULL && v->type == VEH_Train) { - const SpriteGroup *overset = GetWagonOverrideSpriteSet(engine, v->u.rail.first_engine); - - if (overset != NULL) group = overset; - } - - group = ResolveVehicleSpriteGroup(group, v, callback_info, (resolve_callback) ResolveVehicleSpriteGroup); - - if (group == NULL && 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 == NULL || 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. -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 *veh, VehicleTrigger trigger, byte base_random_bits, bool first) -{ - const SpriteGroup *group; - const RealSpriteGroup *rsg; - byte new_random_bits; - - _vsg_random_triggers = trigger; - _vsg_bits_to_reseed = 0; - group = TriggerVehicleSpriteGroup(GetVehicleSpriteGroup(veh->engine_type, veh), veh, 0, - (resolve_callback) TriggerVehicleSpriteGroup); - - if (group == NULL && veh->cargo_type != GC_DEFAULT) { - // This group turned out to be empty but perhaps there'll be a default one. - group = TriggerVehicleSpriteGroup(engine_custom_sprites[veh->engine_type][GC_DEFAULT], veh, 0, - (resolve_callback) TriggerVehicleSpriteGroup); - } - - if (group == NULL) - return; - - 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; - - 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, VehicleTrigger trigger) -{ - if (trigger == VEHICLE_TRIGGER_DEPOT) { - // store that the vehicle entered a depot this tick - VehicleEnteredDepotThisTick(veh); - } - - DoTriggerVehicle(veh, trigger, 0, true); -} - -static char *_engine_custom_names[TOTAL_NUM_ENGINES]; - -void SetCustomEngineName(EngineID engine, const char *name) -{ - _engine_custom_names[engine] = strdup(name); -} - -void UnloadCustomEngineNames(void) -{ - char **i; - for (i = _engine_custom_names; i != endof(_engine_custom_names); i++) { - free(*i); - *i = NULL; - } -} - -StringID GetCustomEngineName(EngineID engine) -{ - if (!_engine_custom_names[engine]) - return _engine_name_strings[engine]; - ttd_strlcpy(_userstring, _engine_custom_names[engine], lengthof(_userstring)); - return STR_SPEC_USERSTRING; -} - - static void AcceptEnginePreview(Engine *e, PlayerID player) { Player *p = GetPlayer(player); diff --git a/engine.h b/engine.h index 7b32c96a5..1cd19a17d 100644 --- a/engine.h +++ b/engine.h @@ -146,67 +146,12 @@ enum GlobalCargo { NUM_GLOBAL_CID = 31 }; -// This enum lists the implemented callbacks -// Use as argument for the GetCallBackResult function (see comments there) -enum CallbackID { - // Powered wagons, if the result is lower as 0x40 then the wagon is powered - // TODO: interpret the rest of the result, aka "visual effects" - CBID_WAGON_POWER = 0x10, - - // Vehicle length, returns the amount of 1/8's the vehicle is shorter - // only for train vehicles - CBID_VEH_LENGTH = 0x11, - - // Refit capacity, the passed vehicle needs to have its ->cargo_type set to - // the cargo we are refitting to, returns the new cargo capacity - CBID_REFIT_CAP = 0x15, - - CBID_ARTIC_ENGINE = 0x16, -}; - -// bit positions for rvi->callbackmask, indicates which callbacks are used by an engine -// (some callbacks are always used, and dont appear here) -enum CallbackMask { - CBM_WAGON_POWER = 0, - CBM_VEH_LENGTH = 1, - CBM_REFIT_CAP = 3, - CBM_ARTIC_ENGINE = 4, -}; - -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]; VARDEF const CargoID _local_cargo_id_ctype[NUM_GLOBAL_CID]; VARDEF const uint32 cargo_classes[16]; -void SetWagonOverrideSprites(EngineID engine, struct SpriteGroup *group, byte *train_id, int trains); -void SetCustomEngineSprites(EngineID engine, byte cargo, struct SpriteGroup *group); -// loaded is in percents, overriding_engine 0xffff is none -int GetCustomEngineSprite(EngineID engine, const Vehicle *v, byte direction); -uint16 GetCallBackResult(uint16 callback_info, EngineID engine, const Vehicle *v); -bool UsesWagonOverride(const Vehicle *v); -#define GetCustomVehicleSprite(v, direction) GetCustomEngineSprite(v->engine_type, v, direction) -#define GetCustomVehicleIcon(et, direction) GetCustomEngineSprite(et, NULL, direction) - -typedef 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, -} VehicleTrigger; -void TriggerVehicle(Vehicle *veh, VehicleTrigger trigger); - -void SetCustomEngineName(EngineID engine, const char *name); -StringID GetCustomEngineName(EngineID engine); - - void DrawTrainEngine(int x, int y, EngineID engine, uint32 image_ormod); void DrawRoadVehEngine(int x, int y, EngineID engine, uint32 image_ormod); void DrawShipEngine(int x, int y, EngineID engine, uint32 image_ormod); @@ -284,10 +229,6 @@ static inline const RoadVehicleInfo* RoadVehInfo(EngineID e) return &_road_vehicle_info[e - ROAD_ENGINES_INDEX]; } -void UnloadWagonOverrides(void); -void UnloadCustomEngineSprites(void); -void UnloadCustomEngineNames(void); - /************************************************************************ * Engine Replacement stuff ************************************************************************/ diff --git a/engine_gui.c b/engine_gui.c index da33fac3d..61147d644 100644 --- a/engine_gui.c +++ b/engine_gui.c @@ -13,6 +13,7 @@ #include "command.h" #include "news.h" #include "variables.h" +#include "newgrf_engine.h" static StringID GetEngineCategoryName(EngineID engine) diff --git a/newgrf.c b/newgrf.c index 402356a3b..109ab68e2 100644 --- a/newgrf.c +++ b/newgrf.c @@ -17,6 +17,7 @@ #include "variables.h" #include "bridge.h" #include "economy.h" +#include "newgrf_engine.h" /* TTDPatch extended GRF format codec * (c) Petr Baudis 2004 (GPL'd) diff --git a/newgrf_engine.c b/newgrf_engine.c new file mode 100644 index 000000000..827014225 --- /dev/null +++ b/newgrf_engine.c @@ -0,0 +1,593 @@ +/* $Id$ */ + +#include "stdafx.h" +#include "openttd.h" +#include "debug.h" +#include "functions.h" +#include "string.h" +#include "strings.h" +#include "engine.h" +#include "newgrf_engine.h" +#include "sprite.h" +#include "variables.h" +#include "train.h" + +// TODO: We don't support cargo-specific wagon overrides. Pretty exotic... ;-) --pasky + +typedef struct WagonOverride { + byte *train_id; + int trains; + SpriteGroup *group; +} WagonOverride; + +typedef struct WagonOverrides { + int overrides_count; + WagonOverride *overrides; +} WagonOverrides; + +static WagonOverrides _engine_wagon_overrides[TOTAL_NUM_ENGINES]; + +void SetWagonOverrideSprites(EngineID engine, SpriteGroup *group, byte *train_id, + int trains) +{ + WagonOverrides *wos; + WagonOverride *wo; + + wos = &_engine_wagon_overrides[engine]; + wos->overrides_count++; + wos->overrides = realloc(wos->overrides, + wos->overrides_count * sizeof(*wos->overrides)); + + wo = &wos->overrides[wos->overrides_count - 1]; + /* FIXME: If we are replacing an override, release original SpriteGroup + * to prevent leaks. But first we need to refcount the SpriteGroup. + * --pasky */ + wo->group = group; + group->ref_count++; + wo->trains = trains; + wo->train_id = malloc(trains); + memcpy(wo->train_id, train_id, trains); +} + +static const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, byte overriding_engine) +{ + const WagonOverrides *wos = &_engine_wagon_overrides[engine]; + int i; + + // XXX: This could turn out to be a timesink on profiles. We could + // always just dedicate 65535 bytes for an [engine][train] trampoline + // for O(1). Or O(logMlogN) and searching binary tree or smt. like + // that. --pasky + + for (i = 0; i < wos->overrides_count; i++) { + const WagonOverride *wo = &wos->overrides[i]; + int j; + + for (j = 0; j < wo->trains; j++) { + if (wo->train_id[j] == overriding_engine) + return wo->group; + } + } + return NULL; +} + +/** + * Unload all wagon override sprite groups. + */ +void UnloadWagonOverrides(void) +{ + WagonOverrides *wos; + WagonOverride *wo; + EngineID engine; + int i; + + for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) { + wos = &_engine_wagon_overrides[engine]; + for (i = 0; i < wos->overrides_count; i++) { + wo = &wos->overrides[i]; + UnloadSpriteGroup(&wo->group); + free(wo->train_id); + } + free(wos->overrides); + wos->overrides_count = 0; + wos->overrides = NULL; + } +} + +// 0 - 28 are cargos, 29 is default, 30 is the advert (purchase list) +// (It isn't and shouldn't be like this in the GRF files since new cargo types +// may appear in future - however it's more convenient to store it like this in +// memory. --pasky) +static SpriteGroup *engine_custom_sprites[TOTAL_NUM_ENGINES][NUM_GLOBAL_CID]; + +void SetCustomEngineSprites(EngineID engine, byte cargo, SpriteGroup *group) +{ + if (engine_custom_sprites[engine][cargo] != NULL) { + DEBUG(grf, 6)("SetCustomEngineSprites: engine `%d' cargo `%d' already has group -- removing.", engine, cargo); + UnloadSpriteGroup(&engine_custom_sprites[engine][cargo]); + } + engine_custom_sprites[engine][cargo] = group; + group->ref_count++; +} + +/** + * Unload all engine sprite groups. + */ +void UnloadCustomEngineSprites(void) +{ + EngineID engine; + CargoID cargo; + + for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) { + for (cargo = 0; cargo < NUM_GLOBAL_CID; cargo++) { + if (engine_custom_sprites[engine][cargo] != NULL) { + DEBUG(grf, 6)("UnloadCustomEngineSprites: Unloading group for engine `%d' cargo `%d'.", engine, cargo); + UnloadSpriteGroup(&engine_custom_sprites[engine][cargo]); + } + } + } +} + +static int MapOldSubType(const Vehicle *v) +{ + if (v->type != VEH_Train) return v->subtype; + if (IsTrainEngine(v)) return 0; + if (IsFreeWagon(v)) return 4; + return 2; +} + +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) +{ + 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; + + //debug("[%p] Having fun resolving variable %x", veh, dsg->variable); + if (dsg->variable == 0x0C) { + /* Callback ID */ + value = callback_info & 0xFF; + } else if (dsg->variable == 0x10) { + value = (callback_info >> 8) & 0xFF; + } else if ((dsg->variable >> 6) == 0) { + /* General property */ + value = GetDeterministicSpriteValue(dsg->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 (dsg->variable == 0x40 || dsg->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 (dsg->variable == 0x41 && u->engine_type != veh->engine_type) + chain_before = 0; + u = u->next; + } + while (u->next != NULL && (dsg->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 (dsg->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(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); + /* 00h..07h=sub image? 40h=in tunnel; actually some kind of status + * aircraft: >=13h when in flight + * train, ship: 80h=in depot + * rv: 0feh=in depot */ + /* TODO veh_prop(0x62, veh->???); */ + + /* TODO: The rest is per-vehicle, I hope no GRF file looks so far. + * But they won't let us have an easy ride so surely *some* GRF + * file does. So someone needs to do this too. --pasky */ + +#undef veh_prop + } + } + } + + 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; + } +} + +static const SpriteGroup *GetVehicleSpriteGroup(EngineID engine, const Vehicle *v) +{ + const SpriteGroup *group; + byte cargo = GC_PURCHASE; + + if (v != NULL) { + cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; + assert(cargo != GC_INVALID); + } + + group = engine_custom_sprites[engine][cargo]; + + if (v != NULL && v->type == VEH_Train) { + const SpriteGroup *overset = GetWagonOverrideSpriteSet(engine, v->u.rail.first_engine); + + if (overset != NULL) group = overset; + } + + return group; +} + +int GetCustomEngineSprite(EngineID engine, const Vehicle *v, byte direction) +{ + const SpriteGroup *group; + const RealSpriteGroup *rsg; + byte cargo = GC_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]; + 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. + 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; + + if (!rsg->sprites_per_set) { + // This group is empty. This function users should therefore + // look up the sprite number in _engine_original_sprites. + return 0; + } + + assert(rsg->sprites_per_set <= 8); + direction %= rsg->sprites_per_set; + + totalsets = in_motion ? rsg->loaded_count : rsg->loading_count; + + // 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--; + } + + r = (in_motion ? rsg->loaded[spriteset]->g.result.result : rsg->loading[spriteset]->g.result.result) + direction; + return r; +} + +/** + * Check if a wagon is currently using a wagon override + * @param v The wagon to check + * @return true if it is using an override, false otherwise + */ +bool UsesWagonOverride(const Vehicle* v) +{ + assert(v->type == VEH_Train); + return GetWagonOverrideSpriteSet(v->engine_type, v->u.rail.first_engine) != NULL; +} + +/** + * 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, EngineID engine, const Vehicle *v) +{ + const SpriteGroup *group; + byte cargo = GC_DEFAULT; + + if (v != NULL) + cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; + + group = engine_custom_sprites[engine][cargo]; + + if (v != NULL && v->type == VEH_Train) { + const SpriteGroup *overset = GetWagonOverrideSpriteSet(engine, v->u.rail.first_engine); + + if (overset != NULL) group = overset; + } + + group = ResolveVehicleSpriteGroup(group, v, callback_info, (resolve_callback) ResolveVehicleSpriteGroup); + + if (group == NULL && 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 == NULL || 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. +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 *veh, VehicleTrigger trigger, byte base_random_bits, bool first) +{ + const SpriteGroup *group; + const RealSpriteGroup *rsg; + byte new_random_bits; + + _vsg_random_triggers = trigger; + _vsg_bits_to_reseed = 0; + group = TriggerVehicleSpriteGroup(GetVehicleSpriteGroup(veh->engine_type, veh), veh, 0, + (resolve_callback) TriggerVehicleSpriteGroup); + + if (group == NULL && veh->cargo_type != GC_DEFAULT) { + // This group turned out to be empty but perhaps there'll be a default one. + group = TriggerVehicleSpriteGroup(engine_custom_sprites[veh->engine_type][GC_DEFAULT], veh, 0, + (resolve_callback) TriggerVehicleSpriteGroup); + } + + if (group == NULL) + return; + + 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; + + 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, VehicleTrigger trigger) +{ + if (trigger == VEHICLE_TRIGGER_DEPOT) { + // store that the vehicle entered a depot this tick + VehicleEnteredDepotThisTick(veh); + } + + DoTriggerVehicle(veh, trigger, 0, true); +} + +static char *_engine_custom_names[TOTAL_NUM_ENGINES]; + +void SetCustomEngineName(EngineID engine, const char *name) +{ + _engine_custom_names[engine] = strdup(name); +} + +void UnloadCustomEngineNames(void) +{ + char **i; + for (i = _engine_custom_names; i != endof(_engine_custom_names); i++) { + free(*i); + *i = NULL; + } +} + +StringID GetCustomEngineName(EngineID engine) +{ + if (!_engine_custom_names[engine]) + return _engine_name_strings[engine]; + ttd_strlcpy(_userstring, _engine_custom_names[engine], lengthof(_userstring)); + return STR_SPEC_USERSTRING; +} + diff --git a/newgrf_engine.h b/newgrf_engine.h new file mode 100644 index 000000000..1812bff9b --- /dev/null +++ b/newgrf_engine.h @@ -0,0 +1,73 @@ +/* $Id$ */ + +#ifndef NEWGRF_ENGINE_H +#define NEWGRF_ENGINE_H + +/** @file newgrf_engine.h + */ + +// This enum lists the implemented callbacks +// Use as argument for the GetCallBackResult function (see comments there) +enum CallbackID { + // Powered wagons, if the result is lower as 0x40 then the wagon is powered + // TODO: interpret the rest of the result, aka "visual effects" + CBID_WAGON_POWER = 0x10, + + // Vehicle length, returns the amount of 1/8's the vehicle is shorter + // only for train vehicles + CBID_VEH_LENGTH = 0x11, + + // Refit capacity, the passed vehicle needs to have its ->cargo_type set to + // the cargo we are refitting to, returns the new cargo capacity + CBID_REFIT_CAP = 0x15, + + CBID_ARTIC_ENGINE = 0x16, +}; + +// bit positions for rvi->callbackmask, indicates which callbacks are used by an engine +// (some callbacks are always used, and dont appear here) +enum CallbackMask { + CBM_WAGON_POWER = 0, + CBM_VEH_LENGTH = 1, + CBM_REFIT_CAP = 3, + CBM_ARTIC_ENGINE = 4, +}; + +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]; +VARDEF const CargoID _local_cargo_id_ctype[NUM_GLOBAL_CID]; +VARDEF const uint32 cargo_classes[16]; + +void SetWagonOverrideSprites(EngineID engine, struct SpriteGroup *group, byte *train_id, int trains); +void SetCustomEngineSprites(EngineID engine, byte cargo, struct SpriteGroup *group); +// loaded is in percents, overriding_engine 0xffff is none +int GetCustomEngineSprite(EngineID engine, const Vehicle *v, byte direction); +uint16 GetCallBackResult(uint16 callback_info, EngineID engine, const Vehicle *v); +bool UsesWagonOverride(const Vehicle *v); +#define GetCustomVehicleSprite(v, direction) GetCustomEngineSprite(v->engine_type, v, direction) +#define GetCustomVehicleIcon(et, direction) GetCustomEngineSprite(et, NULL, direction) + +typedef 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, +} VehicleTrigger; +void TriggerVehicle(Vehicle *veh, VehicleTrigger trigger); + +void SetCustomEngineName(EngineID engine, const char *name); +StringID GetCustomEngineName(EngineID engine); + +void UnloadWagonOverrides(void); +void UnloadCustomEngineSprites(void); +void UnloadCustomEngineNames(void); + +#endif /* NEWGRF_ENGINE_H */ diff --git a/openttd.dsp b/openttd.dsp index 898bbd6f4..4c0f9aebe 100644 --- a/openttd.dsp +++ b/openttd.dsp @@ -276,6 +276,10 @@ SOURCE=.\newgrf.c # End Source File # Begin Source File +SOURCE=.\newgrf_engine.c +# End Source File +# Begin Source File + SOURCE=.\npf.c # End Source File # Begin Source File @@ -618,6 +622,14 @@ SOURCE=.\network.h # End Source File # Begin Source File +SOURCE=.\newgrf.h +# End Source File +# Begin Source File + +SOURCE=.\newgrf_engine.h +# End Source File +# Begin Source File + SOURCE=.\news.h # End Source File # Begin Source File diff --git a/openttd.vcproj b/openttd.vcproj index 1ffebc8cb..35fffbd73 100644 --- a/openttd.vcproj +++ b/openttd.vcproj @@ -272,6 +272,9 @@ + + @@ -486,6 +489,9 @@ + + diff --git a/roadveh_cmd.c b/roadveh_cmd.c index f9fd96d87..510baa010 100644 --- a/roadveh_cmd.c +++ b/roadveh_cmd.c @@ -18,6 +18,7 @@ #include "sound.h" #include "depot.h" #include "vehicle_gui.h" +#include "newgrf_engine.h" static const uint16 _roadveh_images[63] = { 0xCD4, 0xCDC, 0xCE4, 0xCEC, 0xCF4, 0xCFC, 0xD0C, 0xD14, diff --git a/roadveh_gui.c b/roadveh_gui.c index c4d8017c1..e54488024 100644 --- a/roadveh_gui.c +++ b/roadveh_gui.c @@ -18,6 +18,7 @@ #include "engine.h" #include "depot.h" #include "vehicle_gui.h" +#include "newgrf_engine.h" /** * Draw the purchase info details of road vehicle at a given location. diff --git a/ship_cmd.c b/ship_cmd.c index 904ffafa3..3a80aca09 100644 --- a/ship_cmd.c +++ b/ship_cmd.c @@ -17,6 +17,7 @@ #include "npf.h" #include "depot.h" #include "vehicle_gui.h" +#include "newgrf_engine.h" static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D}; static const byte _ship_sometracks[4] = {0x19, 0x16, 0x25, 0x2A}; diff --git a/ship_gui.c b/ship_gui.c index 32c4822fe..8960c64f6 100644 --- a/ship_gui.c +++ b/ship_gui.c @@ -18,6 +18,7 @@ #include "engine.h" #include "depot.h" #include "vehicle_gui.h" +#include "newgrf_engine.h" /** * Draw the purchase info details of a ship at a given location. diff --git a/train_cmd.c b/train_cmd.c index 1de6dcebb..01e6554f5 100644 --- a/train_cmd.c +++ b/train_cmd.c @@ -22,6 +22,7 @@ #include "waypoint.h" #include "vehicle_gui.h" #include "train.h" +#include "newgrf_engine.h" static bool TrainCheckIfLineEnds(Vehicle *v); static void TrainController(Vehicle *v); diff --git a/train_gui.c b/train_gui.c index 8e91e24db..f71339092 100644 --- a/train_gui.c +++ b/train_gui.c @@ -19,6 +19,7 @@ #include "vehicle_gui.h" #include "depot.h" #include "train.h" +#include "newgrf_engine.h" int _traininfo_vehicle_pitch = 0; diff --git a/vehicle_gui.c b/vehicle_gui.c index 2b6eb2f65..169c3ce8b 100644 --- a/vehicle_gui.c +++ b/vehicle_gui.c @@ -19,6 +19,7 @@ #include "vehicle_gui.h" #include "viewport.h" #include "train.h" +#include "newgrf_engine.h" Sorting _sorting; -- cgit v1.2.3-70-g09d2