src/engine.c
changeset 5584 1111b4d36e35
parent 5583 136d8764c7e6
child 5585 189f096bc9a3
equal deleted inserted replaced
5583:136d8764c7e6 5584:1111b4d36e35
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "debug.h"
       
     6 #include "functions.h"
       
     7 #include "table/strings.h"
       
     8 #include "engine.h"
       
     9 #include "gfx.h"
       
    10 #include "player.h"
       
    11 #include "command.h"
       
    12 #include "vehicle.h"
       
    13 #include "news.h"
       
    14 #include "saveload.h"
       
    15 #include "variables.h"
       
    16 #include "train.h"
       
    17 #include "newgrf_cargo.h"
       
    18 #include "date.h"
       
    19 #include "table/engines.h"
       
    20 
       
    21 EngineInfo _engine_info[TOTAL_NUM_ENGINES];
       
    22 RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES];
       
    23 ShipVehicleInfo _ship_vehicle_info[NUM_SHIP_ENGINES];
       
    24 AircraftVehicleInfo _aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES];
       
    25 RoadVehicleInfo _road_vehicle_info[NUM_ROAD_ENGINES];
       
    26 
       
    27 enum {
       
    28 	ENGINE_AVAILABLE   = 1,
       
    29 	ENGINE_INTRODUCING = 2,
       
    30 	ENGINE_PREVIEWING  = 4,
       
    31 };
       
    32 
       
    33 enum {
       
    34 	YEAR_ENGINE_AGING_STOPS = 2050,
       
    35 };
       
    36 
       
    37 
       
    38 void ShowEnginePreviewWindow(EngineID engine);
       
    39 
       
    40 void DeleteCustomEngineNames(void)
       
    41 {
       
    42 	uint i;
       
    43 	StringID old;
       
    44 
       
    45 	for (i = 0; i != TOTAL_NUM_ENGINES; i++) {
       
    46 		old = _engine_name_strings[i];
       
    47 		_engine_name_strings[i] = i + STR_8000_KIRBY_PAUL_TANK_STEAM;
       
    48 		DeleteName(old);
       
    49 	}
       
    50 
       
    51 	_vehicle_design_names &= ~1;
       
    52 }
       
    53 
       
    54 void LoadCustomEngineNames(void)
       
    55 {
       
    56 	/* XXX: not done */
       
    57 	DEBUG(misc, 1, "LoadCustomEngineNames: not done");
       
    58 }
       
    59 
       
    60 static void SetupEngineNames(void)
       
    61 {
       
    62 	StringID *name;
       
    63 
       
    64 	for (name = _engine_name_strings; name != endof(_engine_name_strings); name++)
       
    65 		*name = STR_SV_EMPTY;
       
    66 
       
    67 	DeleteCustomEngineNames();
       
    68 	LoadCustomEngineNames();
       
    69 }
       
    70 
       
    71 static void AdjustAvailAircraft(void)
       
    72 {
       
    73 	byte avail = 0;
       
    74 	if (_cur_year >= 1955) avail |= 2; // big airport
       
    75 	if (_cur_year <  1960 || _patches.always_small_airport) avail |= 1;  // small airport
       
    76 	if (_cur_year >= 1963) avail |= 4; // enable heliport
       
    77 
       
    78 	if (avail != _avail_aircraft) {
       
    79 		_avail_aircraft = avail;
       
    80 		InvalidateWindow(WC_BUILD_STATION, 0);
       
    81 	}
       
    82 }
       
    83 
       
    84 static void CalcEngineReliability(Engine *e)
       
    85 {
       
    86 	uint age = e->age;
       
    87 
       
    88 	if (age < e->duration_phase_1) {
       
    89 		uint start = e->reliability_start;
       
    90 		e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
       
    91 	} else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _patches.never_expire_vehicles) {
       
    92 		/* We are at the peak of this engines life. It will have max reliability.
       
    93 		 * This is also true if the engines never expire. They will not go bad over time */
       
    94 		e->reliability = e->reliability_max;
       
    95 	} else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
       
    96 		uint max = e->reliability_max;
       
    97 		e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
       
    98 	} else {
       
    99 		/* time's up for this engine.
       
   100 		 * We will now completely retire this design */
       
   101 		e->player_avail = 0;
       
   102 		e->reliability = e->reliability_final;
       
   103 		InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Kick this engine out of the lists
       
   104 	}
       
   105 	InvalidateWindowClasses(WC_BUILD_VEHICLE); // Update to show the new reliability
       
   106 }
       
   107 
       
   108 void AddTypeToEngines(void)
       
   109 {
       
   110 	Engine* e = _engines;
       
   111 
       
   112 	do e->type = VEH_Train;    while (++e < &_engines[ROAD_ENGINES_INDEX]);
       
   113 	do e->type = VEH_Road;     while (++e < &_engines[SHIP_ENGINES_INDEX]);
       
   114 	do e->type = VEH_Ship;     while (++e < &_engines[AIRCRAFT_ENGINES_INDEX]);
       
   115 	do e->type = VEH_Aircraft; while (++e < &_engines[TOTAL_NUM_ENGINES]);
       
   116 	do e->type = VEH_Special;  while (++e < endof(_engines));
       
   117 }
       
   118 
       
   119 void StartupEngines(void)
       
   120 {
       
   121 	Engine *e;
       
   122 	const EngineInfo *ei;
       
   123 	/* Aging of vehicles stops, so account for that when starting late */
       
   124 	const Date aging_date = min(_date, ConvertYMDToDate(YEAR_ENGINE_AGING_STOPS, 0, 1));
       
   125 
       
   126 	SetupEngineNames();
       
   127 
       
   128 	for (e = _engines, ei = _engine_info; e != endof(_engines); e++, ei++) {
       
   129 		uint32 r;
       
   130 
       
   131 		e->age = 0;
       
   132 		e->railtype = ei->railtype;
       
   133 		e->flags = 0;
       
   134 		e->player_avail = 0;
       
   135 
       
   136 		// The magic value of 729 days below comes from the NewGRF spec. If the
       
   137 		// base intro date is before 1922 then the random number of days is not
       
   138 		// added.
       
   139 		r = Random();
       
   140 		e->intro_date = ei->base_intro <= ConvertYMDToDate(1922, 0, 1) ? ei->base_intro : (Date)GB(r, 0, 9) + ei->base_intro;
       
   141 		if (e->intro_date <= _date) {
       
   142 			e->age = (aging_date - e->intro_date) >> 5;
       
   143 			e->player_avail = (byte)-1;
       
   144 			e->flags |= ENGINE_AVAILABLE;
       
   145 		}
       
   146 
       
   147 		e->reliability_start = GB(r, 16, 14) + 0x7AE0;
       
   148 		r = Random();
       
   149 		e->reliability_max   = GB(r,  0, 14) + 0xBFFF;
       
   150 		e->reliability_final = GB(r, 16, 14) + 0x3FFF;
       
   151 
       
   152 		r = Random();
       
   153 		e->duration_phase_1 = GB(r, 0, 5) + 7;
       
   154 		e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96;
       
   155 		e->duration_phase_3 = GB(r, 9, 7) + 120;
       
   156 
       
   157 		e->reliability_spd_dec = (ei->unk2&0x7F) << 2;
       
   158 
       
   159 		/* my invented flag for something that is a wagon */
       
   160 		if (ei->unk2 & 0x80) {
       
   161 			e->age = 0xFFFF;
       
   162 		} else {
       
   163 			CalcEngineReliability(e);
       
   164 		}
       
   165 
       
   166 		e->lifelength = ei->lifelength + _patches.extend_vehicle_life;
       
   167 
       
   168 		// prevent certain engines from ever appearing.
       
   169 		if (!HASBIT(ei->climates, _opt.landscape)) {
       
   170 			e->flags |= ENGINE_AVAILABLE;
       
   171 			e->player_avail = 0;
       
   172 		}
       
   173 
       
   174 		/* This sets up type for the engine
       
   175 		 * It is needed if you want to ask the engine what type it is
       
   176 		 * It should hopefully be the same as when you ask a vehicle what it is
       
   177 		 * but using this, you can ask what type an engine number is
       
   178 		 * even if it is not a vehicle (yet)*/
       
   179 	}
       
   180 
       
   181 	AdjustAvailAircraft();
       
   182 }
       
   183 
       
   184 static void AcceptEnginePreview(Engine *e, PlayerID player)
       
   185 {
       
   186 	Player *p = GetPlayer(player);
       
   187 
       
   188 	assert(e->railtype < RAILTYPE_END);
       
   189 	SETBIT(e->player_avail, player);
       
   190 	SETBIT(p->avail_railtypes, e->railtype);
       
   191 
       
   192 	e->preview_player = 0xFF;
       
   193 	if (player == _local_player) {
       
   194 		InvalidateWindowClassesData(WC_BUILD_VEHICLE);
       
   195 		InvalidateWindowClasses(WC_REPLACE_VEHICLE);
       
   196 	}
       
   197 }
       
   198 
       
   199 static PlayerID GetBestPlayer(PlayerID pp)
       
   200 {
       
   201 	const Player *p;
       
   202 	int32 best_hist;
       
   203 	PlayerID best_player;
       
   204 	uint mask = 0;
       
   205 
       
   206 	do {
       
   207 		best_hist = -1;
       
   208 		best_player = PLAYER_SPECTATOR;
       
   209 		FOR_ALL_PLAYERS(p) {
       
   210 			if (p->is_active && p->block_preview == 0 && !HASBIT(mask, p->index) &&
       
   211 					p->old_economy[0].performance_history > best_hist) {
       
   212 				best_hist = p->old_economy[0].performance_history;
       
   213 				best_player = p->index;
       
   214 			}
       
   215 		}
       
   216 
       
   217 		if (best_player == PLAYER_SPECTATOR) return PLAYER_SPECTATOR;
       
   218 
       
   219 		SETBIT(mask, best_player);
       
   220 	} while (--pp != 0);
       
   221 
       
   222 	return best_player;
       
   223 }
       
   224 
       
   225 void EnginesDailyLoop(void)
       
   226 {
       
   227 	EngineID i;
       
   228 
       
   229 	if (_cur_year >= YEAR_ENGINE_AGING_STOPS) return;
       
   230 
       
   231 	for (i = 0; i != lengthof(_engines); i++) {
       
   232 		Engine *e = &_engines[i];
       
   233 
       
   234 		if (e->flags & ENGINE_INTRODUCING) {
       
   235 			if (e->flags & ENGINE_PREVIEWING) {
       
   236 				if (e->preview_player != 0xFF && !--e->preview_wait) {
       
   237 					e->flags &= ~ENGINE_PREVIEWING;
       
   238 					DeleteWindowById(WC_ENGINE_PREVIEW, i);
       
   239 					e->preview_player++;
       
   240 				}
       
   241 			} else if (e->preview_player != 0xFF) {
       
   242 				PlayerID best_player = GetBestPlayer(e->preview_player);
       
   243 
       
   244 				if (best_player == PLAYER_SPECTATOR) {
       
   245 					e->preview_player = 0xFF;
       
   246 					continue;
       
   247 				}
       
   248 
       
   249 				if (!IsHumanPlayer(best_player)) {
       
   250 					/* XXX - TTDBUG: TTD has a bug here ???? */
       
   251 					AcceptEnginePreview(e, best_player);
       
   252 				} else {
       
   253 					e->flags |= ENGINE_PREVIEWING;
       
   254 					e->preview_wait = 20;
       
   255 					if (IsInteractivePlayer(best_player)) ShowEnginePreviewWindow(i);
       
   256 				}
       
   257 			}
       
   258 		}
       
   259 	}
       
   260 }
       
   261 
       
   262 /** Accept an engine prototype. XXX - it is possible that the top-player
       
   263  * changes while you are waiting to accept the offer? Then it becomes invalid
       
   264  * @param tile unused
       
   265  * @param p1 engine-prototype offered
       
   266  * @param p2 unused
       
   267  */
       
   268 int32 CmdWantEnginePreview(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   269 {
       
   270 	Engine *e;
       
   271 
       
   272 	if (!IsEngineIndex(p1)) return CMD_ERROR;
       
   273 	e = GetEngine(p1);
       
   274 	if (GetBestPlayer(e->preview_player) != _current_player) return CMD_ERROR;
       
   275 
       
   276 	if (flags & DC_EXEC) AcceptEnginePreview(e, _current_player);
       
   277 
       
   278 	return 0;
       
   279 }
       
   280 
       
   281 // Determine if an engine type is a wagon (and not a loco)
       
   282 static bool IsWagon(EngineID index)
       
   283 {
       
   284 	return index < NUM_TRAIN_ENGINES && RailVehInfo(index)->flags & RVI_WAGON;
       
   285 }
       
   286 
       
   287 static void NewVehicleAvailable(Engine *e)
       
   288 {
       
   289 	Vehicle *v;
       
   290 	Player *p;
       
   291 	EngineID index = e - _engines;
       
   292 
       
   293 	// In case the player didn't build the vehicle during the intro period,
       
   294 	// prevent that player from getting future intro periods for a while.
       
   295 	if (e->flags & ENGINE_INTRODUCING) {
       
   296 		FOR_ALL_PLAYERS(p) {
       
   297 			uint block_preview = p->block_preview;
       
   298 
       
   299 			if (!HASBIT(e->player_avail, p->index)) continue;
       
   300 
       
   301 			/* We assume the user did NOT build it.. prove me wrong ;) */
       
   302 			p->block_preview = 20;
       
   303 
       
   304 			FOR_ALL_VEHICLES(v) {
       
   305 				if (v->type == VEH_Train || v->type == VEH_Road || v->type == VEH_Ship ||
       
   306 						(v->type == VEH_Aircraft && v->subtype <= 2)) {
       
   307 					if (v->owner == p->index && v->engine_type == index) {
       
   308 						/* The user did prove me wrong, so restore old value */
       
   309 						p->block_preview = block_preview;
       
   310 						break;
       
   311 					}
       
   312 				}
       
   313 			}
       
   314 		}
       
   315 	}
       
   316 
       
   317 	e->flags = (e->flags & ~ENGINE_INTRODUCING) | ENGINE_AVAILABLE;
       
   318 	InvalidateWindowClassesData(WC_BUILD_VEHICLE);
       
   319 	InvalidateWindowClasses(WC_REPLACE_VEHICLE);
       
   320 
       
   321 	// Now available for all players
       
   322 	e->player_avail = (byte)-1;
       
   323 
       
   324 	// Do not introduce new rail wagons
       
   325 	if (IsWagon(index)) return;
       
   326 
       
   327 	// make maglev / monorail available
       
   328 	FOR_ALL_PLAYERS(p) {
       
   329 		if (p->is_active) {
       
   330 			assert(e->railtype < RAILTYPE_END);
       
   331 			SETBIT(p->avail_railtypes, e->railtype);
       
   332 		}
       
   333 	}
       
   334 
       
   335 	if (index < NUM_TRAIN_ENGINES) {
       
   336 		AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_TRAINAVAIL), 0, 0);
       
   337 	} else if (index < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES) {
       
   338 		AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_ROADAVAIL), 0, 0);
       
   339 	} else if (index < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES) {
       
   340 		AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_SHIPAVAIL), 0, 0);
       
   341 	} else {
       
   342 		AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_AIRCRAFTAVAIL), 0, 0);
       
   343 	}
       
   344 }
       
   345 
       
   346 void EnginesMonthlyLoop(void)
       
   347 {
       
   348 	Engine *e;
       
   349 
       
   350 	if (_cur_year < YEAR_ENGINE_AGING_STOPS) {
       
   351 		for (e = _engines; e != endof(_engines); e++) {
       
   352 			// Age the vehicle
       
   353 			if (e->flags & ENGINE_AVAILABLE && e->age != 0xFFFF) {
       
   354 				e->age++;
       
   355 				CalcEngineReliability(e);
       
   356 			}
       
   357 
       
   358 			if (!(e->flags & ENGINE_AVAILABLE) && _date >= (e->intro_date + 365)) {
       
   359 				// Introduce it to all players
       
   360 				NewVehicleAvailable(e);
       
   361 			} else if (!(e->flags & (ENGINE_AVAILABLE|ENGINE_INTRODUCING)) && _date >= e->intro_date) {
       
   362 				// Introduction date has passed.. show introducing dialog to one player.
       
   363 				e->flags |= ENGINE_INTRODUCING;
       
   364 
       
   365 				// Do not introduce new rail wagons
       
   366 				if (!IsWagon(e - _engines))
       
   367 					e->preview_player = 1; // Give to the player with the highest rating.
       
   368 			}
       
   369 		}
       
   370 	}
       
   371 	AdjustAvailAircraft();
       
   372 }
       
   373 
       
   374 /** Rename an engine.
       
   375  * @param tile unused
       
   376  * @param p1 engine ID to rename
       
   377  * @param p2 unused
       
   378  */
       
   379 int32 CmdRenameEngine(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   380 {
       
   381 	StringID str;
       
   382 
       
   383 	if (!IsEngineIndex(p1) || _cmd_text[0] == '\0') return CMD_ERROR;
       
   384 
       
   385 	str = AllocateNameUnique(_cmd_text, 0);
       
   386 	if (str == 0) return CMD_ERROR;
       
   387 
       
   388 	if (flags & DC_EXEC) {
       
   389 		StringID old_str = _engine_name_strings[p1];
       
   390 		_engine_name_strings[p1] = str;
       
   391 		DeleteName(old_str);
       
   392 		_vehicle_design_names |= 3;
       
   393 		MarkWholeScreenDirty();
       
   394 	} else {
       
   395 		DeleteName(str);
       
   396 	}
       
   397 
       
   398 	return 0;
       
   399 }
       
   400 
       
   401 
       
   402 /*
       
   403  * returns true if an engine is valid, of the specified type, and buildable by
       
   404  * the given player, false otherwise
       
   405  *
       
   406  * engine = index of the engine to check
       
   407  * type   = the type the engine should be of (VEH_xxx)
       
   408  * player = index of the player
       
   409  */
       
   410 bool IsEngineBuildable(EngineID engine, byte type, PlayerID player)
       
   411 {
       
   412 	const Engine *e;
       
   413 
       
   414 	// check if it's an engine that is in the engine array
       
   415 	if (!IsEngineIndex(engine)) return false;
       
   416 
       
   417 	e = GetEngine(engine);
       
   418 
       
   419 	// check if it's an engine of specified type
       
   420 	if (e->type != type) return false;
       
   421 
       
   422 	// check if it's available
       
   423 	if (!HASBIT(e->player_avail, player)) return false;
       
   424 
       
   425 	return true;
       
   426 }
       
   427 
       
   428 /************************************************************************
       
   429  * Engine Replacement stuff
       
   430  ************************************************************************/
       
   431 
       
   432 static void EngineRenewPoolNewBlock(uint start_item);
       
   433 
       
   434 DEFINE_OLD_POOL(EngineRenew, EngineRenew, EngineRenewPoolNewBlock, NULL)
       
   435 
       
   436 static void EngineRenewPoolNewBlock(uint start_item)
       
   437 {
       
   438 	EngineRenew *er;
       
   439 
       
   440 	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
       
   441 	 *  TODO - This is just a temporary stage, this will be removed. */
       
   442 	for (er = GetEngineRenew(start_item); er != NULL; er = (er->index + 1U < GetEngineRenewPoolSize()) ? GetEngineRenew(er->index + 1U) : NULL) {
       
   443 		er->index = start_item++;
       
   444 		er->from = INVALID_ENGINE;
       
   445 	}
       
   446 }
       
   447 
       
   448 
       
   449 static EngineRenew *AllocateEngineRenew(void)
       
   450 {
       
   451 	EngineRenew *er;
       
   452 
       
   453 	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
       
   454 	 *  TODO - This is just a temporary stage, this will be removed. */
       
   455 	for (er = GetEngineRenew(0); er != NULL; er = (er->index + 1U < GetEngineRenewPoolSize()) ? GetEngineRenew(er->index + 1U) : NULL) {
       
   456 		if (IsValidEngineRenew(er)) continue;
       
   457 
       
   458 		er->to = INVALID_ENGINE;
       
   459 		er->next = NULL;
       
   460 		return er;
       
   461 	}
       
   462 
       
   463 	/* Check if we can add a block to the pool */
       
   464 	if (AddBlockToPool(&_EngineRenew_pool)) return AllocateEngineRenew();
       
   465 
       
   466 	return NULL;
       
   467 }
       
   468 
       
   469 /**
       
   470  * Retrieves the EngineRenew that specifies the replacement of the given
       
   471  * engine type from the given renewlist */
       
   472 static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine)
       
   473 {
       
   474 	EngineRenew *er = (EngineRenew *)erl;
       
   475 
       
   476 	while (er) {
       
   477 		if (er->from == engine) return er;
       
   478 		er = er->next;
       
   479 	}
       
   480 	return NULL;
       
   481 }
       
   482 
       
   483 void RemoveAllEngineReplacement(EngineRenewList *erl)
       
   484 {
       
   485 	EngineRenew *er = (EngineRenew *)(*erl);
       
   486 	EngineRenew *next;
       
   487 
       
   488 	while (er) {
       
   489 		next = er->next;
       
   490 		DeleteEngineRenew(er);
       
   491 		er = next;
       
   492 	}
       
   493 	*erl = NULL; // Empty list
       
   494 }
       
   495 
       
   496 EngineID EngineReplacement(EngineRenewList erl, EngineID engine)
       
   497 {
       
   498 	const EngineRenew *er = GetEngineReplacement(erl, engine);
       
   499 	return er == NULL ? INVALID_ENGINE : er->to;
       
   500 }
       
   501 
       
   502 int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, uint32 flags)
       
   503 {
       
   504 	EngineRenew *er;
       
   505 
       
   506 	/* Check if the old vehicle is already in the list */
       
   507 	er = GetEngineReplacement(*erl, old_engine);
       
   508 	if (er != NULL) {
       
   509 		if (flags & DC_EXEC) er->to = new_engine;
       
   510 		return 0;
       
   511 	}
       
   512 
       
   513 	er = AllocateEngineRenew();
       
   514 	if (er == NULL) return CMD_ERROR;
       
   515 
       
   516 	if (flags & DC_EXEC) {
       
   517 		er->from = old_engine;
       
   518 		er->to = new_engine;
       
   519 
       
   520 		/* Insert before the first element */
       
   521 		er->next = (EngineRenew *)(*erl);
       
   522 		*erl = (EngineRenewList)er;
       
   523 	}
       
   524 
       
   525 	return 0;
       
   526 }
       
   527 
       
   528 int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, uint32 flags)
       
   529 {
       
   530 	EngineRenew *er = (EngineRenew *)(*erl);
       
   531 	EngineRenew *prev = NULL;
       
   532 
       
   533 	while (er)
       
   534 	{
       
   535 		if (er->from == engine) {
       
   536 			if (flags & DC_EXEC) {
       
   537 				if (prev == NULL) { // First element
       
   538 					/* The second becomes the new first element */
       
   539 					*erl = (EngineRenewList)er->next;
       
   540 				} else {
       
   541 					/* Cut this element out */
       
   542 					prev->next = er->next;
       
   543 				}
       
   544 				DeleteEngineRenew(er);
       
   545 			}
       
   546 			return 0;
       
   547 		}
       
   548 		prev = er;
       
   549 		er = er->next;
       
   550 	}
       
   551 
       
   552 	return CMD_ERROR;
       
   553 }
       
   554 
       
   555 static const SaveLoad _engine_renew_desc[] = {
       
   556 	SLE_VAR(EngineRenew, from, SLE_UINT16),
       
   557 	SLE_VAR(EngineRenew, to,   SLE_UINT16),
       
   558 
       
   559 	SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS),
       
   560 
       
   561 	SLE_END()
       
   562 };
       
   563 
       
   564 static void Save_ERNW(void)
       
   565 {
       
   566 	EngineRenew *er;
       
   567 
       
   568 	FOR_ALL_ENGINE_RENEWS(er) {
       
   569 		SlSetArrayIndex(er->index);
       
   570 		SlObject(er, _engine_renew_desc);
       
   571 	}
       
   572 }
       
   573 
       
   574 static void Load_ERNW(void)
       
   575 {
       
   576 	int index;
       
   577 
       
   578 	while ((index = SlIterateArray()) != -1) {
       
   579 		EngineRenew *er;
       
   580 
       
   581 		if (!AddBlockIfNeeded(&_EngineRenew_pool, index))
       
   582 			error("EngineRenews: failed loading savegame: too many EngineRenews");
       
   583 
       
   584 		er = GetEngineRenew(index);
       
   585 		SlObject(er, _engine_renew_desc);
       
   586 	}
       
   587 }
       
   588 
       
   589 static const SaveLoad _engine_desc[] = {
       
   590 	SLE_CONDVAR(Engine, intro_date,          SLE_FILE_U16 | SLE_VAR_I32,  0,  30),
       
   591 	SLE_CONDVAR(Engine, intro_date,          SLE_INT32,                  31, SL_MAX_VERSION),
       
   592 	SLE_CONDVAR(Engine, age,                 SLE_FILE_U16 | SLE_VAR_I32,  0,  30),
       
   593 	SLE_CONDVAR(Engine, age,                 SLE_INT32,                  31, SL_MAX_VERSION),
       
   594 	    SLE_VAR(Engine, reliability,         SLE_UINT16),
       
   595 	    SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16),
       
   596 	    SLE_VAR(Engine, reliability_start,   SLE_UINT16),
       
   597 	    SLE_VAR(Engine, reliability_max,     SLE_UINT16),
       
   598 	    SLE_VAR(Engine, reliability_final,   SLE_UINT16),
       
   599 	    SLE_VAR(Engine, duration_phase_1,    SLE_UINT16),
       
   600 	    SLE_VAR(Engine, duration_phase_2,    SLE_UINT16),
       
   601 	    SLE_VAR(Engine, duration_phase_3,    SLE_UINT16),
       
   602 
       
   603 	    SLE_VAR(Engine, lifelength,          SLE_UINT8),
       
   604 	    SLE_VAR(Engine, flags,               SLE_UINT8),
       
   605 	    SLE_VAR(Engine, preview_player,      SLE_UINT8),
       
   606 	    SLE_VAR(Engine, preview_wait,        SLE_UINT8),
       
   607 	    SLE_VAR(Engine, railtype,            SLE_UINT8),
       
   608 	    SLE_VAR(Engine, player_avail,        SLE_UINT8),
       
   609 
       
   610 	// reserve extra space in savegame here. (currently 16 bytes)
       
   611 	SLE_CONDNULL(16, 2, SL_MAX_VERSION),
       
   612 
       
   613 	SLE_END()
       
   614 };
       
   615 
       
   616 static void Save_ENGN(void)
       
   617 {
       
   618 	uint i;
       
   619 
       
   620 	for (i = 0; i != lengthof(_engines); i++) {
       
   621 		SlSetArrayIndex(i);
       
   622 		SlObject(&_engines[i], _engine_desc);
       
   623 	}
       
   624 }
       
   625 
       
   626 static void Load_ENGN(void)
       
   627 {
       
   628 	int index;
       
   629 	while ((index = SlIterateArray()) != -1) {
       
   630 		SlObject(GetEngine(index), _engine_desc);
       
   631 	}
       
   632 }
       
   633 
       
   634 static void LoadSave_ENGS(void)
       
   635 {
       
   636 	SlArray(_engine_name_strings, lengthof(_engine_name_strings), SLE_STRINGID);
       
   637 }
       
   638 
       
   639 const ChunkHandler _engine_chunk_handlers[] = {
       
   640 	{ 'ENGN', Save_ENGN,     Load_ENGN,     CH_ARRAY          },
       
   641 	{ 'ENGS', LoadSave_ENGS, LoadSave_ENGS, CH_RIFF           },
       
   642 	{ 'ERNW', Save_ERNW,     Load_ERNW,     CH_ARRAY | CH_LAST},
       
   643 };
       
   644 
       
   645 void InitializeEngines(void)
       
   646 {
       
   647 	/* Clean the engine renew pool and create 1 block in it */
       
   648 	CleanPool(&_EngineRenew_pool);
       
   649 	AddBlockToPool(&_EngineRenew_pool);
       
   650 }