diff --git a/src/engine.cpp b/src/engine.cpp index 6c75ee3e74..abcf4f9d7a 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -578,9 +578,16 @@ static void ClearLastVariant(EngineID engine_id, VehicleType type) * Update #Engine::reliability and (if needed) update the engine GUIs. * @param e %Engine to update. */ -static void CalcEngineReliability(Engine *e) +void CalcEngineReliability(Engine *e, bool new_month) { - uint age = e->age; + /* Get source engine for reliability age. This is normally our engine unless variant reliability syncing is requested. */ + Engine *re = e; + while (re->info.variant_id != INVALID_ENGINE && re->info.variant_id != re->index && (re->info.extra_flags & ExtraEngineFlags::SyncReliability) != ExtraEngineFlags::None) { + re = Engine::Get(re->info.variant_id); + } + + uint age = re->age; + if (new_month && re->index > e->index && age != MAX_DAY) 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) { @@ -671,7 +678,16 @@ void StartupOneEngine(Engine *e, Date aging_date) e->flags |= ENGINE_AVAILABLE; } - RestoreRandomSeeds(saved_seeds); + /* Get parent variant index for syncing reliability via random seed. */ + const Engine *re = e; + while (re->info.variant_id != INVALID_ENGINE && re->info.variant_id != re->index && (re->info.extra_flags & ExtraEngineFlags::SyncReliability) != ExtraEngineFlags::None) { + re = Engine::Get(re->info.variant_id); + } + + SetRandomSeed(_settings_game.game_creation.generation_seed ^ + re->index ^ + e->type ^ + e->GetGRFID()); r = Random(); e->reliability_start = GB(r, 16, 14) + 0x7AE0; @@ -683,9 +699,9 @@ void StartupOneEngine(Engine *e, Date aging_date) e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96; e->duration_phase_3 = GB(r, 9, 7) + 120; - e->reliability_spd_dec = ei->decay_speed << 2; + RestoreRandomSeeds(saved_seeds); - CalcEngineReliability(e); + e->reliability_spd_dec = ei->decay_speed << 2; /* prevent certain engines from ever appearing. */ if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) { @@ -706,6 +722,9 @@ void StartupEngines() for (Engine *e : Engine::Iterate()) { StartupOneEngine(e, aging_date); } + for (Engine *e : Engine::Iterate()) { + CalcEngineReliability(e, false); + } /* Update the bitmasks for the vehicle lists */ for (Company *c : Company::Iterate()) { @@ -775,8 +794,9 @@ static void DisableEngineForCompany(EngineID eid, CompanyID company) * Company \a company accepts engine \a eid for preview. * @param eid Engine being accepted (is under preview). * @param company Current company previewing the engine. + * @param recursion_depth Recursion depth to avoid infinite loop. */ -static void AcceptEnginePreview(EngineID eid, CompanyID company) +static void AcceptEnginePreview(EngineID eid, CompanyID company, int recursion_depth = 0) { Engine *e = Engine::Get(eid); @@ -791,6 +811,16 @@ static void AcceptEnginePreview(EngineID eid, CompanyID company) * we have to use the GUI-scope scheduling of InvalidateWindowData. */ InvalidateWindowData(WC_ENGINE_PREVIEW, eid); + + /* Don't search for variants to include if we are 10 levels deep already. */ + if (recursion_depth >= 10) return; + + /* Find variants to be included in preview. */ + for (Engine *ve : Engine::IterateType(e->type)) { + if (ve->index != eid && ve->info.variant_id == eid && (ve->info.extra_flags & ExtraEngineFlags::JoinPreview) != ExtraEngineFlags::None) { + AcceptEnginePreview(ve->index, company, recursion_depth + 1); + } + } } /** @@ -1014,7 +1044,7 @@ static void NewVehicleAvailable(Engine *e) if (!IsVehicleTypeDisabled(e->type, true)) AI::BroadcastNewEvent(new ScriptEventEngineAvailable(index)); /* Only provide the "New Vehicle available" news paper entry, if engine can be built. */ - if (!IsVehicleTypeDisabled(e->type, false)) { + if (!IsVehicleTypeDisabled(e->type, false) && (e->info.extra_flags & ExtraEngineFlags::NoNews) == ExtraEngineFlags::None) { SetDParam(0, GetEngineCategoryName(index)); SetDParam(1, index); AddNewsItem(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NT_NEW_VEHICLES, NF_VEHICLE, NR_ENGINE, index); @@ -1038,7 +1068,7 @@ void EnginesMonthlyLoop() /* Age the vehicle */ if ((e->flags & ENGINE_AVAILABLE) && e->age != MAX_DAY) { e->age++; - CalcEngineReliability(e); + CalcEngineReliability(e, true); refresh = true; } @@ -1058,6 +1088,9 @@ void EnginesMonthlyLoop() /* Do not introduce new rail wagons */ if (IsWagon(e->index)) continue; + /* Engine has no preview */ + if ((e->info.extra_flags & ExtraEngineFlags::NoPreview) != ExtraEngineFlags::None) continue; + /* Show preview dialog to one of the companies. */ e->flags |= ENGINE_EXCLUSIVE_PREVIEW; e->preview_company = INVALID_COMPANY; diff --git a/src/engine_func.h b/src/engine_func.h index 97bfd0894b..f7d7dad9f8 100644 --- a/src/engine_func.h +++ b/src/engine_func.h @@ -26,6 +26,7 @@ bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company); bool IsEngineRefittable(EngineID engine); void GetArticulatedVehicleCargoesAndRefits(EngineID engine, CargoArray *cargoes, CargoTypes *refits, CargoID cargo_type, uint cargo_capacity); void SetYearEngineAgingStops(); +void CalcEngineReliability(Engine *e, bool new_month); void StartupOneEngine(Engine *e, Date aging_date); uint GetTotalCapacityOfArticulatedParts(EngineID engine); diff --git a/src/engine_type.h b/src/engine_type.h index 9cee244f12..eeccf3e2b8 100644 --- a/src/engine_type.h +++ b/src/engine_type.h @@ -126,6 +126,15 @@ struct RoadVehicleInfo { RoadType roadtype; ///< Road type }; +enum class ExtraEngineFlags : uint32 { + None = 0, + NoNews = (1U << 0), ///< No 'new vehicle' news will be generated. + NoPreview = (1U << 1), ///< No exclusive preview will be offered. + JoinPreview = (1U << 2), ///< Engine will join exclusive preview with variant parent. + SyncReliability = (1U << 3), ///< Engine reliability will be synced with variant parent. +}; +DECLARE_ENUM_AS_BIT_SET(ExtraEngineFlags); + /** * Information about a vehicle * @see table/engines.h @@ -146,6 +155,7 @@ struct EngineInfo { StringID string_id; ///< Default name of engine uint16 cargo_age_period; ///< Number of ticks before carried cargo is aged. EngineID variant_id; ///< Engine variant ID. If set, will be treated specially in purchase lists. + ExtraEngineFlags extra_flags; }; /** diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 71f7abbd25..2d9dc1078f 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -1338,6 +1338,10 @@ static ChangeInfoResult RailVehicleChangeInfo(uint engine, int numinfo, int prop ei->variant_id = GetNewEngineID(_cur.grffile, VEH_TRAIN, buf->ReadWord()); break; + case 0x30: // Extra miscellaneous flags + ei->extra_flags = static_cast(buf->ReadDWord()); + break; + default: ret = CommonVehicleChangeInfo(ei, prop, buf); break; @@ -1536,6 +1540,10 @@ static ChangeInfoResult RoadVehicleChangeInfo(uint engine, int numinfo, int prop ei->variant_id = GetNewEngineID(_cur.grffile, VEH_ROAD, buf->ReadWord()); break; + case 0x27: // Extra miscellaneous flags + ei->extra_flags = static_cast(buf->ReadDWord()); + break; + default: ret = CommonVehicleChangeInfo(ei, prop, buf); break; @@ -1712,6 +1720,10 @@ static ChangeInfoResult ShipVehicleChangeInfo(uint engine, int numinfo, int prop ei->variant_id = GetNewEngineID(_cur.grffile, VEH_SHIP, buf->ReadWord()); break; + case 0x21: // Extra miscellaneous flags + ei->extra_flags = static_cast(buf->ReadDWord()); + break; + default: ret = CommonVehicleChangeInfo(ei, prop, buf); break; @@ -1870,6 +1882,10 @@ static ChangeInfoResult AircraftVehicleChangeInfo(uint engine, int numinfo, int ei->variant_id = GetNewEngineID(_cur.grffile, VEH_AIRCRAFT, buf->ReadWord()); break; + case 0x21: // Extra miscellaneous flags + ei->extra_flags = static_cast(buf->ReadDWord()); + break; + default: ret = CommonVehicleChangeInfo(ei, prop, buf); break; diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index a419e4b344..74a2996601 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -402,6 +402,7 @@ static bool FixTTOEngines() /* Default engine is used */ _date += DAYS_TILL_ORIGINAL_BASE_YEAR; StartupOneEngine(e, aging_date); + CalcEngineReliability(e, false); e->intro_date -= DAYS_TILL_ORIGINAL_BASE_YEAR; _date -= DAYS_TILL_ORIGINAL_BASE_YEAR; diff --git a/src/table/engines.h b/src/table/engines.h index 6d45bfb02f..e3b1481d7f 100644 --- a/src/table/engines.h +++ b/src/table/engines.h @@ -24,7 +24,7 @@ * @param f Bitmask of the climates * @note the 5 between b and f is the load amount */ -#define MT(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE } +#define MT(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of a multiple-unit train into the EngineInfo struct. @@ -37,7 +37,7 @@ * @param f Bitmask of the climates * @note the 5 between b and f is the load amount */ -#define MM(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 1 << EF_RAIL_IS_MU, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE } +#define MM(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 1 << EF_RAIL_IS_MU, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of a train carriage into the EngineInfo struct. @@ -50,7 +50,7 @@ * @see MT * @note the 5 between b and f is the load amount */ -#define MW(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE } +#define MW(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of a road vehicle into the EngineInfo struct. @@ -63,7 +63,7 @@ * @param f Bitmask of the climates * @note the 5 between b and f is the load amount */ -#define MR(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE } +#define MR(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of a ship into the EngineInfo struct. @@ -75,7 +75,7 @@ * @param f Bitmask of the climates * @note the 10 between b and f is the load amount */ -#define MS(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 10, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE } +#define MS(a, b, c, d, e, f) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 10, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of an aeroplane into the EngineInfo struct. @@ -86,7 +86,7 @@ * @param e Bitmask of the climates * @note the 20 between b and e is the load amount */ -#define MA(a, b, c, d, e) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 20, e, CT_INVALID, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE } +#define MA(a, b, c, d, e) { DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 20, e, CT_INVALID, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /* Climates * T = Temperate