Feature: Engine expiry setting to keep owned engines available for purchase

This commit is contained in:
Tyler Trahan 2024-04-02 16:54:04 -04:00
parent 4df44fea38
commit 2a17900f7d
8 changed files with 70 additions and 5 deletions

View File

@ -24,6 +24,7 @@
#include "engine_gui.h"
#include "engine_func.h"
#include "engine_base.h"
#include "engine_type.h"
#include "company_base.h"
#include "vehicle_func.h"
#include "articulated_vehicles.h"
@ -604,6 +605,32 @@ static void ClearLastVariant(EngineID engine_id, VehicleType type)
}
}
/**
* Is a given engine allowed to expire?
* @note This does not age or retire the engine, just check if expiry is allowed.
* @param e The engine in question.
* @return True iff the engine can be checked for retirement.
*/
static bool AllowEngineExpiry(Engine *e)
{
/* The vehicle is set to never expire. */
if (e->info.base_life == 0xFF) return false;
/* The player will not allow any vehicle to expire. */
if (_settings_game.vehicle.engine_expiry == EngineExpiryMode::Never) return false;
/* The player wants to keep buying vehicles someone already owns. */
if (_settings_game.vehicle.engine_expiry == EngineExpiryMode::Owned) {
for (const Company *c : Company::Iterate()) {
if (c == nullptr) continue;
if (GetGroupNumEngines(c->index, ALL_GROUP, e->index) > 0) return false;
}
}
/* Vehicles expire normally. */
return true;
}
/**
* Update #Engine::reliability and (if needed) update the engine GUIs.
* @param e %Engine to update.
@ -620,7 +647,7 @@ void CalcEngineReliability(Engine *e, bool new_month)
if (new_month && re->index > e->index && age != INT32_MAX) age++; /* parent variant's age has not yet updated. */
/* Check for early retirement */
if (e->company_avail != 0 && !_settings_game.vehicle.never_expire_vehicles && e->info.base_life != 0xFF) {
if (e->company_avail != 0 && AllowEngineExpiry(e)) {
int retire_early = e->info.retire_early;
uint retire_early_max_age = std::max(0, e->duration_phase_1 + e->duration_phase_2 - retire_early * 12);
if (retire_early != 0 && age >= retire_early_max_age) {
@ -634,7 +661,7 @@ void CalcEngineReliability(Engine *e, bool new_month)
if (age < e->duration_phase_1) {
uint start = e->reliability_start;
e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
} else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _settings_game.vehicle.never_expire_vehicles || e->info.base_life == 0xFF) {
} else if ((age -= e->duration_phase_1) < e->duration_phase_2 || !AllowEngineExpiry(e)) {
/* We are at the peak of this engines life. It will have max reliability.
* This is also true if the engines never expire. They will not go bad over time */
e->reliability = e->reliability_max;

View File

@ -195,6 +195,15 @@ enum EngineNameContext : uint8_t {
AutoreplaceVehicleInUse = 0x22, ///< Name is show in the autoreplace window 'Vehicles in use' panel.
};
/**
* Possible values of the "engine_expiry" setting.
*/
enum EngineExpiryMode : uint8_t {
Off = 0, ///< Engines expire normally.
Never = 1, ///< Engines never expire.
Owned = 2, ///< Engines which are currently owned by any company never expire. Unowned vehicles expire normally.
};
/** Combine an engine ID and a name context to an engine name dparam. */
inline uint64_t PackEngineNameDParam(EngineID engine_id, EngineNameContext context, uint32_t extra_data = 0)
{

View File

@ -1492,7 +1492,12 @@ STR_CONFIG_SETTING_WARN_INCOME_LESS_HELPTEXT :When enabled, a
STR_CONFIG_SETTING_WARN_INCOME_LESS_HELPTEXT_PERIOD :When enabled, a news message gets sent when a vehicle has not made any profit within a period
STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES :Vehicles never expire: {STRING2}
STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_HELPTEXT :When enabled, all vehicle models remain available forever after their introduction
STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_HELPTEXT :Choose if vehicle models remain available forever after their introduction
###length 3
STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_OFF :Off
STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_NEVER :All available forever
STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_OWNED :Owned vehicles available forever
STR_CONFIG_SETTING_TIMEKEEPING_UNITS :Timekeeping: {STRING2}
STR_CONFIG_SETTING_TIMEKEEPING_UNITS_HELPTEXT :Select the timekeeping units of the game. This cannot be changed later.{}{}Calendar-based is the classic OpenTTD experience, with a year consisting of 12 months, and each month having 28-31 days.{}{}In Wallclock-based time, cargo production and financials are instead based on one-minute increments, which is about as long as a 30 day month takes in Calendar-based mode. These are grouped into 12-minute periods, equivalent to a year in Calendar-based mode.{}{}In either mode there is always a classic calendar, which is used for introduction dates of vehicles, houses, and other infrastructure

View File

@ -795,6 +795,11 @@ bool AfterLoadGame()
_settings_game.linkgraph.recalc_time *= CalendarTime::SECONDS_PER_DAY;
}
/* Convert old engine expiry setting. */
if (IsSavegameVersionBefore(SLV_ENGINE_EXPIRY_OWNED)) {
_settings_game.vehicle.engine_expiry = _settings_game.vehicle.never_expire_vehicles ? EngineExpiryMode::Never : EngineExpiryMode::Off;
}
/* Load the sprites */
GfxLoadSprites();
LoadStringWidthTable();

View File

@ -379,6 +379,8 @@ enum SaveLoadVersion : uint16_t {
SLV_SCRIPT_RANDOMIZER, ///< 333 PR#12063 v14.0-RC1 Save script randomizers.
SLV_VEHICLE_ECONOMY_AGE, ///< 334 PR#12141 v14.0 Add vehicle age in economy year, for profit stats minimum age
SLV_ENGINE_EXPIRY_OWNED, ///< 335 PR#12598 Add engine expiry mode to keep owned engines available for purchase.
SL_MAX_VERSION, ///< Highest possible saveload version
};

View File

@ -2144,7 +2144,7 @@ static SettingsContainer &GetSettingsTree()
limitations->Add(new SettingEntry("construction.max_bridge_height"));
limitations->Add(new SettingEntry("construction.max_tunnel_length"));
limitations->Add(new SettingEntry("station.never_expire_airports"));
limitations->Add(new SettingEntry("vehicle.never_expire_vehicles"));
limitations->Add(new SettingEntry("vehicle.engine_expiry"));
limitations->Add(new SettingEntry("vehicle.max_trains"));
limitations->Add(new SettingEntry("vehicle.max_roadveh"));
limitations->Add(new SettingEntry("vehicle.max_aircraft"));

View File

@ -14,6 +14,7 @@
#include "economy_type.h"
#include "town_type.h"
#include "transport_type.h"
#include "engine_type.h"
#include "network/network_type.h"
#include "company_type.h"
#include "cargotype.h"
@ -516,6 +517,8 @@ struct OrderSettings {
/** Settings related to vehicles. */
struct VehicleSettings {
bool never_expire_vehicles; ///< Unused value, used to load old savegames.
uint8_t max_train_length; ///< maximum length for trains
uint8_t smoke_amount; ///< amount of smoke/sparks locomotives produce
uint8_t train_acceleration_model; ///< realistic acceleration for trains
@ -531,7 +534,7 @@ struct VehicleSettings {
uint8_t plane_speed; ///< divisor for speed of aircraft
uint8_t freight_trains; ///< value to multiply the weight of cargo by
bool dynamic_engines; ///< enable dynamic allocation of engine data
bool never_expire_vehicles; ///< never expire vehicles
EngineExpiryMode engine_expiry; ///< engine expiry
uint8_t extend_vehicle_life; ///< extend vehicle life by this many years
uint8_t road_side; ///< the side of the road vehicles drive on
uint8_t plane_crashes; ///< number of plane crashes, 0 = none, 1 = reduced, 2 = normal

View File

@ -10,6 +10,7 @@
[pre-amble]
static constexpr std::initializer_list<const char*> _roadsides{"left", "right"};
static constexpr std::initializer_list<const char*> _engine_expiry_modes{"off", "never", "owned"};
static void StationSpreadChanged(int32_t new_value);
static void UpdateConsists(int32_t new_value);
@ -243,8 +244,21 @@ strval = STR_CONFIG_SETTING_NONE
var = vehicle.never_expire_vehicles
flags = SF_NO_NETWORK
def = false
to = SLV_ENGINE_EXPIRY_OWNED
[SDT_OMANY]
var = vehicle.engine_expiry
type = SLE_UINT8
from = SLV_ENGINE_EXPIRY_OWNED
flags = SF_GUI_DROPDOWN | SF_NO_NETWORK
def = EngineExpiryMode::Off
min = EngineExpiryMode::Off
max = EngineExpiryMode::Owned
full = _engine_expiry_modes
str = STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES
strhelp = STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_HELPTEXT
strval = STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_OFF
cat = SC_BASIC
[SDT_VAR]
var = vehicle.max_trains