# engine.cpp

Go to the documentation of this file.

    00001 /* $Id: engine.cpp 14268 2008-09-07 22:04:39Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "debug.h"
    00008 #include "engine.h"
    00009 #include "player_base.h"
    00010 #include "player_func.h"
    00011 #include "command_func.h"
    00012 #include "news.h"
    00013 #include "saveload.h"
    00014 #include "variables.h"
    00015 #include "train.h"
    00016 #include "aircraft.h"
    00017 #include "newgrf_cargo.h"
    00018 #include "group.h"
    00019 #include "strings_func.h"
    00020 #include "gfx_func.h"
    00021 #include "functions.h"
    00022 #include "window_func.h"
    00023 #include "date_func.h"
    00024 #include "autoreplace_base.h"
    00025 #include "autoreplace_gui.h"
    00026 #include "string_func.h"
    00027 #include "settings_type.h"
    00028
    00029 #include "table/strings.h"
    00030 #include "table/engines.h"
    00031
    00032 Engine _engines[TOTAL_NUM_ENGINES];
    00033 EngineInfo _engine_info[TOTAL_NUM_ENGINES];
    00034 RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES];
    00035 ShipVehicleInfo _ship_vehicle_info[NUM_SHIP_ENGINES];
    00036 AircraftVehicleInfo _aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES];
    00037 RoadVehicleInfo _road_vehicle_info[NUM_ROAD_ENGINES];
    00038
    00039 enum {
    00040   YEAR_ENGINE_AGING_STOPS = 2050,
    00041 };
    00042
    00043
    00044 void SetupEngines()
    00045 {
    00046   /* Copy original static engine data */
    00047   memcpy(&_engine_info, &_orig_engine_info, sizeof(_orig_engine_info));
    00048   memcpy(&_rail_vehicle_info, &_orig_rail_vehicle_info, sizeof(_orig_rail_vehicle_info));
    00049   memcpy(&_ship_vehicle_info, &_orig_ship_vehicle_info, sizeof(_orig_ship_vehicle_info));
    00050   memcpy(&_aircraft_vehicle_info, &_orig_aircraft_vehicle_info, sizeof(_orig_aircraft_vehicle_info));
    00051   memcpy(&_road_vehicle_info, &_orig_road_vehicle_info, sizeof(_orig_road_vehicle_info));
    00052
    00053   /* Add type to engines */
    00054   Engine* e = _engines;
    00055   do e->type = VEH_TRAIN;    while (++e < &_engines[ROAD_ENGINES_INDEX]);
    00056   do e->type = VEH_ROAD;     while (++e < &_engines[SHIP_ENGINES_INDEX]);
    00057   do e->type = VEH_SHIP;     while (++e < &_engines[AIRCRAFT_ENGINES_INDEX]);
    00058   do e->type = VEH_AIRCRAFT; while (++e < &_engines[TOTAL_NUM_ENGINES]);
    00059
    00060   /* Set up default engine names */
    00061   for (EngineID engine = 0; engine < TOTAL_NUM_ENGINES; engine++) {
    00062     EngineInfo *ei = &_engine_info[engine];
    00063     ei->string_id = STR_8000_KIRBY_PAUL_TANK_STEAM + engine;
    00064   }
    00065 }
    00066
    00067
    00068 void ShowEnginePreviewWindow(EngineID engine);
    00069
    00070 void DeleteCustomEngineNames()
    00071 {
    00072   Engine *e;
    00073   FOR_ALL_ENGINES(e) {
    00074     free(e->name);
    00075     e->name = NULL;
    00076   }
    00077
    00078   _vehicle_design_names &= ~1;
    00079 }
    00080
    00081 void LoadCustomEngineNames()
    00082 {
    00083   /* XXX: not done */
    00084   DEBUG(misc, 1, "LoadCustomEngineNames: not done");
    00085 }
    00086
    00087 static void CalcEngineReliability(Engine *e)
    00088 {
    00089   uint age = e->age;
    00090
    00091   /* Check for early retirement */
    00092   if (e->player_avail != 0 && !_patches.never_expire_vehicles) {
    00093     int retire_early = EngInfo(e - _engines)->retire_early;
    00094     uint retire_early_max_age = max(0, e->duration_phase_1 + e->duration_phase_2 - retire_early * 12);
    00095     if (retire_early != 0 && age >= retire_early_max_age) {
    00096       /* Early retirement is enabled and we're past the date... */
    00097       e->player_avail = 0;
    00098       AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
    00099     }
    00100   }
    00101
    00102   if (age < e->duration_phase_1) {
    00103     uint start = e->reliability_start;
    00104     e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
    00105   } else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _patches.never_expire_vehicles) {
    00106     /* We are at the peak of this engines life. It will have max reliability.
    00107      * This is also true if the engines never expire. They will not go bad over time */
    00108     e->reliability = e->reliability_max;
    00109   } else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
    00110     uint max = e->reliability_max;
    00111     e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
    00112   } else {
    00113     /* time's up for this engine.
    00114      * We will now completely retire this design */
    00115     e->player_avail = 0;
    00116     e->reliability = e->reliability_final;
    00117     /* Kick this engine out of the lists */
    00118     AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
    00119   }
    00120   InvalidateWindowClasses(WC_BUILD_VEHICLE); // Update to show the new reliability
    00121   InvalidateWindowClasses(WC_REPLACE_VEHICLE);
    00122 }
    00123
    00124 void StartupEngines()
    00125 {
    00126   Engine *e;
    00127   const EngineInfo *ei;
    00128   /* Aging of vehicles stops, so account for that when starting late */
    00129   const Date aging_date = min(_date, ConvertYMDToDate(YEAR_ENGINE_AGING_STOPS, 0, 1));
    00130
    00131   for (e = _engines, ei = _engine_info; e != endof(_engines); e++, ei++) {
    00132     uint32 r;
    00133
    00134     e->age = 0;
    00135     e->flags = 0;
    00136     e->player_avail = 0;
    00137
    00138     /* The magic value of 729 days below comes from the NewGRF spec. If the
    00139      * base intro date is before 1922 then the random number of days is not
    00140      * added. */
    00141     r = Random();
    00142     e->intro_date = ei->base_intro <= ConvertYMDToDate(1922, 0, 1) ? ei->base_intro : (Date)GB(r, 0, 9) + ei->base_intro;
    00143     if (e->intro_date <= _date) {
    00144       e->age = (aging_date - e->intro_date) >> 5;
    00145       e->player_avail = (byte)-1;
    00146       e->flags |= ENGINE_AVAILABLE;
    00147     }
    00148
    00149     e->reliability_start = GB(r, 16, 14) + 0x7AE0;
    00150     r = Random();
    00151     e->reliability_max   = GB(r,  0, 14) + 0xBFFF;
    00152     e->reliability_final = GB(r, 16, 14) + 0x3FFF;
    00153
    00154     r = Random();
    00155     e->duration_phase_1 = GB(r, 0, 5) + 7;
    00156     e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96;
    00157     e->duration_phase_3 = GB(r, 9, 7) + 120;
    00158
    00159     e->reliability_spd_dec = (ei->unk2&0x7F) << 2;
    00160
    00161     /* my invented flag for something that is a wagon */
    00162     if (ei->unk2 & 0x80) {
    00163       e->age = 0xFFFF;
    00164     } else {
    00165       CalcEngineReliability(e);
    00166     }
    00167
    00168     e->lifelength = ei->lifelength + _patches.extend_vehicle_life;
    00169
    00170     /* prevent certain engines from ever appearing. */
    00171     if (!HasBit(ei->climates, _opt.landscape)) {
    00172       e->flags |= ENGINE_AVAILABLE;
    00173       e->player_avail = 0;
    00174     }
    00175   }
    00176
    00177   /* Update the bitmasks for the vehicle lists */
    00178   Player *p;
    00179   FOR_ALL_PLAYERS(p) {
    00180     p->avail_railtypes = GetPlayerRailtypes(p->index);
    00181     p->avail_roadtypes = GetPlayerRoadtypes(p->index);
    00182   }
    00183 }
    00184
    00185 static void AcceptEnginePreview(EngineID eid, PlayerID player)
    00186 {
    00187   Engine *e = GetEngine(eid);
    00188   Player *p = GetPlayer(player);
    00189
    00190   SetBit(e->player_avail, player);
    00191   if (e->type == VEH_TRAIN) {
    00192     const RailVehicleInfo *rvi = RailVehInfo(eid);
    00193
    00194     assert(rvi->railtype < RAILTYPE_END);
    00195     SetBit(p->avail_railtypes, rvi->railtype);
    00196   } else if (e->type == VEH_ROAD) {
    00197     SetBit(p->avail_roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
    00198   }
    00199
    00200   e->preview_player_rank = 0xFF;
    00201   if (player == _local_player) {
    00202     AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
    00203   }
    00204 }
    00205
    00206 static PlayerID GetBestPlayer(uint8 pp)
    00207 {
    00208   const Player *p;
    00209   int32 best_hist;
    00210   PlayerID best_player;
    00211   uint mask = 0;
    00212
    00213   do {
    00214     best_hist = -1;
    00215     best_player = PLAYER_SPECTATOR;
    00216     FOR_ALL_PLAYERS(p) {
    00217       if (p->is_active && p->block_preview == 0 && !HasBit(mask, p->index) &&
    00218           p->old_economy[0].performance_history > best_hist) {
    00219         best_hist = p->old_economy[0].performance_history;
    00220         best_player = p->index;
    00221       }
    00222     }
    00223
    00224     if (best_player == PLAYER_SPECTATOR) return PLAYER_SPECTATOR;
    00225
    00226     SetBit(mask, best_player);
    00227   } while (--pp != 0);
    00228
    00229   return best_player;
    00230 }
    00231
    00232 void EnginesDailyLoop()
    00233 {
    00234   EngineID i;
    00235
    00236   if (_cur_year >= YEAR_ENGINE_AGING_STOPS) return;
    00237
    00238   for (i = 0; i != lengthof(_engines); i++) {
    00239     Engine *e = &_engines[i];
    00240
    00241     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
    00242       if (e->flags & ENGINE_OFFER_WINDOW_OPEN) {
    00243         if (e->preview_player_rank != 0xFF && !--e->preview_wait) {
    00244           e->flags &= ~ENGINE_OFFER_WINDOW_OPEN;
    00245           DeleteWindowById(WC_ENGINE_PREVIEW, i);
    00246           e->preview_player_rank++;
    00247         }
    00248       } else if (e->preview_player_rank != 0xFF) {
    00249         PlayerID best_player = GetBestPlayer(e->preview_player_rank);
    00250
    00251         if (best_player == PLAYER_SPECTATOR) {
    00252           e->preview_player_rank = 0xFF;
    00253           continue;
    00254         }
    00255
    00256         if (!IsHumanPlayer(best_player)) {
    00257           /* XXX - TTDBUG: TTD has a bug here ???? */
    00258           AcceptEnginePreview(i, best_player);
    00259         } else {
    00260           e->flags |= ENGINE_OFFER_WINDOW_OPEN;
    00261           e->preview_wait = 20;
    00262           if (IsInteractivePlayer(best_player)) ShowEnginePreviewWindow(i);
    00263         }
    00264       }
    00265     }
    00266   }
    00267 }
    00268
    00276 CommandCost CmdWantEnginePreview(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    00277 {
    00278   Engine *e;
    00279
    00280   if (!IsEngineIndex(p1)) return CMD_ERROR;
    00281   e = GetEngine(p1);
    00282   if (GetBestPlayer(e->preview_player_rank) != _current_player) return CMD_ERROR;
    00283
    00284   if (flags & DC_EXEC) AcceptEnginePreview(p1, _current_player);
    00285
    00286   return CommandCost();
    00287 }
    00288
    00289 /* Determine if an engine type is a wagon (and not a loco) */
    00290 static bool IsWagon(EngineID index)
    00291 {
    00292   return index < NUM_TRAIN_ENGINES && RailVehInfo(index)->railveh_type == RAILVEH_WAGON;
    00293 }
    00294
    00295 static void NewVehicleAvailable(Engine *e)
    00296 {
    00297   Vehicle *v;
    00298   Player *p;
    00299   EngineID index = e - _engines;
    00300
    00301   /* In case the player didn't build the vehicle during the intro period,
    00302    * prevent that player from getting future intro periods for a while. */
    00303   if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
    00304     FOR_ALL_PLAYERS(p) {
    00305       uint block_preview = p->block_preview;
    00306
    00307       if (!HasBit(e->player_avail, p->index)) continue;
    00308
    00309       /* We assume the user did NOT build it.. prove me wrong ;) */
    00310       p->block_preview = 20;
    00311
    00312       FOR_ALL_VEHICLES(v) {
    00313         if (v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP ||
    00314             (v->type == VEH_AIRCRAFT && IsNormalAircraft(v))) {
    00315           if (v->owner == p->index && v->engine_type == index) {
    00316             /* The user did prove me wrong, so restore old value */
    00317             p->block_preview = block_preview;
    00318             break;
    00319           }
    00320         }
    00321       }
    00322     }
    00323   }
    00324
    00325   e->flags = (e->flags & ~ENGINE_EXCLUSIVE_PREVIEW) | ENGINE_AVAILABLE;
    00326   AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
    00327
    00328   /* Now available for all players */
    00329   e->player_avail = (byte)-1;
    00330
    00331   /* Do not introduce new rail wagons */
    00332   if (IsWagon(index)) return;
    00333
    00334   if (e->type == VEH_TRAIN) {
    00335     /* maybe make another rail type available */
    00336     RailType railtype = RailVehInfo(index)->railtype;
    00337     assert(railtype < RAILTYPE_END);
    00338     FOR_ALL_PLAYERS(p) {
    00339       if (p->is_active) SetBit(p->avail_railtypes, railtype);
    00340     }
    00341   } else if (e->type == VEH_ROAD) {
    00342     /* maybe make another road type available */
    00343     FOR_ALL_PLAYERS(p) {
    00344       if (p->is_active) SetBit(p->avail_roadtypes, HasBit(EngInfo(index)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
    00345     }
    00346   }
    00347   AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_VEHICLEAVAIL), 0, 0);
    00348 }
    00349
    00350 void EnginesMonthlyLoop()
    00351 {
    00352   if (_cur_year < YEAR_ENGINE_AGING_STOPS) {
    00353     Engine *e;
    00354     FOR_ALL_ENGINES(e) {
    00355       /* Age the vehicle */
    00356       if (e->flags & ENGINE_AVAILABLE && e->age != 0xFFFF) {
    00357         e->age++;
    00358         CalcEngineReliability(e);
    00359       }
    00360
    00361       if (!(e->flags & ENGINE_AVAILABLE) && _date >= (e->intro_date + 365)) {
    00362         /* Introduce it to all players */
    00363         NewVehicleAvailable(e);
    00364       } else if (!(e->flags & (ENGINE_AVAILABLE|ENGINE_EXCLUSIVE_PREVIEW)) && _date >= e->intro_date) {
    00365         /* Introduction date has passed.. show introducing dialog to one player. */
    00366         e->flags |= ENGINE_EXCLUSIVE_PREVIEW;
    00367
    00368         /* Do not introduce new rail wagons */
    00369         if (!IsWagon(e - _engines))
    00370           e->preview_player_rank = 1; // Give to the player with the highest rating.
    00371       }
    00372     }
    00373   }
    00374 }
    00375
    00376 static bool IsUniqueEngineName(const char *name)
    00377 {
    00378   char buf[512];
    00379
    00380   for (EngineID i = 0; i < TOTAL_NUM_ENGINES; i++) {
    00381     SetDParam(0, i);
    00382     GetString(buf, STR_ENGINE_NAME, lastof(buf));
    00383     if (strcmp(buf, name) == 0) return false;
    00384   }
    00385
    00386   return true;
    00387 }
    00388
    00395 CommandCost CmdRenameEngine(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    00396 {
    00397   if (!IsEngineIndex(p1) || StrEmpty(_cmd_text)) return CMD_ERROR;
    00398
    00399   if (!IsUniqueEngineName(_cmd_text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
    00400
    00401   if (flags & DC_EXEC) {
    00402     Engine *e = GetEngine(p1);
    00403     free(e->name);
    00404     e->name = strdup(_cmd_text);
    00405     _vehicle_design_names |= 3;
    00406     MarkWholeScreenDirty();
    00407   }
    00408
    00409   return CommandCost();
    00410 }
    00411
    00412
    00420 bool IsEngineBuildable(EngineID engine, VehicleType type, PlayerID player)
    00421 {
    00422   /* check if it's an engine that is in the engine array */
    00423   if (!IsEngineIndex(engine)) return false;
    00424
    00425   const Engine *e = GetEngine(engine);
    00426
    00427   /* check if it's an engine of specified type */
    00428   if (e->type != type) return false;
    00429
    00430   /* check if it's available */
    00431   if (!HasBit(e->player_avail, player)) return false;
    00432
    00433   if (type == VEH_TRAIN) {
    00434     /* Check if the rail type is available to this player */
    00435     const Player *p = GetPlayer(player);
    00436     if (!HasBit(p->avail_railtypes, RailVehInfo(engine)->railtype)) return false;
    00437   }
    00438
    00439   return true;
    00440 }
    00441
    00446 CargoID GetEngineCargoType(EngineID engine)
    00447 {
    00448   assert(IsEngineIndex(engine));
    00449
    00450   switch (GetEngine(engine)->type) {
    00451     case VEH_TRAIN:
    00452       if (RailVehInfo(engine)->capacity == 0) return CT_INVALID;
    00453       return RailVehInfo(engine)->cargo_type;
    00454
    00455     case VEH_ROAD:
    00456       if (RoadVehInfo(engine)->capacity == 0) return CT_INVALID;
    00457       return RoadVehInfo(engine)->cargo_type;
    00458
    00459     case VEH_SHIP:
    00460       if (ShipVehInfo(engine)->capacity == 0) return CT_INVALID;
    00461       return ShipVehInfo(engine)->cargo_type;
    00462
    00463     case VEH_AIRCRAFT:
    00464       /* all aircraft starts as passenger planes with cargo capacity */
    00465       return CT_PASSENGERS;
    00466
    00467     default: NOT_REACHED(); return CT_INVALID;
    00468   }
    00469 }
    00470
    00471 /************************************************************************
    00472  * Engine Replacement stuff
    00473  ************************************************************************/
    00474
    00475 DEFINE_OLD_POOL_GENERIC(EngineRenew, EngineRenew)
    00476
    00477
    00480 static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
    00481 {
    00482   EngineRenew *er = (EngineRenew *)erl;
    00483
    00484   while (er) {
    00485     if (er->from == engine && er->group_id == group) return er;
    00486     er = er->next;
    00487   }
    00488   return NULL;
    00489 }
    00490
    00491 void RemoveAllEngineReplacement(EngineRenewList *erl)
    00492 {
    00493   EngineRenew *er = (EngineRenew *)(*erl);
    00494   EngineRenew *next;
    00495
    00496   while (er != NULL) {
    00497     next = er->next;
    00498     delete er;
    00499     er = next;
    00500   }
    00501   *erl = NULL; // Empty list
    00502 }
    00503
    00504 EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
    00505 {
    00506   const EngineRenew *er = GetEngineReplacement(erl, engine, group);
    00507   if (er == NULL && (group == DEFAULT_GROUP || (IsValidGroupID(group) && !GetGroup(group)->replace_protection))) {
    00508     /* We didn't find anything useful in the vehicle's own group so we will try ALL_GROUP */
    00509     er = GetEngineReplacement(erl, engine, ALL_GROUP);
    00510   }
    00511   return er == NULL ? INVALID_ENGINE : er->to;
    00512 }
    00513
    00514 CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags)
    00515 {
    00516   EngineRenew *er;
    00517
    00518   /* Check if the old vehicle is already in the list */
    00519   er = GetEngineReplacement(*erl, old_engine, group);
    00520   if (er != NULL) {
    00521     if (flags & DC_EXEC) er->to = new_engine;
    00522     return CommandCost();
    00523   }
    00524
    00525   if (!EngineRenew::CanAllocateItem()) return CMD_ERROR;
    00526
    00527   if (flags & DC_EXEC) {
    00528     er = new EngineRenew(old_engine, new_engine);
    00529     er->group_id = group;
    00530
    00531     /* Insert before the first element */
    00532     er->next = (EngineRenew *)(*erl);
    00533     *erl = (EngineRenewList)er;
    00534   }
    00535
    00536   return CommandCost();
    00537 }
    00538
    00539 CommandCost RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, uint32 flags)
    00540 {
    00541   EngineRenew *er = (EngineRenew *)(*erl);
    00542   EngineRenew *prev = NULL;
    00543
    00544   while (er)
    00545   {
    00546     if (er->from == engine && er->group_id == group) {
    00547       if (flags & DC_EXEC) {
    00548         if (prev == NULL) { // First element
    00549           /* The second becomes the new first element */
    00550           *erl = (EngineRenewList)er->next;
    00551         } else {
    00552           /* Cut this element out */
    00553           prev->next = er->next;
    00554         }
    00555         delete er;
    00556       }
    00557       return CommandCost();
    00558     }
    00559     prev = er;
    00560     er = er->next;
    00561   }
    00562
    00563   return CMD_ERROR;
    00564 }
    00565
    00566 static const SaveLoad _engine_renew_desc[] = {
    00567       SLE_VAR(EngineRenew, from,     SLE_UINT16),
    00568       SLE_VAR(EngineRenew, to,       SLE_UINT16),
    00569
    00570       SLE_REF(EngineRenew, next,     REF_ENGINE_RENEWS),
    00571   SLE_CONDVAR(EngineRenew, group_id, SLE_UINT16, 60, SL_MAX_VERSION),
    00572   SLE_END()
    00573 };
    00574
    00575 static void Save_ERNW()
    00576 {
    00577   EngineRenew *er;
    00578
    00579   FOR_ALL_ENGINE_RENEWS(er) {
    00580     SlSetArrayIndex(er->index);
    00581     SlObject(er, _engine_renew_desc);
    00582   }
    00583 }
    00584
    00585 static void Load_ERNW()
    00586 {
    00587   int index;
    00588
    00589   while ((index = SlIterateArray()) != -1) {
    00590     EngineRenew *er = new (index) EngineRenew();
    00591     SlObject(er, _engine_renew_desc);
    00592
    00593     /* Advanced vehicle lists, ungrouped vehicles got added */
    00594     if (CheckSavegameVersion(60)) {
    00595       er->group_id = ALL_GROUP;
    00596     } else if (CheckSavegameVersion(71)) {
    00597       if (er->group_id == DEFAULT_GROUP) er->group_id = ALL_GROUP;
    00598     }
    00599   }
    00600 }
    00601
    00602 static const SaveLoad _engine_desc[] = {
    00603   SLE_CONDVAR(Engine, intro_date,          SLE_FILE_U16 | SLE_VAR_I32,  0,  30),
    00604   SLE_CONDVAR(Engine, intro_date,          SLE_INT32,                  31, SL_MAX_VERSION),
    00605   SLE_CONDVAR(Engine, age,                 SLE_FILE_U16 | SLE_VAR_I32,  0,  30),
    00606   SLE_CONDVAR(Engine, age,                 SLE_INT32,                  31, SL_MAX_VERSION),
    00607       SLE_VAR(Engine, reliability,         SLE_UINT16),
    00608       SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16),
    00609       SLE_VAR(Engine, reliability_start,   SLE_UINT16),
    00610       SLE_VAR(Engine, reliability_max,     SLE_UINT16),
    00611       SLE_VAR(Engine, reliability_final,   SLE_UINT16),
    00612       SLE_VAR(Engine, duration_phase_1,    SLE_UINT16),
    00613       SLE_VAR(Engine, duration_phase_2,    SLE_UINT16),
    00614       SLE_VAR(Engine, duration_phase_3,    SLE_UINT16),
    00615
    00616       SLE_VAR(Engine, lifelength,          SLE_UINT8),
    00617       SLE_VAR(Engine, flags,               SLE_UINT8),
    00618       SLE_VAR(Engine, preview_player_rank, SLE_UINT8),
    00619       SLE_VAR(Engine, preview_wait,        SLE_UINT8),
    00620   SLE_CONDNULL(1, 0, 44),
    00621       SLE_VAR(Engine, player_avail,        SLE_UINT8),
    00622   SLE_CONDSTR(Engine, name,                SLE_STR, 0,                 84, SL_MAX_VERSION),
    00623
    00624   /* reserve extra space in savegame here. (currently 16 bytes) */
    00625   SLE_CONDNULL(16, 2, SL_MAX_VERSION),
    00626
    00627   SLE_END()
    00628 };
    00629
    00630 static void Save_ENGN()
    00631 {
    00632   uint i;
    00633
    00634   for (i = 0; i != lengthof(_engines); i++) {
    00635     SlSetArrayIndex(i);
    00636     SlObject(&_engines[i], _engine_desc);
    00637   }
    00638 }
    00639
    00640 static void Load_ENGN()
    00641 {
    00642   int index;
    00643   while ((index = SlIterateArray()) != -1) {
    00644     SlObject(GetEngine(index), _engine_desc);
    00645   }
    00646 }
    00647
    00648 static void Load_ENGS()
    00649 {
    00650   StringID names[TOTAL_NUM_ENGINES];
    00651
    00652   SlArray(names, lengthof(names), SLE_STRINGID);
    00653
    00654   for (EngineID engine = 0; engine < lengthof(names); engine++) {
    00655     Engine *e = GetEngine(engine);
    00656     e->name = CopyFromOldName(names[engine]);
    00657   }
    00658 }
    00659
    00660 extern const ChunkHandler _engine_chunk_handlers[] = {
    00661   { 'ENGN', Save_ENGN,     Load_ENGN,     CH_ARRAY          },
    00662   { 'ENGS', NULL,          Load_ENGS,     CH_RIFF           },
    00663   { 'ERNW', Save_ERNW,     Load_ERNW,     CH_ARRAY | CH_LAST},
    00664 };
    00665
    00666 void InitializeEngines()
    00667 {
    00668   /* Clean the engine renew pool and create 1 block in it */
    00669   _EngineRenew_pool.CleanPool();
    00670   _EngineRenew_pool.AddBlockToPool();
    00671
    00672   Engine *e;
    00673   FOR_ALL_ENGINES(e) {
    00674     free(e->name);
    00675     e->name = NULL;
    00676   }
    00677 }