truelight@0: #include "stdafx.h" truelight@0: #include "ttd.h" truelight@0: #include "engine.h" truelight@0: #include "table/engines.h" truelight@0: #include "player.h" truelight@0: #include "command.h" truelight@0: #include "vehicle.h" truelight@0: #include "news.h" truelight@0: #include "saveload.h" truelight@0: truelight@0: #define UPDATE_PLAYER_RAILTYPE(e,p) if ((byte)(e->railtype + 1) > p->max_railtype) p->max_railtype = e->railtype + 1; truelight@0: truelight@0: enum { truelight@0: ENGINE_AVAILABLE = 1, truelight@0: ENGINE_INTRODUCING = 2, truelight@0: ENGINE_PREVIEWING = 4, truelight@0: }; truelight@0: truelight@0: /* This maps per-landscape cargo ids to globally unique cargo ids usable ie. in truelight@0: * the custom GRF files. It is basically just a transcribed table from truelight@0: * TTDPatch's newgrf.txt. */ truelight@0: byte _global_cargo_id[NUM_LANDSCAPE][NUM_CARGO] = { truelight@0: /* LT_NORMAL */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12 }, truelight@0: /* LT_HILLY */ { 0, 1, 2, 3, 4, 5, 6, 7, 28, 11, 10, 12 }, truelight@0: /* LT_DESERT */ { 0, 16, 2, 3, 13, 5, 6, 7, 14, 15, 10, 12 }, truelight@0: /* LT_CANDY */ { 0, 17, 2, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, truelight@0: // 27 is paper in temperate climate in TTDPatch truelight@0: // Following can be renumbered: truelight@0: // 29 is the default cargo for the purpose of spritesets truelight@0: // 30 is the purchase list image (the equivalent of 0xff) for the purpose of spritesets truelight@0: }; truelight@0: truelight@0: /* These two arrays provide a reverse mapping. */ truelight@0: byte _local_cargo_id_ctype[NUM_CID] = { truelight@0: CT_PASSENGERS, CT_COAL, CT_MAIL, CT_OIL, CT_LIVESTOCK, CT_GOODS, CT_GRAIN, CT_WOOD, // 0-7 truelight@0: CT_IRON_ORE, CT_STEEL, CT_VALUABLES, CT_PAPER, CT_FOOD, CT_FRUIT, CT_COPPER_ORE, CT_WATER, // 8-15 truelight@0: CT_RUBBER, CT_SUGAR, CT_TOYS, CT_BATTERIES, CT_CANDY, CT_TOFFEE, CT_COLA, CT_COTTON_CANDY, // 16-23 truelight@0: CT_BUBBLES, CT_PLASTIC, CT_FIZZY_DRINKS, CT_PAPER /* unsup. */, CT_HILLY_UNUSED // 24-28 truelight@0: }; truelight@0: truelight@0: /* LT'th bit is set of the particular landscape if cargo available there. truelight@0: * 1: LT_NORMAL, 2: LT_HILLY, 4: LT_DESERT, 8: LT_CANDY */ truelight@0: byte _local_cargo_id_landscape[NUM_CID] = { truelight@0: 15, 3, 15, 7, 3, 7, 7, 7, 1, 1, 7, 2, 7, // 0-12 truelight@0: 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 2, // 13-28 truelight@0: }; truelight@0: truelight@0: void ShowEnginePreviewWindow(int engine); truelight@0: truelight@0: void DeleteCustomEngineNames() truelight@0: { truelight@0: uint i; truelight@0: StringID old; truelight@0: truelight@0: for(i=0; i!=TOTAL_NUM_ENGINES; i++) { truelight@0: old = _engine_name_strings[i]; truelight@0: _engine_name_strings[i] = i + STR_8000_KIRBY_PAUL_TANK_STEAM; truelight@0: DeleteName(old); truelight@0: } truelight@0: truelight@0: _vehicle_design_names &= ~1; truelight@0: } truelight@0: truelight@0: void LoadCustomEngineNames() truelight@0: { truelight@0: // XXX: not done */ truelight@0: DEBUG(misc, 1) ("LoadCustomEngineNames: not done"); truelight@0: } truelight@0: truelight@0: static void SetupEngineNames() truelight@0: { truelight@0: uint i; truelight@0: truelight@0: for(i=0; i!=TOTAL_NUM_ENGINES; i++) truelight@0: _engine_name_strings[i] = STR_SV_EMPTY; truelight@0: truelight@0: DeleteCustomEngineNames(); truelight@0: LoadCustomEngineNames(); truelight@0: } truelight@0: truelight@0: static void AdjustAvailAircraft() truelight@0: { truelight@0: uint16 date = _date; truelight@0: byte avail = 0; truelight@0: if (date >= 12784) avail |= 2; // big airport truelight@0: if (date < 14610 || _patches.always_small_airport) avail |= 1; // small airport truelight@0: if (date >= 15706) avail |= 4; // enable heliport truelight@0: truelight@0: if (avail != _avail_aircraft) { truelight@0: _avail_aircraft = avail; truelight@0: InvalidateWindow(WC_BUILD_STATION, 0); truelight@0: } truelight@0: } truelight@0: truelight@0: static void CalcEngineReliability(Engine *e) truelight@0: { truelight@0: uint age = e->age; truelight@0: truelight@0: if (age < e->duration_phase_1) { truelight@0: uint start = e->reliability_start; truelight@0: e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start; truelight@0: } else if ((age -= e->duration_phase_1) < e->duration_phase_2) { truelight@0: e->reliability = e->reliability_max; truelight@0: } else if ((age -= e->duration_phase_2) < e->duration_phase_3) { truelight@0: uint max = e->reliability_max; truelight@0: e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max; truelight@0: } else { dominik@292: // time's up for this engine dominik@292: // make it either available to all players (if never_expire_vehicles is enabled and if it was available earlier) dominik@292: // or disable this engine completely dominik@292: e->player_avail = (_patches.never_expire_vehicles && e->player_avail)? -1 : 0; truelight@0: e->reliability = e->reliability_final; truelight@0: } truelight@0: } truelight@0: truelight@0: void StartupEngines() truelight@0: { truelight@0: Engine *e; truelight@0: const EngineInfo *ei; truelight@0: uint32 r; truelight@0: truelight@0: SetupEngineNames(); truelight@0: truelight@0: truelight@0: for(e=_engines, ei=_engine_info; e != endof(_engines); e++,ei++) { truelight@0: e->age = 0; truelight@0: e->railtype = ei->railtype_climates >> 4; truelight@0: e->flags = 0; truelight@0: e->player_avail = 0; truelight@193: truelight@0: r = Random(); truelight@0: e->intro_date = (uint16)((r & 0x1FF) + ei->base_intro); truelight@0: if (e->intro_date <= _date) { truelight@0: e->age = (_date - e->intro_date) >> 5; truelight@0: e->player_avail = (byte)-1; truelight@0: e->flags |= ENGINE_AVAILABLE; truelight@0: } truelight@0: truelight@0: e->reliability_start = (uint16)(((r >> 16) & 0x3fff) + 0x7AE0); truelight@0: r = Random(); truelight@0: e->reliability_max = (uint16)((r & 0x3fff) + 0xbfff); truelight@0: e->reliability_final = (uint16)(((r>>16) & 0x3fff) + 0x3fff); truelight@0: truelight@0: r = Random(); truelight@0: e->duration_phase_1 = (uint16)((r & 0x1F) + 7); truelight@0: e->duration_phase_2 = (uint16)(((r >> 5) & 0xF) + ei->base_life * 12 - 96); truelight@0: e->duration_phase_3 = (uint16)(((r >> 9) & 0x7F) + 120); truelight@0: truelight@0: e->reliability_spd_dec = (ei->unk2&0x7F) << 2; truelight@0: truelight@0: /* my invented flag for something that is a wagon */ truelight@0: if (ei->unk2 & 0x80) { truelight@0: e->age = 0xFFFF; truelight@0: } else { truelight@0: CalcEngineReliability(e); truelight@0: } truelight@0: truelight@0: e->lifelength = ei->lifelength + _patches.extend_vehicle_life; truelight@0: truelight@0: // prevent certain engines from ever appearing. truelight@0: if (!HASBIT(ei->railtype_climates, _opt.landscape)) { truelight@0: e->flags |= ENGINE_AVAILABLE; truelight@0: e->player_avail = 0; truelight@0: } truelight@0: } truelight@0: truelight@0: AdjustAvailAircraft(); truelight@0: } truelight@0: truelight@0: uint32 _engine_refit_masks[256]; truelight@0: truelight@0: truelight@0: // TODO: We don't support cargo-specific wagon overrides. Pretty exotic... ;-) --pasky truelight@0: truelight@0: struct WagonOverride { truelight@0: byte *train_id; truelight@0: int trains; darkvater@369: struct SpriteGroup group; truelight@0: }; truelight@0: truelight@0: static struct WagonOverrides { truelight@0: int overrides_count; truelight@0: struct WagonOverride *overrides; truelight@0: } _engine_wagon_overrides[256]; truelight@0: darkvater@369: void SetWagonOverrideSprites(byte engine, struct SpriteGroup *group, truelight@0: byte *train_id, int trains) truelight@0: { truelight@0: struct WagonOverrides *wos; truelight@0: struct WagonOverride *wo; truelight@0: truelight@0: wos = &_engine_wagon_overrides[engine]; truelight@0: wos->overrides_count++; truelight@0: wos->overrides = realloc(wos->overrides, truelight@0: wos->overrides_count * sizeof(struct WagonOverride)); truelight@193: truelight@0: wo = &wos->overrides[wos->overrides_count - 1]; darkvater@369: 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: darkvater@369: static struct SpriteGroup *GetWagonOverrideSpriteSet(byte engine, byte overriding_engine) truelight@0: { truelight@0: struct WagonOverrides *wos = &_engine_wagon_overrides[engine]; truelight@0: int i; truelight@0: truelight@0: // XXX: This could turn out to be a timesink on profiles. We could always just truelight@0: // dedicate 65535 bytes for an [engine][train] trampoline. truelight@0: truelight@0: for (i = 0; i < wos->overrides_count; i++) { truelight@0: struct 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) darkvater@369: return &wo->group; truelight@0: } truelight@0: } truelight@0: return NULL; truelight@0: } truelight@0: truelight@0: truelight@0: byte _engine_original_sprites[256]; 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) darkvater@369: static struct SpriteGroup _engine_custom_sprites[256][NUM_CID]; truelight@0: darkvater@369: void SetCustomEngineSprites(byte engine, byte cargo, struct SpriteGroup *group) truelight@0: { darkvater@369: assert(group->sprites_per_set == 4 || group->sprites_per_set == 8); darkvater@369: _engine_custom_sprites[engine][cargo] = *group; truelight@0: } truelight@0: truelight@0: int GetCustomEngineSprite(byte engine, uint16 overriding_engine, byte cargo, truelight@0: byte loaded, byte in_motion, byte direction) truelight@0: { darkvater@369: struct SpriteGroup *group = &_engine_custom_sprites[engine][cargo]; truelight@0: int totalsets, spriteset; truelight@0: int r; truelight@0: truelight@0: if (overriding_engine != 0xffff) { darkvater@369: struct SpriteGroup *overset; truelight@0: truelight@0: overset = GetWagonOverrideSpriteSet(engine, overriding_engine); darkvater@369: if (overset) group = overset; truelight@0: } truelight@193: darkvater@369: if (!group->sprites_per_set && cargo != 29) { darkvater@369: // This group is empty but perhaps there'll be a default one. darkvater@369: group = &_engine_custom_sprites[engine][29]; truelight@0: } truelight@0: darkvater@369: if (!group->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: truelight@0: direction %= 8; darkvater@369: if (group->sprites_per_set == 4) truelight@0: direction %= 4; truelight@0: darkvater@369: totalsets = in_motion ? group->loaded_count : group->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: darkvater@369: r = (in_motion ? group->loaded[spriteset] : group->loading[spriteset]) + direction; truelight@0: return r; truelight@0: } truelight@0: truelight@0: truelight@0: static char *_engine_custom_names[256]; truelight@0: truelight@0: void SetCustomEngineName(int engine, char *name) truelight@0: { truelight@0: _engine_custom_names[engine] = strdup(name); truelight@0: } truelight@0: truelight@0: StringID GetCustomEngineName(int engine) truelight@0: { truelight@0: if (!_engine_custom_names[engine]) truelight@0: return _engine_name_strings[engine]; truelight@0: strcpy(_userstring, _engine_custom_names[engine]); truelight@0: return STR_SPEC_USERSTRING; truelight@0: } truelight@0: truelight@0: truelight@0: void AcceptEnginePreview(Engine *e, int player) truelight@0: { truelight@0: Player *p; truelight@0: truelight@0: SETBIT(e->player_avail, player); truelight@0: truelight@0: p = DEREF_PLAYER(player); truelight@193: truelight@0: UPDATE_PLAYER_RAILTYPE(e,p); truelight@0: truelight@0: e->preview_player = 0xFF; truelight@0: InvalidateWindowClasses(WC_BUILD_VEHICLE); truelight@0: } truelight@0: truelight@0: void EnginesDailyLoop() truelight@0: { truelight@0: Engine *e; truelight@0: int i,num; truelight@0: Player *p; truelight@0: uint mask; truelight@0: int32 best_hist; truelight@0: int best_player; truelight@0: truelight@0: if (_cur_year >= 130) truelight@0: return; truelight@0: truelight@0: for(e=_engines,i=0; i!=TOTAL_NUM_ENGINES; e++,i++) { truelight@0: if (e->flags & ENGINE_INTRODUCING) { truelight@0: if (e->flags & ENGINE_PREVIEWING) { truelight@0: if (!--e->preview_wait) { truelight@0: e->flags &= ~ENGINE_PREVIEWING; truelight@0: DeleteWindowById(WC_ENGINE_PREVIEW, i); truelight@0: e->preview_player++; truelight@193: } truelight@0: } else if (e->preview_player != 0xFF) { truelight@0: num = e->preview_player; truelight@0: mask = 0; truelight@0: do { truelight@0: best_hist = -1; truelight@0: best_player = -1; truelight@0: FOR_ALL_PLAYERS(p) { truelight@193: if (p->is_active && p->block_preview == 0 && !HASBIT(mask,p->index) && truelight@0: p->old_economy[0].performance_history > best_hist) { truelight@0: best_hist = p->old_economy[0].performance_history; truelight@0: best_player = p->index; truelight@0: } truelight@0: } truelight@0: if (best_player == -1) { truelight@0: e->preview_player = 0xFF; truelight@0: goto next_engine; truelight@0: } truelight@0: mask |= (1 << best_player); truelight@0: } while (--num != 0); truelight@193: truelight@0: if (!IS_HUMAN_PLAYER(best_player)) { truelight@0: /* TTDBUG: TTD has a bug here */ truelight@0: AcceptEnginePreview(e, best_player); truelight@0: } else { truelight@0: e->flags |= ENGINE_PREVIEWING; truelight@0: e->preview_wait = 20; truelight@0: if (IS_INTERACTIVE_PLAYER(best_player)) { truelight@193: ShowEnginePreviewWindow(i); truelight@0: } truelight@0: } truelight@0: } truelight@0: } truelight@0: next_engine:; truelight@0: } truelight@0: } truelight@0: truelight@0: int32 CmdWantEnginePreview(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: if (flags & DC_EXEC) { truelight@0: AcceptEnginePreview(&_engines[p1], _current_player); truelight@0: } truelight@0: return 0; truelight@0: } truelight@0: dominik@257: // Determine if an engine type is a wagon (and not a loco) dominik@257: bool isWagon(byte index) dominik@257: { dominik@257: if (index < NUM_TRAIN_ENGINES) { dominik@257: const RailVehicleInfo *rvi = &_rail_vehicle_info[index]; dominik@257: if(rvi->flags & RVI_WAGON) dominik@257: return true; dominik@257: } dominik@257: return false; dominik@257: } dominik@257: truelight@0: void NewVehicleAvailable(Engine *e) truelight@0: { truelight@0: Vehicle *v; truelight@0: Player *p; truelight@0: int index = e - _engines; truelight@0: truelight@0: // In case the player didn't build the vehicle during the intro period, truelight@0: // prevent that player from getting future intro periods for a while. truelight@0: if (e->flags&ENGINE_INTRODUCING) { truelight@0: FOR_ALL_PLAYERS(p) { truelight@0: if (!HASBIT(e->player_avail,p->index)) truelight@0: continue; truelight@193: truelight@0: for(v=_vehicles;;) { truelight@0: if (v->type == VEH_Train || v->type == VEH_Road || v->type == VEH_Ship || truelight@0: (v->type == VEH_Aircraft && v->subtype <= 2)) { truelight@0: if (v->owner == p->index && v->engine_type == index) break; truelight@0: } truelight@0: if (++v == endof(_vehicles)) { truelight@0: p->block_preview = 20; truelight@0: break; truelight@0: } truelight@0: } truelight@0: } truelight@0: } truelight@0: dominik@114: e->flags = (e->flags & ~ENGINE_INTRODUCING) | ENGINE_AVAILABLE; dominik@114: InvalidateWindowClasses(WC_BUILD_VEHICLE); dominik@114: truelight@0: // Now available for all players truelight@0: e->player_avail = (byte)-1; dominik@114: dominik@114: // Do not introduce new rail wagons dominik@257: if(isWagon(index)) dominik@257: return; dominik@114: dominik@114: // make maglev / monorail available truelight@0: FOR_ALL_PLAYERS(p) { truelight@0: if (p->is_active) truelight@0: UPDATE_PLAYER_RAILTYPE(e,p); truelight@0: } truelight@0: truelight@0: if ((byte)index < NUM_TRAIN_ENGINES) { truelight@0: AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_TRAINAVAIL), 0, 0); truelight@0: } else if ((byte)index < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES) { truelight@0: AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_ROADAVAIL), 0, 0); truelight@0: } else if ((byte)index < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES) { truelight@0: AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_SHIPAVAIL), 0, 0); truelight@0: } else { truelight@0: AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_AIRCRAFTAVAIL), 0, 0); truelight@0: } truelight@0: } truelight@0: truelight@0: void EnginesMonthlyLoop() truelight@0: { truelight@0: Engine *e; truelight@0: truelight@0: if (_cur_year < 130) { truelight@0: for(e=_engines; e != endof(_engines); e++) { truelight@0: // Age the vehicle truelight@0: if (e->flags&ENGINE_AVAILABLE && e->age != 0xFFFF) { truelight@0: e->age++; truelight@0: CalcEngineReliability(e); truelight@0: } truelight@0: truelight@0: if (!(e->flags & ENGINE_AVAILABLE) && (uint16)(_date - min(_date, 365)) >= e->intro_date) { truelight@0: // Introduce it to all players truelight@0: NewVehicleAvailable(e); truelight@0: } else if (!(e->flags & (ENGINE_AVAILABLE|ENGINE_INTRODUCING)) && _date >= e->intro_date) { truelight@0: // Introduction date has passed.. show introducing dialog to one player. truelight@0: e->flags |= ENGINE_INTRODUCING; dominik@257: dominik@257: // Do not introduce new rail wagons dominik@257: if(!isWagon(e - _engines)) dominik@257: e->preview_player = 1; // Give to the player with the highest rating. truelight@0: } truelight@0: } truelight@0: } truelight@0: AdjustAvailAircraft(); truelight@0: } truelight@0: truelight@0: int32 CmdRenameEngine(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: StringID str; truelight@0: truelight@0: str = AllocateName((byte*)_decode_parameters, 0); truelight@0: if (str == 0) truelight@0: return CMD_ERROR; truelight@193: truelight@0: if (flags & DC_EXEC) { truelight@0: StringID old_str = _engine_name_strings[p1]; truelight@0: _engine_name_strings[p1] = str; truelight@0: DeleteName(old_str); truelight@0: _vehicle_design_names |= 3; truelight@0: MarkWholeScreenDirty(); truelight@0: } else { truelight@0: DeleteName(str); truelight@0: } truelight@0: truelight@0: return 0; truelight@0: } truelight@0: truelight@0: int GetPlayerMaxRailtype(int p) truelight@0: { truelight@0: Engine *e; truelight@0: int rt = 0; truelight@0: int i; truelight@0: truelight@0: for(e=_engines,i=0; i!=lengthof(_engines); e++,i++) { truelight@0: if (!HASBIT(e->player_avail, p)) truelight@0: continue; truelight@0: truelight@0: if ((i >= 27 && i < 54) || (i >= 57 && i < 84) || (i >= 89 && i < 116)) truelight@0: continue; truelight@0: truelight@0: if (rt < e->railtype) truelight@0: rt = e->railtype; truelight@0: } truelight@0: truelight@0: return rt + 1; truelight@0: } truelight@0: truelight@0: truelight@0: static const byte _engine_desc[] = { truelight@0: SLE_VAR(Engine,intro_date, SLE_UINT16), truelight@0: SLE_VAR(Engine,age, SLE_UINT16), truelight@0: SLE_VAR(Engine,reliability, SLE_UINT16), truelight@0: SLE_VAR(Engine,reliability_spd_dec, SLE_UINT16), truelight@0: SLE_VAR(Engine,reliability_start, SLE_UINT16), truelight@0: SLE_VAR(Engine,reliability_max, SLE_UINT16), truelight@0: SLE_VAR(Engine,reliability_final, SLE_UINT16), truelight@0: SLE_VAR(Engine,duration_phase_1, SLE_UINT16), truelight@0: SLE_VAR(Engine,duration_phase_2, SLE_UINT16), truelight@0: SLE_VAR(Engine,duration_phase_3, SLE_UINT16), truelight@0: truelight@0: SLE_VAR(Engine,lifelength, SLE_UINT8), truelight@0: SLE_VAR(Engine,flags, SLE_UINT8), truelight@0: SLE_VAR(Engine,preview_player, SLE_UINT8), truelight@0: SLE_VAR(Engine,preview_wait, SLE_UINT8), truelight@0: SLE_VAR(Engine,railtype, SLE_UINT8), truelight@0: SLE_VAR(Engine,player_avail, SLE_UINT8), truelight@0: truelight@0: // reserve extra space in savegame here. (currently 16 bytes) truelight@0: SLE_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 2, 2, 255), truelight@0: truelight@0: SLE_END() truelight@0: }; truelight@0: truelight@0: static void Save_ENGN() truelight@0: { truelight@0: Engine *e; truelight@0: int i; truelight@0: for(i=0,e=_engines; i != lengthof(_engines); i++,e++) { truelight@0: SlSetArrayIndex(i); truelight@0: SlObject(e, _engine_desc); truelight@0: } truelight@0: } truelight@0: truelight@0: static void Load_ENGN() truelight@0: { truelight@0: int index; truelight@0: while ((index = SlIterateArray()) != -1) { truelight@0: SlObject(&_engines[index], _engine_desc); truelight@0: } truelight@0: } truelight@0: truelight@0: static void LoadSave_ENGS() truelight@0: { truelight@0: SlArray(_engine_name_strings, lengthof(_engine_name_strings), SLE_STRINGID); truelight@0: } truelight@0: truelight@0: const ChunkHandler _engine_chunk_handlers[] = { truelight@0: { 'ENGN', Save_ENGN, Load_ENGN, CH_ARRAY}, truelight@0: { 'ENGS', LoadSave_ENGS, LoadSave_ENGS, CH_RIFF | CH_LAST}, truelight@0: }; truelight@0: truelight@0: truelight@0: