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 } |
|