tron@2186: /* $Id$ */ tron@2186: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@1299: #include "debug.h" tron@2163: #include "functions.h" tron@2201: #include "string.h" tron@2201: #include "strings.h" truelight@0: #include "engine.h" peter1138@2982: #include "newgrf_callbacks.h" peter1138@2962: #include "newgrf_engine.h" darkvater@405: #include "sprite.h" tron@2159: #include "variables.h" peter1138@2708: #include "train.h" truelight@0: truelight@0: // TODO: We don't support cargo-specific wagon overrides. Pretty exotic... ;-) --pasky truelight@0: tron@1477: typedef struct WagonOverride { truelight@0: byte *train_id; truelight@0: int trains; peter1138@2488: SpriteGroup *group; tron@1477: } WagonOverride; truelight@0: tron@1477: typedef struct WagonOverrides { truelight@0: int overrides_count; tron@1477: WagonOverride *overrides; tron@1477: } WagonOverrides; truelight@0: tron@1477: static WagonOverrides _engine_wagon_overrides[TOTAL_NUM_ENGINES]; tron@1477: tron@2477: void SetWagonOverrideSprites(EngineID engine, SpriteGroup *group, byte *train_id, tron@1477: int trains) truelight@0: { tron@1477: WagonOverrides *wos; tron@1477: WagonOverride *wo; truelight@0: truelight@0: wos = &_engine_wagon_overrides[engine]; truelight@0: wos->overrides_count++; truelight@0: wos->overrides = realloc(wos->overrides, tron@1477: wos->overrides_count * sizeof(*wos->overrides)); truelight@193: truelight@0: wo = &wos->overrides[wos->overrides_count - 1]; darkvater@408: /* FIXME: If we are replacing an override, release original SpriteGroup darkvater@408: * to prevent leaks. But first we need to refcount the SpriteGroup. darkvater@408: * --pasky */ peter1138@2488: wo->group = group; truelight@0: wo->trains = trains; truelight@0: wo->train_id = malloc(trains); truelight@0: memcpy(wo->train_id, train_id, trains); truelight@0: } truelight@0: tron@2477: static const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, byte overriding_engine) truelight@0: { tron@2242: const WagonOverrides *wos = &_engine_wagon_overrides[engine]; truelight@0: int i; truelight@0: darkvater@408: // XXX: This could turn out to be a timesink on profiles. We could darkvater@408: // always just dedicate 65535 bytes for an [engine][train] trampoline darkvater@408: // for O(1). Or O(logMlogN) and searching binary tree or smt. like darkvater@408: // that. --pasky truelight@0: truelight@0: for (i = 0; i < wos->overrides_count; i++) { tron@2242: const WagonOverride *wo = &wos->overrides[i]; truelight@0: int j; truelight@0: truelight@0: for (j = 0; j < wo->trains; j++) { truelight@0: if (wo->train_id[j] == overriding_engine) peter1138@2488: return wo->group; truelight@0: } truelight@0: } truelight@0: return NULL; truelight@0: } truelight@0: peter1138@2491: /** peter1138@2491: * Unload all wagon override sprite groups. peter1138@2491: */ peter1138@2491: void UnloadWagonOverrides(void) peter1138@2491: { peter1138@2491: WagonOverrides *wos; peter1138@2491: WagonOverride *wo; peter1138@2491: EngineID engine; peter1138@2491: int i; peter1138@2491: peter1138@2491: for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) { peter1138@2491: wos = &_engine_wagon_overrides[engine]; peter1138@2491: for (i = 0; i < wos->overrides_count; i++) { peter1138@2491: wo = &wos->overrides[i]; peter1138@3595: wo->group = NULL; peter1138@2491: free(wo->train_id); peter1138@2491: } peter1138@2491: free(wos->overrides); peter1138@2491: wos->overrides_count = 0; peter1138@2491: wos->overrides = NULL; peter1138@2491: } peter1138@2491: } truelight@0: truelight@0: // 0 - 28 are cargos, 29 is default, 30 is the advert (purchase list) truelight@0: // (It isn't and shouldn't be like this in the GRF files since new cargo types truelight@0: // may appear in future - however it's more convenient to store it like this in truelight@0: // memory. --pasky) peter1138@2488: static SpriteGroup *engine_custom_sprites[TOTAL_NUM_ENGINES][NUM_GLOBAL_CID]; truelight@0: tron@2477: void SetCustomEngineSprites(EngineID engine, byte cargo, SpriteGroup *group) truelight@0: { peter1138@2491: if (engine_custom_sprites[engine][cargo] != NULL) { peter1138@3595: DEBUG(grf, 6)("SetCustomEngineSprites: engine `%d' cargo `%d' already has group -- replacing.", engine, cargo); peter1138@2491: } peter1138@2488: engine_custom_sprites[engine][cargo] = group; truelight@0: } truelight@0: peter1138@2491: /** peter1138@2491: * Unload all engine sprite groups. peter1138@2491: */ peter1138@2491: void UnloadCustomEngineSprites(void) peter1138@2491: { peter1138@2491: EngineID engine; peter1138@2491: CargoID cargo; peter1138@2491: peter1138@2491: for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) { peter1138@2491: for (cargo = 0; cargo < NUM_GLOBAL_CID; cargo++) { peter1138@3595: engine_custom_sprites[engine][cargo] = NULL; peter1138@2491: } peter1138@2491: } peter1138@2491: } peter1138@2491: peter1138@2708: static int MapOldSubType(const Vehicle *v) peter1138@2708: { peter1138@2708: if (v->type != VEH_Train) return v->subtype; peter1138@2708: if (IsTrainEngine(v)) return 0; peter1138@2708: if (IsFreeWagon(v)) return 4; peter1138@2708: return 2; peter1138@2708: } peter1138@2708: peter1138@3014: static int VehicleSpecificProperty(const Vehicle *v, byte var) { peter1138@3014: switch (v->type) { peter1138@3014: case VEH_Train: peter1138@3014: switch (var) { peter1138@3014: case 0x62: return v->u.rail.track; peter1138@3014: case 0x66: return v->u.rail.railtype; peter1138@3014: case 0x73: return v->u.rail.cached_veh_length; peter1138@3014: case 0x74: return v->u.rail.cached_power; peter1138@3014: case 0x75: return v->u.rail.cached_power & 0xFFFFFF; peter1138@3014: case 0x76: return v->u.rail.cached_power & 0xFFFF; peter1138@3014: case 0x77: return v->u.rail.cached_power & 0xFF; peter1138@3014: case 0x7C: return v->first->index; peter1138@3014: case 0x7D: return v->first->index & 0xFF; peter1138@3014: } peter1138@3014: break; peter1138@3014: peter1138@3014: case VEH_Road: peter1138@3014: switch (var) { peter1138@3014: case 0x62: return v->u.road.state; peter1138@3014: case 0x64: return v->u.road.blocked_ctr; peter1138@3014: case 0x65: return v->u.road.blocked_ctr & 0xFF; peter1138@3014: case 0x66: return v->u.road.overtaking; peter1138@3014: case 0x67: return v->u.road.overtaking_ctr; peter1138@3014: case 0x68: return v->u.road.crashed_ctr; peter1138@3014: case 0x69: return v->u.road.crashed_ctr & 0xFF; peter1138@3014: } peter1138@3014: break; peter1138@3014: peter1138@3014: case VEH_Aircraft: peter1138@3014: switch (var) { peter1138@3014: // case 0x62: XXX Need to convert from ottd to ttdp state peter1138@3014: case 0x63: return v->u.air.targetairport; peter1138@3014: // case 0x66: XXX peter1138@3014: } peter1138@3014: break; peter1138@3014: } peter1138@3014: peter1138@3014: DEBUG(grf, 1)("Unhandled vehicle property 0x%x (var 0x%x), type 0x%x", var, var + 0x80, v->type); peter1138@3014: peter1138@3014: return -1; peter1138@3014: } peter1138@3014: tron@2242: typedef SpriteGroup *(*resolve_callback)(const SpriteGroup *spritegroup, hackykid@1883: const Vehicle *veh, uint16 callback_info, void *resolve_func); /* XXX data pointer used as function pointer */ tron@445: tron@2242: static const SpriteGroup* ResolveVehicleSpriteGroup(const SpriteGroup *spritegroup, hackykid@1883: const Vehicle *veh, uint16 callback_info, resolve_callback resolve_func) tron@426: { peter1138@2488: if (spritegroup == NULL) peter1138@2488: return NULL; peter1138@2488: tron@445: //debug("spgt %d", spritegroup->type); tron@426: switch (spritegroup->type) { tron@426: case SGT_REAL: hackykid@1883: case SGT_CALLBACK: hackykid@1883: return spritegroup; tron@426: tron@426: case SGT_DETERMINISTIC: { tron@2242: const DeterministicSpriteGroup *dsg = &spritegroup->g.determ; tron@2242: const SpriteGroup *target; tron@426: int value = -1; tron@426: tron@433: //debug("[%p] Having fun resolving variable %x", veh, dsg->variable); hackykid@1883: if (dsg->variable == 0x0C) { hackykid@1883: /* Callback ID */ hackykid@1883: value = callback_info & 0xFF; peter1138@2602: } else if (dsg->variable == 0x10) { peter1138@2602: value = (callback_info >> 8) & 0xFF; hackykid@1883: } else if ((dsg->variable >> 6) == 0) { tron@426: /* General property */ tron@426: value = GetDeterministicSpriteValue(dsg->variable); tron@426: } else { tron@433: /* Vehicle-specific property. */ tron@433: tron@433: if (veh == NULL) { tron@433: /* We are in a purchase list of something, tron@433: * and we are checking for something undefined. tron@433: * That means we should get the first target tron@433: * (NOT the default one). */ tron@433: if (dsg->num_ranges > 0) { peter1138@2488: target = dsg->ranges[0].group; tron@433: } else { tron@433: target = dsg->default_group; tron@433: } hackykid@1883: return resolve_func(target, NULL, callback_info, resolve_func); tron@433: } tron@433: tron@426: if (dsg->var_scope == VSG_SCOPE_PARENT) { tron@426: /* First engine in the vehicle chain */ tron@433: if (veh->type == VEH_Train) tron@426: veh = GetFirstVehicleInChain(veh); tron@426: } tron@426: hackykid@1855: if (dsg->variable == 0x40 || dsg->variable == 0x41) { tron@433: if (veh->type == VEH_Train) { tron@2242: const Vehicle *u = GetFirstVehicleInChain(veh); tron@426: byte chain_before = 0, chain_after = 0; tron@426: tron@426: while (u != veh) { hackykid@1855: chain_before++; hackykid@1855: if (dsg->variable == 0x41 && u->engine_type != veh->engine_type) hackykid@1855: chain_before = 0; tron@426: u = u->next; tron@426: } hackykid@1855: while (u->next != NULL && (dsg->variable == 0x40 || u->next->engine_type == veh->engine_type)) { hackykid@1855: chain_after++; tron@426: u = u->next; tron@426: }; tron@426: pasky@1559: value = chain_before | chain_after << 8 pasky@1559: | (chain_before + chain_after) << 16; tron@426: } else { tron@426: value = 1; /* 1 vehicle in the chain */ tron@426: } tron@426: tron@426: } else { tron@426: // TTDPatch runs on little-endian arch; tron@426: // Variable is 0x80 + offset in TTD's vehicle structure tron@426: switch (dsg->variable - 0x80) { tron@555: #define veh_prop(id_, value_) case (id_): value = (value_); break tron@426: veh_prop(0x00, veh->type); peter1138@2708: veh_prop(0x01, MapOldSubType(veh)); tron@426: veh_prop(0x04, veh->index); tron@426: veh_prop(0x05, veh->index & 0xFF); tron@426: /* XXX? Is THIS right? */ tron@555: veh_prop(0x0A, PackOrder(&veh->current_order)); tron@555: veh_prop(0x0B, PackOrder(&veh->current_order) & 0xff); tron@426: veh_prop(0x0C, veh->num_orders); tron@426: veh_prop(0x0D, veh->cur_order_index); tron@426: veh_prop(0x10, veh->load_unload_time_rem); tron@426: veh_prop(0x11, veh->load_unload_time_rem & 0xFF); tron@426: veh_prop(0x12, veh->date_of_last_service); tron@426: veh_prop(0x13, veh->date_of_last_service & 0xFF); tron@426: veh_prop(0x14, veh->service_interval); tron@426: veh_prop(0x15, veh->service_interval & 0xFF); tron@426: veh_prop(0x16, veh->last_station_visited); tron@426: veh_prop(0x17, veh->tick_counter); tron@426: veh_prop(0x18, veh->max_speed); tron@426: veh_prop(0x19, veh->max_speed & 0xFF); peter1138@3300: veh_prop(0x1A, veh->x_pos); peter1138@3300: veh_prop(0x1B, veh->x_pos & 0xFF); peter1138@3300: veh_prop(0x1C, veh->y_pos); peter1138@3300: veh_prop(0x1D, veh->y_pos & 0xFF); peter1138@3300: veh_prop(0x1E, veh->z_pos); tron@426: veh_prop(0x1F, veh->direction); tron@426: veh_prop(0x28, veh->cur_image); tron@426: veh_prop(0x29, veh->cur_image & 0xFF); tron@426: veh_prop(0x32, veh->vehstatus); tron@426: veh_prop(0x33, veh->vehstatus); tron@426: veh_prop(0x34, veh->cur_speed); tron@426: veh_prop(0x35, veh->cur_speed & 0xFF); tron@426: veh_prop(0x36, veh->subspeed); tron@426: veh_prop(0x37, veh->acceleration); tron@426: veh_prop(0x39, veh->cargo_type); tron@426: veh_prop(0x3A, veh->cargo_cap); tron@426: veh_prop(0x3B, veh->cargo_cap & 0xFF); tron@426: veh_prop(0x3C, veh->cargo_count); tron@426: veh_prop(0x3D, veh->cargo_count & 0xFF); tron@426: veh_prop(0x3E, veh->cargo_source); // Probably useless; so what tron@426: veh_prop(0x3F, veh->cargo_days); tron@426: veh_prop(0x40, veh->age); tron@426: veh_prop(0x41, veh->age & 0xFF); tron@426: veh_prop(0x42, veh->max_age); tron@426: veh_prop(0x43, veh->max_age & 0xFF); tron@426: veh_prop(0x44, veh->build_year); tron@426: veh_prop(0x45, veh->unitnumber); tron@426: veh_prop(0x46, veh->engine_type); tron@426: veh_prop(0x47, veh->engine_type & 0xFF); tron@426: veh_prop(0x48, veh->spritenum); tron@426: veh_prop(0x49, veh->day_counter); tron@426: veh_prop(0x4A, veh->breakdowns_since_last_service); tron@426: veh_prop(0x4B, veh->breakdown_ctr); tron@426: veh_prop(0x4C, veh->breakdown_delay); tron@426: veh_prop(0x4D, veh->breakdown_chance); tron@426: veh_prop(0x4E, veh->reliability); tron@426: veh_prop(0x4F, veh->reliability & 0xFF); tron@426: veh_prop(0x50, veh->reliability_spd_dec); tron@426: veh_prop(0x51, veh->reliability_spd_dec & 0xFF); tron@426: veh_prop(0x52, veh->profit_this_year); tron@426: veh_prop(0x53, veh->profit_this_year & 0xFFFFFF); tron@426: veh_prop(0x54, veh->profit_this_year & 0xFFFF); tron@426: veh_prop(0x55, veh->profit_this_year & 0xFF); tron@426: veh_prop(0x56, veh->profit_last_year); tron@426: veh_prop(0x57, veh->profit_last_year & 0xFF); tron@426: veh_prop(0x58, veh->profit_last_year); tron@426: veh_prop(0x59, veh->profit_last_year & 0xFF); hackykid@1855: veh_prop(0x5A, veh->next == NULL ? INVALID_VEHICLE : veh->next->index); tron@426: veh_prop(0x5C, veh->value); tron@426: veh_prop(0x5D, veh->value & 0xFFFFFF); tron@426: veh_prop(0x5E, veh->value & 0xFFFF); tron@426: veh_prop(0x5F, veh->value & 0xFF); tron@426: veh_prop(0x60, veh->string_id); tron@426: veh_prop(0x61, veh->string_id & 0xFF); tron@426: peter1138@3014: veh_prop(0x72, 0); // XXX Refit cycle currently unsupported peter1138@3014: veh_prop(0x7A, veh->random_bits); peter1138@3014: veh_prop(0x7B, veh->waiting_triggers); peter1138@3014: #undef veh_prop tron@426: peter1138@3014: // Handle vehicle specific properties. peter1138@3014: default: value = VehicleSpecificProperty(veh, dsg->variable - 0x80); break; tron@426: } tron@426: } tron@426: } tron@426: tron@426: target = value != -1 ? EvalDeterministicSpriteGroup(dsg, value) : dsg->default_group; tron@445: //debug("Resolved variable %x: %d, %p", dsg->variable, value, callback); hackykid@1883: return resolve_func(target, veh, callback_info, resolve_func); tron@445: } tron@445: tron@445: case SGT_RANDOMIZED: { tron@2242: const RandomizedSpriteGroup *rsg = &spritegroup->g.random; tron@445: tron@445: if (veh == NULL) { tron@445: /* Purchase list of something. Show the first one. */ tron@445: assert(rsg->num_groups > 0); tron@445: //debug("going for %p: %d", rsg->groups[0], rsg->groups[0].type); peter1138@2488: return resolve_func(rsg->groups[0], NULL, callback_info, resolve_func); tron@445: } tron@445: tron@445: if (rsg->var_scope == VSG_SCOPE_PARENT) { tron@445: /* First engine in the vehicle chain */ tron@445: if (veh->type == VEH_Train) tron@445: veh = GetFirstVehicleInChain(veh); tron@445: } tron@445: hackykid@1883: return resolve_func(EvalRandomizedSpriteGroup(rsg, veh->random_bits), veh, callback_info, resolve_func); tron@426: } tron@426: tron@426: default: tron@445: error("I don't know how to handle such a spritegroup %d!", spritegroup->type); tron@426: return NULL; tron@426: } tron@426: } tron@426: tron@2477: static const SpriteGroup *GetVehicleSpriteGroup(EngineID engine, const Vehicle *v) truelight@0: { tron@2242: const SpriteGroup *group; Darkvater@3344: CargoID cargo = GC_PURCHASE; truelight@0: darkvater@414: if (v != NULL) { darkvater@414: cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; Darkvater@1802: assert(cargo != GC_INVALID); darkvater@414: } darkvater@414: peter1138@2488: group = engine_custom_sprites[engine][cargo]; darkvater@414: pasky@1560: if (v != NULL && v->type == VEH_Train) { tron@2242: const SpriteGroup *overset = GetWagonOverrideSpriteSet(engine, v->u.rail.first_engine); truelight@0: tron@1477: if (overset != NULL) group = overset; truelight@0: } truelight@193: tron@445: return group; tron@445: } tron@445: tron@3157: int GetCustomEngineSprite(EngineID engine, const Vehicle* v, Direction direction) tron@445: { tron@2242: const SpriteGroup *group; tron@2242: const RealSpriteGroup *rsg; Darkvater@3348: CargoID cargo = GC_PURCHASE; tron@445: byte loaded = 0; tron@445: bool in_motion = 0; tron@445: int totalsets, spriteset; tron@445: int r; tron@445: tron@445: if (v != NULL) { tron@445: int capacity = v->cargo_cap; tron@445: tron@445: cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; Darkvater@1802: assert(cargo != GC_INVALID); Darkvater@1802: tron@445: if (capacity == 0) capacity = 1; tron@445: loaded = (v->cargo_count * 100) / capacity; peter1138@2830: peter1138@2830: if (v->type == VEH_Train) { peter1138@2830: in_motion = GetFirstVehicleInChain(v)->current_order.type != OT_LOADING; peter1138@2830: } else { peter1138@2830: in_motion = v->current_order.type != OT_LOADING; peter1138@2830: } tron@445: } tron@445: tron@445: group = GetVehicleSpriteGroup(engine, v); hackykid@1883: group = ResolveVehicleSpriteGroup(group, v, 0, (resolve_callback) ResolveVehicleSpriteGroup); darkvater@408: peter1138@2488: if (group == NULL && cargo != GC_DEFAULT) { darkvater@369: // This group is empty but perhaps there'll be a default one. peter1138@2488: group = ResolveVehicleSpriteGroup(engine_custom_sprites[engine][GC_DEFAULT], v, 0, tron@445: (resolve_callback) ResolveVehicleSpriteGroup); truelight@0: } truelight@0: peter1138@2488: if (group == NULL) peter1138@2488: return 0; peter1138@2488: hackykid@1883: assert(group->type == SGT_REAL); hackykid@1883: rsg = &group->g.real; hackykid@1883: darkvater@408: if (!rsg->sprites_per_set) { darkvater@369: // This group is empty. This function users should therefore truelight@0: // look up the sprite number in _engine_original_sprites. truelight@0: return 0; truelight@0: } truelight@0: hackykid@1988: assert(rsg->sprites_per_set <= 8); hackykid@1988: direction %= rsg->sprites_per_set; truelight@0: darkvater@408: totalsets = in_motion ? rsg->loaded_count : rsg->loading_count; truelight@0: truelight@0: // My aim here is to make it possible to visually determine absolutely truelight@0: // empty and totally full vehicles. --pasky truelight@0: if (loaded == 100 || totalsets == 1) { // full truelight@0: spriteset = totalsets - 1; truelight@0: } else if (loaded == 0 || totalsets == 2) { // empty truelight@0: spriteset = 0; truelight@0: } else { // something inbetween truelight@0: spriteset = loaded * (totalsets - 2) / 100 + 1; truelight@0: // correct possible rounding errors truelight@0: if (!spriteset) truelight@0: spriteset = 1; truelight@0: else if (spriteset == totalsets - 1) truelight@0: spriteset--; truelight@0: } truelight@0: peter1138@2489: r = (in_motion ? rsg->loaded[spriteset]->g.result.result : rsg->loading[spriteset]->g.result.result) + direction; truelight@0: return r; truelight@0: } truelight@0: hackykid@1883: /** hackykid@1908: * Check if a wagon is currently using a wagon override hackykid@1908: * @param v The wagon to check hackykid@1908: * @return true if it is using an override, false otherwise hackykid@1908: */ tron@2548: bool UsesWagonOverride(const Vehicle* v) tron@2548: { hackykid@1908: assert(v->type == VEH_Train); tron@2242: return GetWagonOverrideSpriteSet(v->engine_type, v->u.rail.first_engine) != NULL; hackykid@1908: } hackykid@1908: hackykid@1908: /** peter1138@3390: * Evaluate a newgrf callback for vehicles peter1138@3390: * @param callback The callback to evalute peter1138@3390: * @param param1 First parameter of the callback peter1138@3390: * @param param2 Second parameter of the callback peter1138@3390: * @param engine Engine type of the vehicle to evaluate the callback for peter1138@3390: * @param vehicle The vehicle to evaluate the callback for, or NULL if it doesnt exist yet hackykid@1883: * @return The value the callback returned, or CALLBACK_FAILED if it failed hackykid@1883: */ peter1138@3390: uint16 GetVehicleCallback(byte callback, uint32 param1, uint32 param2, EngineID engine, const Vehicle *v) hackykid@1883: { tron@2242: const SpriteGroup *group; Darkvater@3348: CargoID cargo = GC_DEFAULT; peter1138@3390: uint16 callback_info = callback | (param1 << 8); // XXX Temporary conversion between new and old format. hackykid@1883: hackykid@1883: if (v != NULL) hackykid@1883: cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; hackykid@1883: peter1138@2488: group = engine_custom_sprites[engine][cargo]; hackykid@1904: hackykid@1904: if (v != NULL && v->type == VEH_Train) { tron@2242: const SpriteGroup *overset = GetWagonOverrideSpriteSet(engine, v->u.rail.first_engine); hackykid@1904: hackykid@1904: if (overset != NULL) group = overset; hackykid@1904: } hackykid@1904: hackykid@1883: group = ResolveVehicleSpriteGroup(group, v, callback_info, (resolve_callback) ResolveVehicleSpriteGroup); hackykid@1883: peter1138@2488: if (group == NULL && cargo != GC_DEFAULT) { hackykid@1883: // This group is empty but perhaps there'll be a default one. peter1138@2488: group = ResolveVehicleSpriteGroup(engine_custom_sprites[engine][GC_DEFAULT], v, callback_info, hackykid@1883: (resolve_callback) ResolveVehicleSpriteGroup); hackykid@1883: } hackykid@1883: peter1138@2488: if (group == NULL || group->type != SGT_CALLBACK) hackykid@1883: return CALLBACK_FAILED; hackykid@1883: hackykid@1883: return group->g.callback.result; hackykid@1883: } hackykid@1883: hackykid@1883: truelight@0: tron@445: // Global variables are evil, yes, but we would end up with horribly overblown tron@445: // calling convention otherwise and this should be 100% reentrant. tron@445: static byte _vsg_random_triggers; tron@445: static byte _vsg_bits_to_reseed; tron@445: tron@2242: static const SpriteGroup *TriggerVehicleSpriteGroup(const SpriteGroup *spritegroup, hackykid@1883: Vehicle *veh, uint16 callback_info, resolve_callback resolve_func) tron@445: { peter1138@2488: if (spritegroup == NULL) peter1138@2488: return NULL; peter1138@2488: tron@1477: if (spritegroup->type == SGT_RANDOMIZED) { tron@1477: _vsg_bits_to_reseed |= RandomizedSpriteGroupTriggeredBits( tron@1477: &spritegroup->g.random, tron@1477: _vsg_random_triggers, tron@1477: &veh->waiting_triggers tron@1477: ); tron@1477: } tron@445: hackykid@1883: return ResolveVehicleSpriteGroup(spritegroup, veh, callback_info, resolve_func); tron@445: } tron@445: tron@1477: static void DoTriggerVehicle(Vehicle *veh, VehicleTrigger trigger, byte base_random_bits, bool first) tron@445: { tron@2242: const SpriteGroup *group; tron@2242: const RealSpriteGroup *rsg; tron@445: byte new_random_bits; tron@445: tron@445: _vsg_random_triggers = trigger; tron@445: _vsg_bits_to_reseed = 0; hackykid@1883: group = TriggerVehicleSpriteGroup(GetVehicleSpriteGroup(veh->engine_type, veh), veh, 0, hackykid@1883: (resolve_callback) TriggerVehicleSpriteGroup); hackykid@1883: peter1138@2488: if (group == NULL && veh->cargo_type != GC_DEFAULT) { tron@445: // This group turned out to be empty but perhaps there'll be a default one. peter1138@2488: group = TriggerVehicleSpriteGroup(engine_custom_sprites[veh->engine_type][GC_DEFAULT], veh, 0, hackykid@1883: (resolve_callback) TriggerVehicleSpriteGroup); tron@445: } hackykid@1883: peter1138@2488: if (group == NULL) peter1138@2488: return; peter1138@2488: hackykid@1883: assert(group->type == SGT_REAL); hackykid@1883: rsg = &group->g.real; hackykid@1883: truelight@542: new_random_bits = Random(); tron@445: veh->random_bits &= ~_vsg_bits_to_reseed; truelight@542: veh->random_bits |= (first ? new_random_bits : base_random_bits) & _vsg_bits_to_reseed; tron@445: tron@445: switch (trigger) { tron@445: case VEHICLE_TRIGGER_NEW_CARGO: tron@445: /* All vehicles in chain get ANY_NEW_CARGO trigger now. tron@445: * So we call it for the first one and they will recurse. */ tron@445: /* Indexing part of vehicle random bits needs to be tron@445: * same for all triggered vehicles in the chain (to get tron@445: * all the random-cargo wagons carry the same cargo, tron@445: * i.e.), so we give them all the NEW_CARGO triggered tron@445: * vehicle's portion of random bits. */ tron@445: assert(first); tron@445: DoTriggerVehicle(GetFirstVehicleInChain(veh), VEHICLE_TRIGGER_ANY_NEW_CARGO, new_random_bits, false); tron@445: break; tron@445: case VEHICLE_TRIGGER_DEPOT: tron@445: /* We now trigger the next vehicle in chain recursively. tron@445: * The random bits portions may be different for each tron@445: * vehicle in chain. */ tron@445: if (veh->next != NULL) tron@445: DoTriggerVehicle(veh->next, trigger, 0, true); tron@445: break; tron@445: case VEHICLE_TRIGGER_EMPTY: tron@445: /* We now trigger the next vehicle in chain tron@445: * recursively. The random bits portions must be same tron@445: * for each vehicle in chain, so we give them all tron@445: * first chained vehicle's portion of random bits. */ tron@445: if (veh->next != NULL) tron@445: DoTriggerVehicle(veh->next, trigger, first ? new_random_bits : base_random_bits, false); tron@445: break; tron@445: case VEHICLE_TRIGGER_ANY_NEW_CARGO: tron@445: /* Now pass the trigger recursively to the next vehicle tron@445: * in chain. */ tron@445: assert(!first); tron@445: if (veh->next != NULL) tron@445: DoTriggerVehicle(veh->next, VEHICLE_TRIGGER_ANY_NEW_CARGO, base_random_bits, false); tron@445: break; tron@445: } tron@445: } tron@445: tron@1477: void TriggerVehicle(Vehicle *veh, VehicleTrigger trigger) tron@445: { peter1138@2589: if (trigger == VEHICLE_TRIGGER_DEPOT) { peter1138@2589: // store that the vehicle entered a depot this tick peter1138@2589: VehicleEnteredDepotThisTick(veh); peter1138@2589: } peter1138@2589: tron@445: DoTriggerVehicle(veh, trigger, 0, true); tron@445: } tron@445: Darkvater@1474: static char *_engine_custom_names[TOTAL_NUM_ENGINES]; truelight@0: tron@2477: void SetCustomEngineName(EngineID engine, const char *name) truelight@0: { truelight@0: _engine_custom_names[engine] = strdup(name); truelight@0: } truelight@0: peter1138@2769: void UnloadCustomEngineNames(void) Darkvater@1474: { Darkvater@1474: char **i; Darkvater@1474: for (i = _engine_custom_names; i != endof(_engine_custom_names); i++) { Darkvater@1474: free(*i); Darkvater@1474: *i = NULL; Darkvater@1474: } Darkvater@1474: } Darkvater@1474: tron@2477: StringID GetCustomEngineName(EngineID engine) truelight@0: { truelight@0: if (!_engine_custom_names[engine]) truelight@0: return _engine_name_strings[engine]; tron@2201: ttd_strlcpy(_userstring, _engine_custom_names[engine], lengthof(_userstring)); truelight@0: return STR_SPEC_USERSTRING; truelight@0: } truelight@0: peter1138@2971: // Functions for changing the order of vehicle purchase lists peter1138@2971: // This is currently only implemented for rail vehicles. peter1138@2971: static EngineID engine_list_order[NUM_TRAIN_ENGINES]; peter1138@2971: peter1138@2971: void ResetEngineListOrder(void) peter1138@2971: { peter1138@2971: EngineID i; peter1138@2971: peter1138@2971: for (i = 0; i < NUM_TRAIN_ENGINES; i++) peter1138@2971: engine_list_order[i] = i; peter1138@2971: } peter1138@2971: peter1138@2971: EngineID GetRailVehAtPosition(EngineID pos) peter1138@2971: { peter1138@2971: return engine_list_order[pos]; peter1138@2971: } peter1138@2971: peter1138@2971: void AlterRailVehListOrder(EngineID engine, EngineID target) peter1138@2971: { peter1138@2971: EngineID i; peter1138@2971: bool moving = false; peter1138@2971: peter1138@2971: if (engine == target) return; peter1138@2971: peter1138@2971: // First, remove our ID from the list. peter1138@2971: for (i = 0; i < NUM_TRAIN_ENGINES - 1; i++) { peter1138@2971: if (engine_list_order[i] == engine) peter1138@2971: moving = true; peter1138@2971: if (moving) peter1138@2971: engine_list_order[i] = engine_list_order[i + 1]; peter1138@2971: } peter1138@2971: peter1138@2971: // Now, insert it again, before the target engine. peter1138@2971: for (i = NUM_TRAIN_ENGINES - 1; i > 0; i--) { peter1138@2971: engine_list_order[i] = engine_list_order[i - 1]; peter1138@2971: if (engine_list_order[i] == target) { peter1138@2971: engine_list_order[i - 1] = engine; peter1138@2971: break; peter1138@2971: } peter1138@2971: } peter1138@2971: }