From 9a602ff304457e9c45ec276393853e4f550b39a9 Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Sun, 6 Aug 2023 12:57:10 -0400 Subject: [PATCH] Fix #10334: Store separate newgrf-safe version of date_of_last_service. (#11124) This value is not changed when the date cheat is used, which caused issues with changing properties based on service date. Co-authored-by: Peter Nelson --- src/aircraft_cmd.cpp | 2 ++ src/articulated_vehicles.cpp | 1 + src/newgrf_engine.cpp | 6 +++--- src/roadveh_cmd.cpp | 1 + src/saveload/afterload.cpp | 7 +++++++ src/saveload/saveload.h | 1 + src/saveload/vehicle_sl.cpp | 1 + src/ship_cmd.cpp | 1 + src/train_cmd.cpp | 3 +++ src/vehicle.cpp | 3 +++ src/vehicle_base.h | 1 + 11 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index 9ce0cf3164..9960faaa2b 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -339,6 +339,7 @@ CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine * v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_aircraft); v->date_of_last_service = TimerGameCalendar::date; + v->date_of_last_service_newgrf = TimerGameCalendar::date; v->build_year = u->build_year = TimerGameCalendar::year; v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY); @@ -1557,6 +1558,7 @@ static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass * if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) { /* an excerpt of ServiceAircraft, without the invisibility stuff */ v->date_of_last_service = TimerGameCalendar::date; + v->date_of_last_service_newgrf = TimerGameCalendar::date; v->breakdowns_since_last_service = 0; v->reliability = v->GetEngine()->reliability; SetWindowDirty(WC_VEHICLE_DETAILS, v->index); diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index 291deab99f..a0bea42b97 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -398,6 +398,7 @@ void AddArticulatedParts(Vehicle *first) v->y_pos = first->y_pos; v->z_pos = first->z_pos; v->date_of_last_service = first->date_of_last_service; + v->date_of_last_service_newgrf = first->date_of_last_service_newgrf; v->build_year = first->build_year; v->vehstatus = first->vehstatus & ~VS_STOPPED; diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index e1e7e7a0bd..6007860861 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -597,7 +597,7 @@ static uint32_t VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *objec } case 0x4B: // Long date of last service - return v->date_of_last_service; + return v->date_of_last_service_newgrf; case 0x4C: // Current maximum speed in NewGRF units if (!v->IsPrimaryVehicle()) return 0; @@ -767,8 +767,8 @@ static uint32_t VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *objec } return (variable - 0x80) == 0x10 ? ticks : GB(ticks, 8, 8); } - case 0x12: return ClampTo(v->date_of_last_service - DAYS_TILL_ORIGINAL_BASE_YEAR); - case 0x13: return GB(ClampTo(v->date_of_last_service - DAYS_TILL_ORIGINAL_BASE_YEAR), 8, 8); + case 0x12: return ClampTo(v->date_of_last_service_newgrf - DAYS_TILL_ORIGINAL_BASE_YEAR); + case 0x13: return GB(ClampTo(v->date_of_last_service_newgrf - DAYS_TILL_ORIGINAL_BASE_YEAR), 8, 8); case 0x14: return v->GetServiceInterval(); case 0x15: return GB(v->GetServiceInterval(), 8, 8); case 0x16: return v->last_station_visited; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index d022e233fe..7f1ef4f116 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -300,6 +300,7 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin v->SetServiceInterval(Company::Get(v->owner)->settings.vehicle.servint_roadveh); v->date_of_last_service = TimerGameCalendar::date; + v->date_of_last_service_newgrf = TimerGameCalendar::date; v->build_year = TimerGameCalendar::year; v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 8d784533db..26497ed0e9 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3234,6 +3234,13 @@ bool AfterLoadGame() _new_competitor_timeout.fired = _new_competitor_timeout.period == 0; } + if (IsSavegameVersionBefore(SLV_NEWGRF_LAST_SERVICE)) { + /* Set service date provided to NewGRF. */ + for (Vehicle *v : Vehicle::Iterate()) { + v->date_of_last_service_newgrf = v->date_of_last_service; + } + } + AfterLoadLabelMaps(); AfterLoadCompanyStats(); AfterLoadStoryBook(); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 3aedfb266f..8c7c283774 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -358,6 +358,7 @@ enum SaveLoadVersion : uint16_t { SLV_INDUSTRY_CARGO_REORGANISE, ///< 315 PR#10853 Industry accepts/produced data reorganised. SLV_PERIODS_IN_TRANSIT_RENAME, ///< 316 PR#11112 Rename days in transit to (cargo) periods in transit. + SLV_NEWGRF_LAST_SERVICE, ///< 317 PR#11124 Added stable date_of_last_service to avoid NewGRF trouble. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index 43c2240d22..c4840bdf9a 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -675,6 +675,7 @@ public: SLE_CONDVAR(Vehicle, max_age, SLE_INT32, SLV_31, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31), SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, SLV_31, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, date_of_last_service_newgrf, SLE_INT32, SLV_NEWGRF_LAST_SERVICE, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SL_MIN_VERSION, SLV_31), SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SLV_31, SLV_180), SLE_CONDVAR(Vehicle, service_interval, SLE_UINT16, SLV_180, SL_MAX_VERSION), diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index fc44f6f76c..48620a2502 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -903,6 +903,7 @@ CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, V v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_ships); v->date_of_last_service = TimerGameCalendar::date; + v->date_of_last_service_newgrf = TimerGameCalendar::date; v->build_year = TimerGameCalendar::year; v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = Random(); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 628675a635..dde3b329a8 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -654,6 +654,7 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const v->railtype = rvi->railtype; v->date_of_last_service = TimerGameCalendar::date; + v->date_of_last_service_newgrf = TimerGameCalendar::date; v->build_year = TimerGameCalendar::year; v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = Random(); @@ -719,6 +720,7 @@ static void AddRearEngineToMultiheadedTrain(Train *v) u->railtype = v->railtype; u->engine_type = v->engine_type; u->date_of_last_service = v->date_of_last_service; + u->date_of_last_service_newgrf = v->date_of_last_service_newgrf; u->build_year = v->build_year; u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY); u->random_bits = Random(); @@ -783,6 +785,7 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains); v->date_of_last_service = TimerGameCalendar::date; + v->date_of_last_service_newgrf = TimerGameCalendar::date; v->build_year = TimerGameCalendar::year; v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = Random(); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 1efdb30ba5..5e86e3c347 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -170,6 +170,7 @@ void VehicleServiceInDepot(Vehicle *v) do { v->date_of_last_service = TimerGameCalendar::date; + v->date_of_last_service_newgrf = TimerGameCalendar::date; v->breakdowns_since_last_service = 0; v->reliability = v->GetEngine()->reliability; /* Prevent vehicles from breaking down directly after exiting the depot. */ @@ -761,6 +762,8 @@ uint32_t Vehicle::GetGRFID() const void Vehicle::ShiftDates(int interval) { this->date_of_last_service = std::max(this->date_of_last_service + interval, 0); + /* date_of_last_service_newgrf is not updated here as it must stay stable + * for vehicles outside of a depot. */ } /** diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 093f4b5c2e..5ceb992690 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -288,6 +288,7 @@ public: TimerGameCalendar::Date age; ///< Age in days TimerGameCalendar::Date max_age; ///< Maximum age TimerGameCalendar::Date date_of_last_service; ///< Last date the vehicle had a service at a depot. + TimerGameCalendar::Date date_of_last_service_newgrf; ///< Last date the vehicle had a service at a depot, unchanged by the date cheat to protect against unsafe NewGRF behavior. uint16_t reliability; ///< Reliability. uint16_t reliability_spd_dec; ///< Reliability decrease speed. byte breakdown_ctr; ///< Counter for managing breakdown events. @see Vehicle::HandleBreakdown