2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file vehicle_sl.cpp Code handling saving and loading of vehicles */
10 #include "../stdafx.h"
13 #include "compat/vehicle_sl_compat.h"
16 #include "../vehicle_func.h"
18 #include "../roadveh.h"
20 #include "../aircraft.h"
21 #include "../timetable.h"
22 #include "../station_base.h"
23 #include "../effectvehicle_base.h"
24 #include "../company_base.h"
25 #include "../company_func.h"
26 #include "../disaster_vehicle.h"
27 #include "../economy_base.h"
29 #include "../safeguards.h"
32 * Link front and rear multiheaded engines to each other
33 * This is done when loading a savegame
35 void ConnectMultiheadedTrains()
37 for (Train
*v
: Train::Iterate()) {
38 v
->other_multiheaded_part
= nullptr;
41 for (Train
*v
: Train::Iterate()) {
42 if (v
->IsFrontEngine() || v
->IsFreeWagon()) {
43 /* Two ways to associate multiheaded parts to each other:
44 * sequential-matching: Trains shall be arranged to look like <..>..<..>..<..>..
45 * bracket-matching: Free vehicle chains shall be arranged to look like ..<..<..>..<..>..>..
47 * Note: Old savegames might contain chains which do not comply with these rules, e.g.
48 * - the front and read parts have invalid orders
49 * - different engine types might be combined
50 * - there might be different amounts of front and rear parts.
52 * Note: The multiheaded parts need to be matched exactly like they are matched on the server, else desyncs will occur.
53 * This is why two matching strategies are needed.
56 bool sequential_matching
= v
->IsFrontEngine();
58 for (Train
*u
= v
; u
!= nullptr; u
= u
->GetNextVehicle()) {
59 if (u
->other_multiheaded_part
!= nullptr) continue; // we already linked this one
61 if (u
->IsMultiheaded()) {
63 /* we got a rear car without a front car. We will convert it to a front one */
68 /* Find a matching back part */
69 EngineID eid
= u
->engine_type
;
71 if (sequential_matching
) {
72 for (w
= u
->GetNextVehicle(); w
!= nullptr; w
= w
->GetNextVehicle()) {
73 if (w
->engine_type
!= eid
|| w
->other_multiheaded_part
!= nullptr || !w
->IsMultiheaded()) continue;
75 /* we found a car to partner with this engine. Now we will make sure it face the right way */
84 for (w
= u
->GetNextVehicle(); w
!= nullptr; w
= w
->GetNextVehicle()) {
85 if (w
->engine_type
!= eid
|| w
->other_multiheaded_part
!= nullptr || !w
->IsMultiheaded()) continue;
90 if (stack_pos
== 0) break;
97 w
->other_multiheaded_part
= u
;
98 u
->other_multiheaded_part
= w
;
100 /* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */
101 u
->ClearMultiheaded();
110 * Converts all trains to the new subtype format introduced in savegame 16.2
111 * It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found
113 void ConvertOldMultiheadToNew()
115 for (Train
*t
: Train::Iterate()) SetBit(t
->subtype
, 7); // indicates that it's the old format and needs to be converted in the next loop
117 for (Train
*t
: Train::Iterate()) {
118 if (HasBit(t
->subtype
, 7) && ((t
->subtype
& ~0x80) == 0 || (t
->subtype
& ~0x80) == 4)) {
119 for (Train
*u
= t
; u
!= nullptr; u
= u
->Next()) {
120 const RailVehicleInfo
*rvi
= RailVehInfo(u
->engine_type
);
122 ClrBit(u
->subtype
, 7);
123 switch (u
->subtype
) {
124 case 0: // TS_Front_Engine
125 if (rvi
->railveh_type
== RAILVEH_MULTIHEAD
) u
->SetMultiheaded();
130 case 1: // TS_Artic_Part
132 u
->SetArticulatedPart();
135 case 2: // TS_Not_First
137 if (rvi
->railveh_type
== RAILVEH_WAGON
) {
142 if (rvi
->railveh_type
== RAILVEH_MULTIHEAD
&& rvi
->image_index
== u
->spritenum
- 1) {
143 /* rear end of a multiheaded engine */
147 if (rvi
->railveh_type
== RAILVEH_MULTIHEAD
) u
->SetMultiheaded();
151 case 4: // TS_Free_Car
156 default: SlErrorCorrupt("Invalid train subtype");
164 /** need to be called to load aircraft from old version */
165 void UpdateOldAircraft()
167 /* set airport_flags to 0 for all airports just to be sure */
168 for (Station
*st
: Station::Iterate()) {
169 st
->airport
.flags
= 0; // reset airport
172 for (Aircraft
*a
: Aircraft::Iterate()) {
173 /* airplane has another vehicle with subtype 4 (shadow), helicopter also has 3 (rotor)
175 if (a
->IsNormalAircraft()) {
176 /* airplane in terminal stopped doesn't hurt anyone, so goto next */
177 if ((a
->vehstatus
& VS_STOPPED
) && a
->state
== 0) {
182 AircraftLeaveHangar(a
, a
->direction
); // make airplane visible if it was in a depot for example
183 a
->vehstatus
&= ~VS_STOPPED
; // make airplane moving
184 UpdateAircraftCache(a
);
185 a
->cur_speed
= a
->vcache
.cached_max_speed
; // so aircraft don't have zero speed while in air
186 if (!a
->current_order
.IsType(OT_GOTO_STATION
) && !a
->current_order
.IsType(OT_GOTO_DEPOT
)) {
187 /* reset current order so aircraft doesn't have invalid "station-only" order */
188 a
->current_order
.MakeDummy();
191 AircraftNextAirportPos_and_Order(a
); // move it to the entry point of the airport
192 GetNewVehiclePosResult gp
= GetNewVehiclePos(a
);
193 a
->tile
= TileIndex
{}; // aircraft in air is tile=0
195 /* correct speed of helicopter-rotors */
196 if (a
->subtype
== AIR_HELICOPTER
) a
->Next()->Next()->cur_speed
= 32;
198 /* set new position x,y,z */
199 GetAircraftFlightLevelBounds(a
, &a
->z_pos
, nullptr);
200 SetAircraftPosition(a
, gp
.x
, gp
.y
, GetAircraftFlightLevel(a
));
204 /* Clear aircraft from loading vehicles, if we bumped them into the air. */
205 for (Station
*st
: Station::Iterate()) {
206 for (auto iter
= st
->loading_vehicles
.begin(); iter
!= st
->loading_vehicles
.end(); /* nothing */) {
208 if (v
->type
== VEH_AIRCRAFT
&& !v
->current_order
.IsType(OT_LOADING
)) {
209 iter
= st
->loading_vehicles
.erase(iter
);
210 delete v
->cargo_payment
;
219 * Check all vehicles to ensure their engine type is valid
220 * for the currently loaded NewGRFs (that includes none...)
221 * This only makes a difference if NewGRFs are missing, otherwise
222 * all vehicles will be valid. This does not make such a game
223 * playable, it only prevents crash.
225 static void CheckValidVehicles()
227 size_t total_engines
= Engine::GetPoolSize();
228 EngineID first_engine
[4] = { INVALID_ENGINE
, INVALID_ENGINE
, INVALID_ENGINE
, INVALID_ENGINE
};
230 for (const Engine
*e
: Engine::IterateType(VEH_TRAIN
)) { first_engine
[VEH_TRAIN
] = e
->index
; break; }
231 for (const Engine
*e
: Engine::IterateType(VEH_ROAD
)) { first_engine
[VEH_ROAD
] = e
->index
; break; }
232 for (const Engine
*e
: Engine::IterateType(VEH_SHIP
)) { first_engine
[VEH_SHIP
] = e
->index
; break; }
233 for (const Engine
*e
: Engine::IterateType(VEH_AIRCRAFT
)) { first_engine
[VEH_AIRCRAFT
] = e
->index
; break; }
235 for (Vehicle
*v
: Vehicle::Iterate()) {
236 /* Test if engine types match */
242 if (v
->engine_type
>= total_engines
|| v
->type
!= v
->GetEngine()->type
) {
243 v
->engine_type
= first_engine
[v
->type
];
253 extern uint8_t _age_cargo_skip_counter
; // From misc_sl.cpp
255 /** Called after load for phase 1 of vehicle initialisation */
256 void AfterLoadVehiclesPhase1(bool part_of_load
)
258 for (Vehicle
*v
: Vehicle::Iterate()) {
259 /* Reinstate the previous pointer */
260 if (v
->Next() != nullptr) v
->Next()->previous
= v
;
261 if (v
->NextShared() != nullptr) v
->NextShared()->previous_shared
= v
;
263 if (part_of_load
) v
->fill_percent_te_id
= INVALID_TE_ID
;
265 if (v
->IsGroundVehicle()) v
->GetGroundVehicleCache()->first_engine
= INVALID_ENGINE
;
268 /* AfterLoadVehicles may also be called in case of NewGRF reload, in this
269 * case we may not convert orders again. */
271 /* Create shared vehicle chain for very old games (pre 5,2) and create
272 * OrderList from shared vehicle chains. For this to work correctly, the
273 * following conditions must be fulfilled:
274 * a) both next_shared and previous_shared are not set for pre 5,2 games
275 * b) both next_shared and previous_shared are set for later games
277 std::map
<Order
*, OrderList
*> mapping
;
279 for (Vehicle
*v
: Vehicle::Iterate()) {
280 if (v
->old_orders
!= nullptr) {
281 if (IsSavegameVersionBefore(SLV_105
)) { // Pre-105 didn't save an OrderList
282 if (mapping
[v
->old_orders
] == nullptr) {
283 /* This adds the whole shared vehicle chain for case b */
285 /* Creating an OrderList here is safe because the number of vehicles
286 * allowed in these savegames matches the number of OrderLists. As
287 * such each vehicle can get an OrderList and it will (still) fit. */
288 assert(OrderList::CanAllocateItem());
289 v
->orders
= mapping
[v
->old_orders
] = new OrderList(v
->old_orders
, v
);
291 v
->orders
= mapping
[v
->old_orders
];
292 /* For old games (case a) we must create the shared vehicle chain */
293 if (IsSavegameVersionBefore(SLV_5
, 2)) {
294 v
->AddToShared(v
->orders
->GetFirstSharedVehicle());
297 } else { // OrderList was saved as such, only recalculate not saved values
298 if (v
->PreviousShared() == nullptr) {
299 v
->orders
->Initialize(v
->orders
->first
, v
);
306 for (Vehicle
*v
: Vehicle::Iterate()) {
307 /* Fill the first pointers */
308 if (v
->Previous() == nullptr) {
309 for (Vehicle
*u
= v
; u
!= nullptr; u
= u
->Next()) {
316 if (IsSavegameVersionBefore(SLV_105
)) {
317 /* Before 105 there was no order for shared orders, thus it messed up horribly */
318 for (Vehicle
*v
: Vehicle::Iterate()) {
319 if (v
->First() != v
|| v
->orders
!= nullptr || v
->previous_shared
!= nullptr || v
->next_shared
== nullptr) continue;
321 /* As above, allocating OrderList here is safe. */
322 assert(OrderList::CanAllocateItem());
323 v
->orders
= new OrderList(nullptr, v
);
324 for (Vehicle
*u
= v
; u
!= nullptr; u
= u
->next_shared
) {
325 u
->orders
= v
->orders
;
330 if (IsSavegameVersionBefore(SLV_157
)) {
331 /* The road vehicle subtype was converted to a flag. */
332 for (RoadVehicle
*rv
: RoadVehicle::Iterate()) {
333 if (rv
->subtype
== 0) {
334 /* The road vehicle is at the front. */
335 rv
->SetFrontEngine();
336 } else if (rv
->subtype
== 1) {
337 /* The road vehicle is an articulated part. */
339 rv
->SetArticulatedPart();
341 SlErrorCorrupt("Invalid road vehicle subtype");
346 if (IsSavegameVersionBefore(SLV_160
)) {
347 /* In some old savegames there might be some "crap" stored. */
348 for (Vehicle
*v
: Vehicle::Iterate()) {
349 if (!v
->IsPrimaryVehicle() && v
->type
!= VEH_DISASTER
) {
350 v
->current_order
.Free();
356 if (IsSavegameVersionBefore(SLV_162
)) {
357 /* Set the vehicle-local cargo age counter from the old global counter. */
358 for (Vehicle
*v
: Vehicle::Iterate()) {
359 v
->cargo_age_counter
= _age_cargo_skip_counter
;
363 if (IsSavegameVersionBefore(SLV_180
)) {
364 /* Set service interval flags */
365 for (Vehicle
*v
: Vehicle::Iterate()) {
366 if (!v
->IsPrimaryVehicle()) continue;
368 const Company
*c
= Company::Get(v
->owner
);
369 int interval
= CompanyServiceInterval(c
, v
->type
);
371 v
->SetServiceIntervalIsCustom(v
->GetServiceInterval() != interval
);
372 v
->SetServiceIntervalIsPercent(c
->settings
.vehicle
.servint_ispercent
);
376 if (IsSavegameVersionBefore(SLV_SHIP_ROTATION
)) {
377 /* Ship rotation added */
378 for (Ship
*s
: Ship::Iterate()) {
379 s
->rotation
= s
->direction
;
382 for (Ship
*s
: Ship::Iterate()) {
383 if (s
->rotation
== s
->direction
) continue;
384 /* In case we are rotating on gameload, set the rotation position to
385 * the current position, otherwise the applied workaround offset would
386 * be with respect to 0,0.
388 s
->rotation_x_pos
= s
->x_pos
;
389 s
->rotation_y_pos
= s
->y_pos
;
393 if (IsSavegameVersionBefore(SLV_TIMETABLE_START_TICKS
)) {
394 /* Convert timetable start from a date to an absolute tick in TimerGameTick::counter. */
395 for (Vehicle
*v
: Vehicle::Iterate()) {
396 /* If the start date is 0, the vehicle is not waiting to start and can be ignored. */
397 if (v
->timetable_start
== 0) continue;
399 v
->timetable_start
= GetStartTickFromDate(TimerGameEconomy::Date(v
->timetable_start
));
403 if (IsSavegameVersionBefore(SLV_VEHICLE_ECONOMY_AGE
)) {
404 /* Set vehicle economy age based on calendar age. */
405 for (Vehicle
*v
: Vehicle::Iterate()) {
406 v
->economy_age
= TimerGameEconomy::Date
{v
->age
.base()};
411 CheckValidVehicles();
414 /** Called after load for phase 2 of vehicle initialisation */
415 void AfterLoadVehiclesPhase2(bool part_of_load
)
417 for (Vehicle
*v
: Vehicle::Iterate()) {
418 assert(v
->First() != nullptr);
420 v
->trip_occupancy
= CalcPercentVehicleFilled(v
, nullptr);
424 Train
*t
= Train::From(v
);
425 if (t
->IsFrontEngine() || t
->IsFreeWagon()) {
426 t
->gcache
.last_speed
= t
->cur_speed
; // update displayed train speed
427 t
->ConsistChanged(CCF_SAVELOAD
);
433 RoadVehicle
*rv
= RoadVehicle::From(v
);
434 if (rv
->IsFrontEngine()) {
435 rv
->gcache
.last_speed
= rv
->cur_speed
; // update displayed road vehicle speed
437 rv
->roadtype
= Engine::Get(rv
->engine_type
)->u
.road
.roadtype
;
438 rv
->compatible_roadtypes
= GetRoadTypeInfo(rv
->roadtype
)->powered_roadtypes
;
439 RoadTramType rtt
= GetRoadTramType(rv
->roadtype
);
440 for (RoadVehicle
*u
= rv
; u
!= nullptr; u
= u
->Next()) {
441 u
->roadtype
= rv
->roadtype
;
442 u
->compatible_roadtypes
= rv
->compatible_roadtypes
;
443 if (GetRoadType(u
->tile
, rtt
) == INVALID_ROADTYPE
) SlErrorCorrupt("Road vehicle on invalid road type");
446 RoadVehUpdateCache(rv
);
447 if (_settings_game
.vehicle
.roadveh_acceleration_model
!= AM_ORIGINAL
) {
455 Ship::From(v
)->UpdateCache();
462 /* Stop non-front engines */
463 if (part_of_load
&& IsSavegameVersionBefore(SLV_112
)) {
464 for (Vehicle
*v
: Vehicle::Iterate()) {
465 if (v
->type
== VEH_TRAIN
) {
466 Train
*t
= Train::From(v
);
467 if (!t
->IsFrontEngine()) {
468 if (t
->IsEngine()) t
->vehstatus
|= VS_STOPPED
;
469 /* cur_speed is now relevant for non-front parts - nonzero breaks
470 * moving-wagons-inside-depot- and autoreplace- code */
474 /* trains weren't stopping gradually in old OTTD versions (and TTO/TTD)
475 * other vehicle types didn't have zero speed while stopped (even in 'recent' OTTD versions) */
476 if ((v
->vehstatus
& VS_STOPPED
) && (v
->type
!= VEH_TRAIN
|| IsSavegameVersionBefore(SLV_2
, 1))) {
482 for (Vehicle
*v
: Vehicle::Iterate()) {
487 v
->GetImage(v
->direction
, EIT_ON_MAP
, &v
->sprite_cache
.sprite_seq
);
491 if (Aircraft::From(v
)->IsNormalAircraft()) {
492 v
->GetImage(v
->direction
, EIT_ON_MAP
, &v
->sprite_cache
.sprite_seq
);
494 /* The aircraft's shadow will have the same image as the aircraft, but no colour */
495 Vehicle
*shadow
= v
->Next();
496 if (shadow
== nullptr) SlErrorCorrupt("Missing shadow for aircraft");
498 shadow
->sprite_cache
.sprite_seq
.CopyWithoutPalette(v
->sprite_cache
.sprite_seq
);
500 /* In the case of a helicopter we will update the rotor sprites */
501 if (v
->subtype
== AIR_HELICOPTER
) {
502 Vehicle
*rotor
= shadow
->Next();
503 if (rotor
== nullptr) SlErrorCorrupt("Missing rotor for helicopter");
505 GetRotorImage(Aircraft::From(v
), EIT_ON_MAP
, &rotor
->sprite_cache
.sprite_seq
);
508 UpdateAircraftCache(Aircraft::From(v
), true);
513 auto *dv
= DisasterVehicle::From(v
);
514 if (dv
->subtype
== ST_SMALL_UFO
&& dv
->state
!= 0) {
515 RoadVehicle
*u
= RoadVehicle::GetIfValid(v
->dest_tile
.base());
516 if (u
!= nullptr && u
->IsFrontEngine()) {
517 /* Delete UFO targetting a vehicle which is already a target. */
518 if (u
->disaster_vehicle
!= INVALID_VEHICLE
&& u
->disaster_vehicle
!= dv
->index
) {
522 u
->disaster_vehicle
= dv
->index
;
532 if (part_of_load
&& v
->unitnumber
!= 0) {
533 Company::Get(v
->owner
)->freeunits
[v
->type
].UseID(v
->unitnumber
);
537 v
->coord
.left
= INVALID_COORD
;
538 v
->sprite_cache
.old_coord
.left
= INVALID_COORD
;
540 v
->UpdateViewport(false);
544 bool TrainController(Train
*v
, Vehicle
*nomove
, bool reverse
= true); // From train_cmd.cpp
545 void ReverseTrainDirection(Train
*v
);
546 void ReverseTrainSwapVeh(Train
*v
, int l
, int r
);
548 /** Fixup old train spacing. */
549 void FixupTrainLengths()
551 /* Vehicle center was moved from 4 units behind the front to half the length
552 * behind the front. Move vehicles so they end up on the same spot. */
553 for (Vehicle
*v
: Vehicle::Iterate()) {
554 if (v
->type
== VEH_TRAIN
&& v
->IsPrimaryVehicle()) {
555 /* The vehicle center is now more to the front depending on vehicle length,
556 * so we need to move all vehicles forward to cover the difference to the
557 * old center, otherwise wagon spacing in trains would be broken upon load. */
558 for (Train
*u
= Train::From(v
); u
!= nullptr; u
= u
->Next()) {
559 if (u
->track
== TRACK_BIT_DEPOT
|| (u
->vehstatus
& VS_CRASHED
)) continue;
561 Train
*next
= u
->Next();
563 /* Try to pull the vehicle half its length forward. */
564 int diff
= (VEHICLE_LENGTH
- u
->gcache
.cached_veh_length
) / 2;
566 for (done
= 0; done
< diff
; done
++) {
567 if (!TrainController(u
, next
, false)) break;
570 if (next
!= nullptr && done
< diff
&& u
->IsFrontEngine()) {
571 /* Pulling the front vehicle forwards failed, we either encountered a dead-end
572 * or a red signal. To fix this, we try to move the whole train the required
573 * space backwards and re-do the fix up of the front vehicle. */
575 /* Ignore any signals when backtracking. */
576 TrainForceProceeding old_tfp
= u
->force_proceed
;
577 u
->force_proceed
= TFP_SIGNAL
;
579 /* Swap start<>end, start+1<>end-1, ... */
580 int r
= CountVehiclesInChain(u
) - 1; // number of vehicles - 1
582 do ReverseTrainSwapVeh(u
, l
++, r
--); while (l
<= r
);
584 /* We moved the first vehicle which is now the last. Move it back to the
585 * original position as we will fix up the last vehicle later in the loop. */
586 for (int i
= 0; i
< done
; i
++) TrainController(u
->Last(), nullptr);
588 /* Move the train backwards to get space for the first vehicle. As the stopping
589 * distance from a line end is rounded up, move the train one unit more to cater
590 * for front vehicles with odd lengths. */
592 for (moved
= 0; moved
< diff
+ 1; moved
++) {
593 if (!TrainController(u
, nullptr, false)) break;
596 /* Swap start<>end, start+1<>end-1, ... again. */
597 r
= CountVehiclesInChain(u
) - 1; // number of vehicles - 1
599 do ReverseTrainSwapVeh(u
, l
++, r
--); while (l
<= r
);
601 u
->force_proceed
= old_tfp
;
603 /* Tracks are too short to fix the train length. The player has to fix the
604 * train in a depot. Bail out so we don't damage the vehicle chain any more. */
605 if (moved
< diff
+ 1) break;
607 /* Re-do the correction for the first vehicle. */
608 for (done
= 0; done
< diff
; done
++) TrainController(u
, next
, false);
610 /* We moved one unit more backwards than needed for even-length front vehicles,
611 * try to move that unit forward again. We don't care if this step fails. */
612 TrainController(u
, nullptr, false);
615 /* If the next wagon is still in a depot, check if it shouldn't be outside already. */
616 if (next
!= nullptr && next
->track
== TRACK_BIT_DEPOT
) {
617 int d
= TicksToLeaveDepot(u
);
619 /* Next vehicle should have left the depot already, show it and pull forward. */
620 next
->vehstatus
&= ~VS_HIDDEN
;
621 next
->track
= TrackToTrackBits(GetRailDepotTrack(next
->tile
));
622 for (int i
= 0; i
>= d
; i
--) TrainController(next
, nullptr);
627 /* Update all cached properties after moving the vehicle chain around. */
628 Train::From(v
)->ConsistChanged(CCF_TRACK
);
633 static uint8_t _cargo_periods
;
634 static StationID _cargo_source
;
635 static TileIndex _cargo_source_xy
;
636 static uint16_t _cargo_count
;
637 static uint16_t _cargo_paid_for
;
638 static Money _cargo_feeder_share
;
640 class SlVehicleCommon
: public DefaultSaveLoadHandler
<SlVehicleCommon
, Vehicle
> {
642 inline static const SaveLoad description
[] = {
643 SLE_VAR(Vehicle
, subtype
, SLE_UINT8
),
645 SLE_REF(Vehicle
, next
, REF_VEHICLE_OLD
),
646 SLE_CONDVAR(Vehicle
, name
, SLE_NAME
, SL_MIN_VERSION
, SLV_84
),
647 SLE_CONDSSTR(Vehicle
, name
, SLE_STR
| SLF_ALLOW_CONTROL
, SLV_84
, SL_MAX_VERSION
),
648 SLE_CONDVAR(Vehicle
, unitnumber
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_8
),
649 SLE_CONDVAR(Vehicle
, unitnumber
, SLE_UINT16
, SLV_8
, SL_MAX_VERSION
),
650 SLE_VAR(Vehicle
, owner
, SLE_UINT8
),
651 SLE_CONDVAR(Vehicle
, tile
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
652 SLE_CONDVAR(Vehicle
, tile
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
653 SLE_CONDVAR(Vehicle
, dest_tile
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
654 SLE_CONDVAR(Vehicle
, dest_tile
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
656 SLE_CONDVAR(Vehicle
, x_pos
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
657 SLE_CONDVAR(Vehicle
, x_pos
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
658 SLE_CONDVAR(Vehicle
, y_pos
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
659 SLE_CONDVAR(Vehicle
, y_pos
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
660 SLE_CONDVAR(Vehicle
, z_pos
, SLE_FILE_U8
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_164
),
661 SLE_CONDVAR(Vehicle
, z_pos
, SLE_INT32
, SLV_164
, SL_MAX_VERSION
),
662 SLE_VAR(Vehicle
, direction
, SLE_UINT8
),
664 SLE_VAR(Vehicle
, spritenum
, SLE_UINT8
),
665 SLE_VAR(Vehicle
, engine_type
, SLE_UINT16
),
666 SLE_VAR(Vehicle
, cur_speed
, SLE_UINT16
),
667 SLE_VAR(Vehicle
, subspeed
, SLE_UINT8
),
668 SLE_VAR(Vehicle
, acceleration
, SLE_UINT8
),
669 SLE_CONDVAR(Vehicle
, motion_counter
, SLE_UINT32
, SLV_VEH_MOTION_COUNTER
, SL_MAX_VERSION
),
670 SLE_VAR(Vehicle
, progress
, SLE_UINT8
),
672 SLE_VAR(Vehicle
, vehstatus
, SLE_UINT8
),
673 SLE_CONDVAR(Vehicle
, last_station_visited
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_5
),
674 SLE_CONDVAR(Vehicle
, last_station_visited
, SLE_UINT16
, SLV_5
, SL_MAX_VERSION
),
675 SLE_CONDVAR(Vehicle
, last_loading_station
, SLE_UINT16
, SLV_182
, SL_MAX_VERSION
),
677 SLE_VAR(Vehicle
, cargo_type
, SLE_UINT8
),
678 SLE_CONDVAR(Vehicle
, cargo_subtype
, SLE_UINT8
, SLV_35
, SL_MAX_VERSION
),
679 SLEG_CONDVAR("cargo_days", _cargo_periods
, SLE_UINT8
, SL_MIN_VERSION
, SLV_68
),
680 SLEG_CONDVAR("cargo_source", _cargo_source
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_7
),
681 SLEG_CONDVAR("cargo_source", _cargo_source
, SLE_UINT16
, SLV_7
, SLV_68
),
682 SLEG_CONDVAR("cargo_source_xy", _cargo_source_xy
, SLE_UINT32
, SLV_44
, SLV_68
),
683 SLE_VAR(Vehicle
, cargo_cap
, SLE_UINT16
),
684 SLE_CONDVAR(Vehicle
, refit_cap
, SLE_UINT16
, SLV_182
, SL_MAX_VERSION
),
685 SLEG_CONDVAR("cargo_count", _cargo_count
, SLE_UINT16
, SL_MIN_VERSION
, SLV_68
),
686 SLE_CONDREFLIST(Vehicle
, cargo
.packets
, REF_CARGO_PACKET
, SLV_68
, SL_MAX_VERSION
),
687 SLE_CONDARR(Vehicle
, cargo
.action_counts
, SLE_UINT
, VehicleCargoList::NUM_MOVE_TO_ACTION
, SLV_181
, SL_MAX_VERSION
),
688 SLE_CONDVAR(Vehicle
, cargo_age_counter
, SLE_UINT16
, SLV_162
, SL_MAX_VERSION
),
690 SLE_VAR(Vehicle
, day_counter
, SLE_UINT8
),
691 SLE_VAR(Vehicle
, tick_counter
, SLE_UINT8
),
692 SLE_CONDVAR(Vehicle
, running_ticks
, SLE_UINT8
, SLV_88
, SL_MAX_VERSION
),
694 SLE_VAR(Vehicle
, cur_implicit_order_index
, SLE_UINT8
),
695 SLE_CONDVAR(Vehicle
, cur_real_order_index
, SLE_UINT8
, SLV_158
, SL_MAX_VERSION
),
697 /* This next line is for version 4 and prior compatibility.. it temporarily reads
698 type and flags (which were both 4 bits) into type. Later on this is
699 converted correctly */
700 SLE_CONDVAR(Vehicle
, current_order
.type
, SLE_UINT8
, SL_MIN_VERSION
, SLV_5
),
701 SLE_CONDVAR(Vehicle
, current_order
.dest
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_5
),
703 /* Orders for version 5 and on */
704 SLE_CONDVAR(Vehicle
, current_order
.type
, SLE_UINT8
, SLV_5
, SL_MAX_VERSION
),
705 SLE_CONDVAR(Vehicle
, current_order
.flags
, SLE_UINT8
, SLV_5
, SL_MAX_VERSION
),
706 SLE_CONDVAR(Vehicle
, current_order
.dest
, SLE_UINT16
, SLV_5
, SL_MAX_VERSION
),
708 /* Refit in current order */
709 SLE_CONDVAR(Vehicle
, current_order
.refit_cargo
, SLE_UINT8
, SLV_36
, SL_MAX_VERSION
),
711 /* Timetable in current order */
712 SLE_CONDVAR(Vehicle
, current_order
.wait_time
, SLE_UINT16
, SLV_67
, SL_MAX_VERSION
),
713 SLE_CONDVAR(Vehicle
, current_order
.travel_time
, SLE_UINT16
, SLV_67
, SL_MAX_VERSION
),
714 SLE_CONDVAR(Vehicle
, current_order
.max_speed
, SLE_UINT16
, SLV_174
, SL_MAX_VERSION
),
715 SLE_CONDVAR(Vehicle
, timetable_start
, SLE_FILE_I32
| SLE_VAR_U64
, SLV_129
, SLV_TIMETABLE_START_TICKS
),
716 SLE_CONDVAR(Vehicle
, timetable_start
, SLE_UINT64
, SLV_TIMETABLE_START_TICKS
, SL_MAX_VERSION
),
718 SLE_CONDREF(Vehicle
, orders
, REF_ORDER
, SL_MIN_VERSION
, SLV_105
),
719 SLE_CONDREF(Vehicle
, orders
, REF_ORDERLIST
, SLV_105
, SL_MAX_VERSION
),
721 SLE_CONDVAR(Vehicle
, age
, SLE_FILE_U16
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_31
),
722 SLE_CONDVAR(Vehicle
, age
, SLE_INT32
, SLV_31
, SL_MAX_VERSION
),
723 SLE_CONDVAR(Vehicle
, economy_age
, SLE_INT32
, SLV_VEHICLE_ECONOMY_AGE
, SL_MAX_VERSION
),
724 SLE_CONDVAR(Vehicle
, max_age
, SLE_FILE_U16
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_31
),
725 SLE_CONDVAR(Vehicle
, max_age
, SLE_INT32
, SLV_31
, SL_MAX_VERSION
),
726 SLE_CONDVAR(Vehicle
, date_of_last_service
, SLE_FILE_U16
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_31
),
727 SLE_CONDVAR(Vehicle
, date_of_last_service
, SLE_INT32
, SLV_31
, SL_MAX_VERSION
),
728 SLE_CONDVAR(Vehicle
, date_of_last_service_newgrf
, SLE_INT32
, SLV_NEWGRF_LAST_SERVICE
, SL_MAX_VERSION
),
729 SLE_CONDVAR(Vehicle
, service_interval
, SLE_UINT16
, SL_MIN_VERSION
, SLV_31
),
730 SLE_CONDVAR(Vehicle
, service_interval
, SLE_FILE_U32
| SLE_VAR_U16
, SLV_31
, SLV_180
),
731 SLE_CONDVAR(Vehicle
, service_interval
, SLE_UINT16
, SLV_180
, SL_MAX_VERSION
),
732 SLE_VAR(Vehicle
, reliability
, SLE_UINT16
),
733 SLE_VAR(Vehicle
, reliability_spd_dec
, SLE_UINT16
),
734 SLE_VAR(Vehicle
, breakdown_ctr
, SLE_UINT8
),
735 SLE_VAR(Vehicle
, breakdown_delay
, SLE_UINT8
),
736 SLE_VAR(Vehicle
, breakdowns_since_last_service
, SLE_UINT8
),
737 SLE_VAR(Vehicle
, breakdown_chance
, SLE_UINT8
),
738 SLE_CONDVAR(Vehicle
, build_year
, SLE_FILE_U8
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_31
),
739 SLE_CONDVAR(Vehicle
, build_year
, SLE_INT32
, SLV_31
, SL_MAX_VERSION
),
741 SLE_VAR(Vehicle
, load_unload_ticks
, SLE_UINT16
),
742 SLEG_CONDVAR("cargo_paid_for", _cargo_paid_for
, SLE_UINT16
, SLV_45
, SL_MAX_VERSION
),
743 SLE_CONDVAR(Vehicle
, vehicle_flags
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_40
, SLV_180
),
744 SLE_CONDVAR(Vehicle
, vehicle_flags
, SLE_UINT16
, SLV_180
, SL_MAX_VERSION
),
746 SLE_CONDVAR(Vehicle
, profit_this_year
, SLE_FILE_I32
| SLE_VAR_I64
, SL_MIN_VERSION
, SLV_65
),
747 SLE_CONDVAR(Vehicle
, profit_this_year
, SLE_INT64
, SLV_65
, SL_MAX_VERSION
),
748 SLE_CONDVAR(Vehicle
, profit_last_year
, SLE_FILE_I32
| SLE_VAR_I64
, SL_MIN_VERSION
, SLV_65
),
749 SLE_CONDVAR(Vehicle
, profit_last_year
, SLE_INT64
, SLV_65
, SL_MAX_VERSION
),
750 SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share
, SLE_FILE_I32
| SLE_VAR_I64
, SLV_51
, SLV_65
),
751 SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share
, SLE_INT64
, SLV_65
, SLV_68
),
752 SLE_CONDVAR(Vehicle
, value
, SLE_FILE_I32
| SLE_VAR_I64
, SL_MIN_VERSION
, SLV_65
),
753 SLE_CONDVAR(Vehicle
, value
, SLE_INT64
, SLV_65
, SL_MAX_VERSION
),
755 SLE_CONDVAR(Vehicle
, random_bits
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_2
, SLV_EXTEND_VEHICLE_RANDOM
),
756 SLE_CONDVAR(Vehicle
, random_bits
, SLE_UINT16
, SLV_EXTEND_VEHICLE_RANDOM
, SL_MAX_VERSION
),
757 SLE_CONDVAR(Vehicle
, waiting_triggers
, SLE_UINT8
, SLV_2
, SL_MAX_VERSION
),
759 SLE_CONDREF(Vehicle
, next_shared
, REF_VEHICLE
, SLV_2
, SL_MAX_VERSION
),
760 SLE_CONDVAR(Vehicle
, group_id
, SLE_UINT16
, SLV_60
, SL_MAX_VERSION
),
762 SLE_CONDVAR(Vehicle
, current_order_time
, SLE_FILE_U32
| SLE_VAR_I32
, SLV_67
, SLV_TIMETABLE_TICKS_TYPE
),
763 SLE_CONDVAR(Vehicle
, current_order_time
, SLE_INT32
, SLV_TIMETABLE_TICKS_TYPE
, SL_MAX_VERSION
),
764 SLE_CONDVAR(Vehicle
, last_loading_tick
, SLE_UINT64
, SLV_LAST_LOADING_TICK
, SL_MAX_VERSION
),
765 SLE_CONDVAR(Vehicle
, lateness_counter
, SLE_INT32
, SLV_67
, SL_MAX_VERSION
),
767 SLE_CONDVAR(Vehicle
, depot_unbunching_last_departure
, SLE_UINT64
, SLV_DEPOT_UNBUNCHING
, SL_MAX_VERSION
),
768 SLE_CONDVAR(Vehicle
, depot_unbunching_next_departure
, SLE_UINT64
, SLV_DEPOT_UNBUNCHING
, SL_MAX_VERSION
),
769 SLE_CONDVAR(Vehicle
, round_trip_time
, SLE_INT32
, SLV_DEPOT_UNBUNCHING
, SL_MAX_VERSION
),
772 inline const static SaveLoadCompatTable compat_description
= _vehicle_common_sl_compat
;
774 void Save(Vehicle
*v
) const override
776 SlObject(v
, this->GetDescription());
779 void Load(Vehicle
*v
) const override
781 SlObject(v
, this->GetLoadDescription());
784 void FixPointers(Vehicle
*v
) const override
786 SlObject(v
, this->GetDescription());
790 class SlVehicleTrain
: public DefaultSaveLoadHandler
<SlVehicleTrain
, Vehicle
> {
792 inline static const SaveLoad description
[] = {
793 SLEG_STRUCT("common", SlVehicleCommon
),
794 SLE_VAR(Train
, crash_anim_pos
, SLE_UINT16
),
795 SLE_VAR(Train
, force_proceed
, SLE_UINT8
),
796 SLE_VAR(Train
, railtype
, SLE_UINT8
),
797 SLE_VAR(Train
, track
, SLE_UINT8
),
799 SLE_CONDVAR(Train
, flags
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_2
, SLV_100
),
800 SLE_CONDVAR(Train
, flags
, SLE_UINT16
, SLV_100
, SL_MAX_VERSION
),
801 SLE_CONDVAR(Train
, wait_counter
, SLE_UINT16
, SLV_136
, SL_MAX_VERSION
),
802 SLE_CONDVAR(Train
, gv_flags
, SLE_UINT16
, SLV_139
, SL_MAX_VERSION
),
804 inline const static SaveLoadCompatTable compat_description
= _vehicle_train_sl_compat
;
806 void Save(Vehicle
*v
) const override
808 if (v
->type
!= VEH_TRAIN
) return;
809 SlObject(v
, this->GetDescription());
812 void Load(Vehicle
*v
) const override
814 if (v
->type
!= VEH_TRAIN
) return;
815 SlObject(v
, this->GetLoadDescription());
818 void FixPointers(Vehicle
*v
) const override
820 if (v
->type
!= VEH_TRAIN
) return;
821 SlObject(v
, this->GetDescription());
825 class SlVehicleRoadVehPath
: public VectorSaveLoadHandler
<SlVehicleRoadVehPath
, RoadVehicle
, RoadVehPathElement
> {
827 inline static const SaveLoad description
[] = {
828 SLE_VAR(RoadVehPathElement
, trackdir
, SLE_UINT8
),
829 SLE_VAR(RoadVehPathElement
, tile
, SLE_UINT32
),
831 inline const static SaveLoadCompatTable compat_description
= {};
833 std::vector
<RoadVehPathElement
> &GetVector(RoadVehicle
*rv
) const override
{ return rv
->path
; }
836 class SlVehicleRoadVeh
: public DefaultSaveLoadHandler
<SlVehicleRoadVeh
, Vehicle
> {
838 /* RoadVehicle path is stored in std::pair which cannot be directly saved. */
839 static inline std::vector
<Trackdir
> rv_path_td
;
840 static inline std::vector
<TileIndex
> rv_path_tile
;
842 inline static const SaveLoad description
[] = {
843 SLEG_STRUCT("common", SlVehicleCommon
),
844 SLE_VAR(RoadVehicle
, state
, SLE_UINT8
),
845 SLE_VAR(RoadVehicle
, frame
, SLE_UINT8
),
846 SLE_VAR(RoadVehicle
, blocked_ctr
, SLE_UINT16
),
847 SLE_VAR(RoadVehicle
, overtaking
, SLE_UINT8
),
848 SLE_VAR(RoadVehicle
, overtaking_ctr
, SLE_UINT8
),
849 SLE_VAR(RoadVehicle
, crashed_ctr
, SLE_UINT16
),
850 SLE_VAR(RoadVehicle
, reverse_ctr
, SLE_UINT8
),
851 SLEG_CONDVECTOR("path.td", rv_path_td
, SLE_UINT8
, SLV_ROADVEH_PATH_CACHE
, SLV_PATH_CACHE_FORMAT
),
852 SLEG_CONDVECTOR("path.tile", rv_path_tile
, SLE_UINT32
, SLV_ROADVEH_PATH_CACHE
, SLV_PATH_CACHE_FORMAT
),
853 SLEG_CONDSTRUCTLIST("path", SlVehicleRoadVehPath
, SLV_PATH_CACHE_FORMAT
, SL_MAX_VERSION
),
854 SLE_CONDVAR(RoadVehicle
, gv_flags
, SLE_UINT16
, SLV_139
, SL_MAX_VERSION
),
856 inline const static SaveLoadCompatTable compat_description
= _vehicle_roadveh_sl_compat
;
858 static void ConvertPathCache(RoadVehicle
&rv
)
860 /* The two vectors should be the same size, but if not we can just ignore the cache and not cause more issues. */
861 if (rv_path_td
.size() != rv_path_tile
.size()) {
862 Debug(sl
, 1, "Found RoadVehicle {} with invalid path cache, ignoring.", rv
.index
);
865 size_t n
= std::min(rv_path_td
.size(), rv_path_tile
.size());
869 for (size_t c
= 0; c
< n
; ++c
) {
870 rv
.path
.emplace_back(rv_path_td
[c
], rv_path_tile
[c
]);
873 /* Path cache is now taken from back instead of front, so needs reversing. */
874 std::reverse(std::begin(rv
.path
), std::end(rv
.path
));
877 void Save(Vehicle
*v
) const override
879 if (v
->type
!= VEH_ROAD
) return;
880 SlObject(v
, this->GetDescription());
883 void Load(Vehicle
*v
) const override
885 if (v
->type
!= VEH_ROAD
) return;
886 SlObject(v
, this->GetLoadDescription());
887 if (!IsSavegameVersionBefore(SLV_ROADVEH_PATH_CACHE
) && IsSavegameVersionBefore(SLV_PATH_CACHE_FORMAT
)) {
888 ConvertPathCache(*static_cast<RoadVehicle
*>(v
));
892 void FixPointers(Vehicle
*v
) const override
894 if (v
->type
!= VEH_ROAD
) return;
895 SlObject(v
, this->GetDescription());
899 class SlVehicleShipPath
: public VectorSaveLoadHandler
<SlVehicleShipPath
, Ship
, ShipPathElement
> {
901 inline static const SaveLoad description
[] = {
902 SLE_VAR(ShipPathElement
, trackdir
, SLE_UINT8
),
904 inline const static SaveLoadCompatTable compat_description
= {};
906 std::vector
<ShipPathElement
> &GetVector(Ship
*s
) const override
{ return s
->path
; }
909 class SlVehicleShip
: public DefaultSaveLoadHandler
<SlVehicleShip
, Vehicle
> {
911 static inline std::vector
<Trackdir
> ship_path_td
;
913 inline static const SaveLoad description
[] = {
914 SLEG_STRUCT("common", SlVehicleCommon
),
915 SLE_VAR(Ship
, state
, SLE_UINT8
),
916 SLEG_CONDVECTOR("path", ship_path_td
, SLE_UINT8
, SLV_SHIP_PATH_CACHE
, SLV_PATH_CACHE_FORMAT
),
917 SLEG_CONDSTRUCTLIST("path", SlVehicleShipPath
, SLV_PATH_CACHE_FORMAT
, SL_MAX_VERSION
),
918 SLE_CONDVAR(Ship
, rotation
, SLE_UINT8
, SLV_SHIP_ROTATION
, SL_MAX_VERSION
),
920 inline const static SaveLoadCompatTable compat_description
= _vehicle_ship_sl_compat
;
922 void Save(Vehicle
*v
) const override
924 if (v
->type
!= VEH_SHIP
) return;
925 SlObject(v
, this->GetDescription());
928 void Load(Vehicle
*v
) const override
930 if (v
->type
!= VEH_SHIP
) return;
931 SlObject(v
, this->GetLoadDescription());
933 if (IsSavegameVersionBefore(SLV_PATH_CACHE_FORMAT
)) {
934 /* Path cache is now taken from back instead of front, so needs reversing. */
935 Ship
*s
= static_cast<Ship
*>(v
);
936 std::transform(std::rbegin(ship_path_td
), std::rend(ship_path_td
), std::back_inserter(s
->path
), [](Trackdir trackdir
) { return trackdir
; });
940 void FixPointers(Vehicle
*v
) const override
942 if (v
->type
!= VEH_SHIP
) return;
943 SlObject(v
, this->GetDescription());
947 class SlVehicleAircraft
: public DefaultSaveLoadHandler
<SlVehicleAircraft
, Vehicle
> {
949 inline static const SaveLoad description
[] = {
950 SLEG_STRUCT("common", SlVehicleCommon
),
951 SLE_VAR(Aircraft
, crashed_counter
, SLE_UINT16
),
952 SLE_VAR(Aircraft
, pos
, SLE_UINT8
),
954 SLE_CONDVAR(Aircraft
, targetairport
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_5
),
955 SLE_CONDVAR(Aircraft
, targetairport
, SLE_UINT16
, SLV_5
, SL_MAX_VERSION
),
957 SLE_VAR(Aircraft
, state
, SLE_UINT8
),
959 SLE_CONDVAR(Aircraft
, previous_pos
, SLE_UINT8
, SLV_2
, SL_MAX_VERSION
),
960 SLE_CONDVAR(Aircraft
, last_direction
, SLE_UINT8
, SLV_2
, SL_MAX_VERSION
),
961 SLE_CONDVAR(Aircraft
, number_consecutive_turns
, SLE_UINT8
, SLV_2
, SL_MAX_VERSION
),
963 SLE_CONDVAR(Aircraft
, turn_counter
, SLE_UINT8
, SLV_136
, SL_MAX_VERSION
),
964 SLE_CONDVAR(Aircraft
, flags
, SLE_UINT8
, SLV_167
, SL_MAX_VERSION
),
966 inline const static SaveLoadCompatTable compat_description
= _vehicle_aircraft_sl_compat
;
968 void Save(Vehicle
*v
) const override
970 if (v
->type
!= VEH_AIRCRAFT
) return;
971 SlObject(v
, this->GetDescription());
974 void Load(Vehicle
*v
) const override
976 if (v
->type
!= VEH_AIRCRAFT
) return;
977 SlObject(v
, this->GetLoadDescription());
980 void FixPointers(Vehicle
*v
) const override
982 if (v
->type
!= VEH_AIRCRAFT
) return;
983 SlObject(v
, this->GetDescription());
987 class SlVehicleEffect
: public DefaultSaveLoadHandler
<SlVehicleEffect
, Vehicle
> {
989 inline static const SaveLoad description
[] = {
990 SLE_VAR(Vehicle
, subtype
, SLE_UINT8
),
992 SLE_CONDVAR(Vehicle
, tile
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
993 SLE_CONDVAR(Vehicle
, tile
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
995 SLE_CONDVAR(Vehicle
, x_pos
, SLE_FILE_I16
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_6
),
996 SLE_CONDVAR(Vehicle
, x_pos
, SLE_INT32
, SLV_6
, SL_MAX_VERSION
),
997 SLE_CONDVAR(Vehicle
, y_pos
, SLE_FILE_I16
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_6
),
998 SLE_CONDVAR(Vehicle
, y_pos
, SLE_INT32
, SLV_6
, SL_MAX_VERSION
),
999 SLE_CONDVAR(Vehicle
, z_pos
, SLE_FILE_U8
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_164
),
1000 SLE_CONDVAR(Vehicle
, z_pos
, SLE_INT32
, SLV_164
, SL_MAX_VERSION
),
1002 SLE_VAR(Vehicle
, sprite_cache
.sprite_seq
.seq
[0].sprite
, SLE_FILE_U16
| SLE_VAR_U32
),
1003 SLE_VAR(Vehicle
, progress
, SLE_UINT8
),
1004 SLE_VAR(Vehicle
, vehstatus
, SLE_UINT8
),
1006 SLE_VAR(EffectVehicle
, animation_state
, SLE_UINT16
),
1007 SLE_VAR(EffectVehicle
, animation_substate
, SLE_UINT8
),
1009 SLE_CONDVAR(Vehicle
, spritenum
, SLE_UINT8
, SLV_2
, SL_MAX_VERSION
),
1011 inline const static SaveLoadCompatTable compat_description
= _vehicle_effect_sl_compat
;
1013 void Save(Vehicle
*v
) const override
1015 if (v
->type
!= VEH_EFFECT
) return;
1016 SlObject(v
, this->GetDescription());
1019 void Load(Vehicle
*v
) const override
1021 if (v
->type
!= VEH_EFFECT
) return;
1022 SlObject(v
, this->GetLoadDescription());
1025 void FixPointers(Vehicle
*v
) const override
1027 if (v
->type
!= VEH_EFFECT
) return;
1028 SlObject(v
, this->GetDescription());
1032 class SlVehicleDisaster
: public DefaultSaveLoadHandler
<SlVehicleDisaster
, Vehicle
> {
1034 inline static const SaveLoad description
[] = {
1035 SLE_REF(Vehicle
, next
, REF_VEHICLE_OLD
),
1037 SLE_VAR(Vehicle
, subtype
, SLE_UINT8
),
1038 SLE_CONDVAR(Vehicle
, tile
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
1039 SLE_CONDVAR(Vehicle
, tile
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
1040 SLE_CONDVAR(Vehicle
, dest_tile
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
1041 SLE_CONDVAR(Vehicle
, dest_tile
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
1043 SLE_CONDVAR(Vehicle
, x_pos
, SLE_FILE_I16
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_6
),
1044 SLE_CONDVAR(Vehicle
, x_pos
, SLE_INT32
, SLV_6
, SL_MAX_VERSION
),
1045 SLE_CONDVAR(Vehicle
, y_pos
, SLE_FILE_I16
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_6
),
1046 SLE_CONDVAR(Vehicle
, y_pos
, SLE_INT32
, SLV_6
, SL_MAX_VERSION
),
1047 SLE_CONDVAR(Vehicle
, z_pos
, SLE_FILE_U8
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_164
),
1048 SLE_CONDVAR(Vehicle
, z_pos
, SLE_INT32
, SLV_164
, SL_MAX_VERSION
),
1049 SLE_VAR(Vehicle
, direction
, SLE_UINT8
),
1051 SLE_VAR(Vehicle
, owner
, SLE_UINT8
),
1052 SLE_VAR(Vehicle
, vehstatus
, SLE_UINT8
),
1053 SLE_CONDVARNAME(DisasterVehicle
, state
, "current_order.dest", SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_5
),
1054 SLE_CONDVARNAME(DisasterVehicle
, state
, "current_order.dest", SLE_UINT16
, SLV_5
, SLV_DISASTER_VEH_STATE
),
1055 SLE_CONDVAR(DisasterVehicle
, state
, SLE_UINT16
, SLV_DISASTER_VEH_STATE
, SL_MAX_VERSION
),
1057 SLE_VAR(Vehicle
, sprite_cache
.sprite_seq
.seq
[0].sprite
, SLE_FILE_U16
| SLE_VAR_U32
),
1058 SLE_CONDVAR(Vehicle
, age
, SLE_FILE_U16
| SLE_VAR_I32
, SL_MIN_VERSION
, SLV_31
),
1059 SLE_CONDVAR(Vehicle
, age
, SLE_INT32
, SLV_31
, SL_MAX_VERSION
),
1060 SLE_VAR(Vehicle
, tick_counter
, SLE_UINT8
),
1062 SLE_CONDVAR(DisasterVehicle
, image_override
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_191
),
1063 SLE_CONDVAR(DisasterVehicle
, image_override
, SLE_UINT32
, SLV_191
, SL_MAX_VERSION
),
1064 SLE_CONDVAR(DisasterVehicle
, big_ufo_destroyer_target
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_191
),
1065 SLE_CONDVAR(DisasterVehicle
, big_ufo_destroyer_target
, SLE_UINT32
, SLV_191
, SL_MAX_VERSION
),
1066 SLE_CONDVAR(DisasterVehicle
, flags
, SLE_UINT8
, SLV_194
, SL_MAX_VERSION
),
1069 inline const static SaveLoadCompatTable compat_description
= _vehicle_disaster_sl_compat
;
1071 void Save(Vehicle
*v
) const override
1073 if (v
->type
!= VEH_DISASTER
) return;
1074 SlObject(v
, this->GetDescription());
1077 void Load(Vehicle
*v
) const override
1079 if (v
->type
!= VEH_DISASTER
) return;
1080 SlObject(v
, this->GetLoadDescription());
1083 void FixPointers(Vehicle
*v
) const override
1085 if (v
->type
!= VEH_DISASTER
) return;
1086 SlObject(v
, this->GetDescription());
1090 const static SaveLoad _vehicle_desc
[] = {
1091 SLE_SAVEBYTE(Vehicle
, type
),
1092 SLEG_STRUCT("train", SlVehicleTrain
),
1093 SLEG_STRUCT("roadveh", SlVehicleRoadVeh
),
1094 SLEG_STRUCT("ship", SlVehicleShip
),
1095 SLEG_STRUCT("aircraft", SlVehicleAircraft
),
1096 SLEG_STRUCT("effect", SlVehicleEffect
),
1097 SLEG_STRUCT("disaster", SlVehicleDisaster
),
1100 struct VEHSChunkHandler
: ChunkHandler
{
1101 VEHSChunkHandler() : ChunkHandler('VEHS', CH_SPARSE_TABLE
) {}
1103 void Save() const override
1105 SlTableHeader(_vehicle_desc
);
1107 /* Write the vehicles */
1108 for (Vehicle
*v
: Vehicle::Iterate()) {
1109 SlSetArrayIndex(v
->index
);
1110 SlObject(v
, _vehicle_desc
);
1114 void Load() const override
1116 const std::vector
<SaveLoad
> slt
= SlCompatTableHeader(_vehicle_desc
, _vehicle_sl_compat
);
1122 while ((index
= SlIterateArray()) != -1) {
1124 VehicleType vtype
= (VehicleType
)SlReadByte();
1127 case VEH_TRAIN
: v
= new (index
) Train(); break;
1128 case VEH_ROAD
: v
= new (index
) RoadVehicle(); break;
1129 case VEH_SHIP
: v
= new (index
) Ship(); break;
1130 case VEH_AIRCRAFT
: v
= new (index
) Aircraft(); break;
1131 case VEH_EFFECT
: v
= new (index
) EffectVehicle(); break;
1132 case VEH_DISASTER
: v
= new (index
) DisasterVehicle(); break;
1133 case VEH_INVALID
: // Savegame shouldn't contain invalid vehicles
1134 default: SlErrorCorrupt("Invalid vehicle type");
1139 if (_cargo_count
!= 0 && IsCompanyBuildableVehicleType(v
) && CargoPacket::CanAllocateItem()) {
1140 /* Don't construct the packet with station here, because that'll fail with old savegames */
1141 CargoPacket
*cp
= new CargoPacket(_cargo_count
, _cargo_periods
, _cargo_source
, _cargo_source_xy
, _cargo_feeder_share
);
1142 v
->cargo
.Append(cp
);
1145 /* Old savegames used 'last_station_visited = 0xFF' */
1146 if (IsSavegameVersionBefore(SLV_5
) && v
->last_station_visited
== 0xFF) {
1147 v
->last_station_visited
= INVALID_STATION
;
1150 if (IsSavegameVersionBefore(SLV_182
)) v
->last_loading_station
= INVALID_STATION
;
1152 if (IsSavegameVersionBefore(SLV_5
)) {
1153 /* Convert the current_order.type (which is a mix of type and flags, because
1154 * in those versions, they both were 4 bits big) to type and flags */
1155 v
->current_order
.flags
= GB(v
->current_order
.type
, 4, 4);
1156 v
->current_order
.type
&= 0x0F;
1159 /* Advanced vehicle lists got added */
1160 if (IsSavegameVersionBefore(SLV_60
)) v
->group_id
= DEFAULT_GROUP
;
1164 void FixPointers() const override
1166 for (Vehicle
*v
: Vehicle::Iterate()) {
1167 SlObject(v
, _vehicle_desc
);
1172 static const VEHSChunkHandler VEHS
;
1173 static const ChunkHandlerRef veh_chunk_handlers
[] = {
1177 extern const ChunkHandlerTable
_veh_chunk_handlers(veh_chunk_handlers
);