From e69b3a344604fdcfd7aec8159572a7b96d24070c Mon Sep 17 00:00:00 2001 From: Delnar_Ersike Date: Wed, 13 Mar 2024 06:09:26 +0100 Subject: [PATCH] Better & More Robust Handling of Vehicle Mod Ratings --- Chummer/Backend/Equipment/Vehicle.cs | 488 ++++++++++-------- Chummer/Backend/Equipment/VehicleMod.cs | 206 ++++++-- .../Forms/Character Forms/CharacterCareer.cs | 116 +---- .../Forms/Character Forms/CharacterCreate.cs | 142 +---- .../Forms/Creation Forms/CreatePACKSKit.cs | 5 +- 5 files changed, 494 insertions(+), 463 deletions(-) diff --git a/Chummer/Backend/Equipment/Vehicle.cs b/Chummer/Backend/Equipment/Vehicle.cs index b677103b8b..81251ac8a2 100644 --- a/Chummer/Backend/Equipment/Vehicle.cs +++ b/Chummer/Backend/Equipment/Vehicle.cs @@ -1608,7 +1608,7 @@ public async Task GetPilotAsync(CancellationToken token = default) ? objMod.WirelessBonus?["pilot"]?.InnerText ?? objMod.Bonus?["pilot"]?.InnerText : objMod.Bonus?["pilot"]?.InnerText; intReturn = Math.Max( - await ParseBonusAsync(strBonusPilot, objMod.Rating, _intPilot, "Pilot", false, token) + await ParseBonusAsync(strBonusPilot, await objMod.GetRatingAsync(token).ConfigureAwait(false), _intPilot, "Pilot", false, token) .ConfigureAwait(false), intReturn); } @@ -2112,13 +2112,13 @@ public async Task GetCalculatedSensorAsync(CancellationToken token = defaul string strLoop = objMod.Bonus?["sensor"]?.InnerText; intTotalSensor = Math.Max(intTotalSensor, - await ParseBonusAsync(strLoop, objMod.Rating, intTotalSensor, "Sensor", false, token) + await ParseBonusAsync(strLoop, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalSensor, "Sensor", false, token) .ConfigureAwait(false)); if (!objMod.WirelessOn || objMod.WirelessBonus == null) return; strLoop = objMod.WirelessBonus?["sensor"]?.InnerText; intTotalSensor = Math.Max(intTotalSensor, - await ParseBonusAsync(strLoop, objMod.Rating, intTotalSensor, "Sensor", false, token) + await ParseBonusAsync(strLoop, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalSensor, "Sensor", false, token) .ConfigureAwait(false)); }, token).ConfigureAwait(false); @@ -2129,12 +2129,12 @@ await ParseBonusAsync(strLoop, objMod.Rating, intTotalSensor, "Sensor", false, t return 0; string strLoop = objMod.Bonus?["sensor"]?.InnerText; - int intTemp = await ParseBonusAsync(strLoop, objMod.Rating, intTotalSensor, "Sensor", token: token) + int intTemp = await ParseBonusAsync(strLoop, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalSensor, "Sensor", token: token) .ConfigureAwait(false); if (objMod.WirelessOn && objMod.WirelessBonus != null) { strLoop = objMod.WirelessBonus?["sensor"]?.InnerText; - intTemp += await ParseBonusAsync(strLoop, objMod.Rating, intTotalSensor, "Sensor", token: token) + intTemp += await ParseBonusAsync(strLoop, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalSensor, "Sensor", token: token) .ConfigureAwait(false); } @@ -2836,51 +2836,63 @@ public async Task GetOwnCostAsync(CancellationToken token = default) /// /// Total Seats of the Vehicle including Modifications. /// - public int TotalSeats + public int TotalSeats => GetTotalSeats(); + + /// + /// Total Seats of the Vehicle including Modifications. + /// + public int GetTotalSeats(VehicleMod objExcludeMod = null) { - get + using (_objCharacter.LockObject.EnterReadLock()) { - using (_objCharacter.LockObject.EnterReadLock()) + // First check for mods that overwrite the seat value + int intTotalSeats = Seats; + foreach (VehicleMod objMod in Mods) { - // First check for mods that overwrite the seat value - int intTotalSeats = Seats; - foreach (VehicleMod objMod in Mods) - { - if (objMod.IncludedInVehicle || !objMod.Equipped) - continue; + if (objMod.IncludedInVehicle || !objMod.Equipped || objMod == objExcludeMod) + continue; - string strBonusSeats = objMod.WirelessOn - ? objMod.WirelessBonus?["seats"]?.InnerText ?? objMod.Bonus?["seats"]?.InnerText - : objMod.Bonus?["seats"]?.InnerText; + string strBonusSeats = objMod.WirelessOn + ? objMod.WirelessBonus?["seats"]?.InnerText ?? objMod.Bonus?["seats"]?.InnerText + : objMod.Bonus?["seats"]?.InnerText; + if (!string.IsNullOrEmpty(strBonusSeats)) intTotalSeats = Math.Max(ParseBonus(strBonusSeats, objMod.Rating, Seats, "Seats", false), intTotalSeats); - } + } - // Then check for mods that modify the seat value (needs separate loop in case of % modifiers on top of stat-overriding mods) - int intTotalBonusSeats = 0; - foreach (VehicleMod objMod in Mods) - { - if (objMod.IncludedInVehicle || !objMod.Equipped) - continue; - intTotalBonusSeats += ParseBonus(objMod.Bonus?["seats"]?.InnerText, objMod.Rating, + // Then check for mods that modify the seat value (needs separate loop in case of % modifiers on top of stat-overriding mods) + int intTotalBonusSeats = 0; + foreach (VehicleMod objMod in Mods) + { + if (objMod.IncludedInVehicle || !objMod.Equipped || objMod == objExcludeMod) + continue; + string strText = objMod.Bonus?["seats"]?.InnerText; + if (!string.IsNullOrEmpty(strText)) + intTotalBonusSeats += ParseBonus(strText, objMod.Rating, intTotalSeats, "Seats"); - if (objMod.WirelessOn && objMod.WirelessBonus != null) - { - intTotalBonusSeats += ParseBonus(objMod.WirelessBonus?["seats"]?.InnerText, objMod.Rating, + if (objMod.WirelessOn && objMod.WirelessBonus != null) + { + strText = objMod.WirelessBonus?["seats"]?.InnerText; + if (!string.IsNullOrEmpty(strText)) + intTotalBonusSeats += ParseBonus(strText, objMod.Rating, intTotalSeats, "Seats"); - } } - - return intTotalSeats + intTotalBonusSeats; } + + return intTotalSeats + intTotalBonusSeats; } } /// /// Total Seats of the Vehicle including Modifications. /// - public async Task GetTotalSeatsAsync(CancellationToken token = default) + public Task GetTotalSeatsAsync(CancellationToken token = default) => GetTotalSeatsAsync(null, token); + + /// + /// Total Seats of the Vehicle including Modifications. + /// + public async Task GetTotalSeatsAsync(VehicleMod objExcludeMod, CancellationToken token = default) { token.ThrowIfCancellationRequested(); IAsyncDisposable objLocker = await _objCharacter.LockObject.EnterReadLockAsync(token).ConfigureAwait(false); @@ -2891,35 +2903,41 @@ public async Task GetTotalSeatsAsync(CancellationToken token = default) int intTotalSeats = Seats; await Mods.ForEachAsync(async objMod => { - if (objMod.IncludedInVehicle || !objMod.Equipped) + if (objMod.IncludedInVehicle || !objMod.Equipped || objMod == objExcludeMod) return; string strBonusSeats = objMod.WirelessOn ? objMod.WirelessBonus?["seats"]?.InnerText ?? objMod.Bonus?["seats"]?.InnerText : objMod.Bonus?["seats"]?.InnerText; - intTotalSeats = - Math.Max( - await ParseBonusAsync(strBonusSeats, objMod.Rating, Seats, "Seats", false, token) - .ConfigureAwait(false), - intTotalSeats); + if (!string.IsNullOrEmpty(strBonusSeats)) + intTotalSeats = + Math.Max( + await ParseBonusAsync(strBonusSeats, await objMod.GetRatingAsync(token).ConfigureAwait(false), Seats, "Seats", + false, token) + .ConfigureAwait(false), + intTotalSeats); }, token).ConfigureAwait(false); // Then check for mods that modify the seat value (needs separate loop in case of % modifiers on top of stat-overriding mods) - int intTotalBonusSeats = await Mods.SumAsync(async objMod => - { - if (objMod.IncludedInVehicle || !objMod.Equipped) - return 0; - int intTemp = await ParseBonusAsync(objMod.Bonus?["seats"]?.InnerText, objMod.Rating, intTotalSeats, - "Seats", token: token).ConfigureAwait(false); - - if (objMod.WirelessOn && objMod.WirelessBonus != null) + int intTotalBonusSeats = await Mods.SumAsync( + objMod => !objMod.IncludedInVehicle && objMod.Equipped && objMod != objExcludeMod, async objMod => { - intTemp += await ParseBonusAsync(objMod.WirelessBonus?["seats"]?.InnerText, objMod.Rating, - intTotalSeats, "Seats", token: token).ConfigureAwait(false); - } + int intTemp = 0; + string strText = objMod.Bonus?["seats"]?.InnerText; + if (!string.IsNullOrEmpty(strText)) + intTemp += await ParseBonusAsync(strText, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalSeats, + "Seats", token: token).ConfigureAwait(false); - return intTemp; - }, token).ConfigureAwait(false); + if (objMod.WirelessOn && objMod.WirelessBonus != null) + { + strText = objMod.WirelessBonus?["seats"]?.InnerText; + if (!string.IsNullOrEmpty(strText)) + intTemp += await ParseBonusAsync(strText, await objMod.GetRatingAsync(token).ConfigureAwait(false), + intTotalSeats, "Seats", token: token).ConfigureAwait(false); + } + + return intTemp; + }, token).ConfigureAwait(false); return intTotalSeats + intTotalBonusSeats; } @@ -2952,27 +2970,32 @@ public string TotalSpeed string strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["speed"]?.InnerText ?? objMod.Bonus?["speed"]?.InnerText : objMod.Bonus?["speed"]?.InnerText; - intTotalSpeed = Math.Max(ParseBonus(strBonus, objMod.Rating, Speed, "Speed", false), - intTotalSpeed); + if (!string.IsNullOrEmpty(strBonus)) + intTotalSpeed = Math.Max(ParseBonus(strBonus, objMod.Rating, Speed, "Speed", false), + intTotalSpeed); strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["offroadspeed"]?.InnerText ?? objMod.Bonus?["offroadspeed"]?.InnerText : objMod.Bonus?["offroadspeed"]?.InnerText; - intBaseOffroadSpeed = - Math.Max(ParseBonus(strBonus, objMod.Rating, OffroadSpeed, "OffroadSpeed", false), - intTotalSpeed); + if (!string.IsNullOrEmpty(strBonus)) + intBaseOffroadSpeed = + Math.Max(ParseBonus(strBonus, objMod.Rating, OffroadSpeed, "OffroadSpeed", false), + intTotalSpeed); if (IsDrone && _objCharacter.Settings.DroneMods) { strBonus = objMod.Bonus?["armor"]?.InnerText; - intTotalArmor = Math.Max(ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), - intTotalArmor); + if (!string.IsNullOrEmpty(strBonus)) + intTotalArmor = Math.Max( + ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), + intTotalArmor); if (objMod.WirelessOn && objMod.WirelessBonus != null) { strBonus = objMod.WirelessBonus["armor"]?.InnerText; - intTotalArmor = - Math.Max(ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), - intTotalArmor); + if (!string.IsNullOrEmpty(strBonus)) + intTotalArmor = + Math.Max(ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), + intTotalArmor); } } } @@ -3047,35 +3070,43 @@ public async Task GetTotalSpeedAsync(CancellationToken token = default) string strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["speed"]?.InnerText ?? objMod.Bonus?["speed"]?.InnerText : objMod.Bonus?["speed"]?.InnerText; - intTotalSpeed = Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, Speed, "Speed", false, token) - .ConfigureAwait(false), - intTotalSpeed); + if (!string.IsNullOrEmpty(strBonus)) + intTotalSpeed = Math.Max( + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), Speed, "Speed", false, + token) + .ConfigureAwait(false), + intTotalSpeed); strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["offroadspeed"]?.InnerText ?? objMod.Bonus?["offroadspeed"]?.InnerText : objMod.Bonus?["offroadspeed"]?.InnerText; - intBaseOffroadSpeed = - Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, OffroadSpeed, "OffroadSpeed", false, token) - .ConfigureAwait(false), - intTotalSpeed); + if (!string.IsNullOrEmpty(strBonus)) + intBaseOffroadSpeed = + Math.Max( + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), OffroadSpeed, + "OffroadSpeed", false, token) + .ConfigureAwait(false), + intTotalSpeed); if (IsDrone && _objCharacter.Settings.DroneMods) { strBonus = objMod.Bonus?["armor"]?.InnerText; - intTotalArmor = - Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, token) - .ConfigureAwait(false), - intTotalArmor); - if (objMod.WirelessOn && objMod.WirelessBonus != null) - { - strBonus = objMod.WirelessBonus["armor"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) intTotalArmor = Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, token) + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, + "Armor", false, token) .ConfigureAwait(false), intTotalArmor); + if (objMod.WirelessOn && objMod.WirelessBonus != null) + { + strBonus = objMod.WirelessBonus["armor"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) + intTotalArmor = + Math.Max( + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), + intTotalArmor, "Armor", false, token) + .ConfigureAwait(false), + intTotalArmor); } } }, token).ConfigureAwait(false); @@ -3090,25 +3121,30 @@ await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, to int intTemp = 0; if (objMod.Bonus != null) { - intTotalBonusSpeed += await ParseBonusAsync(objMod.Bonus["speed"]?.InnerText, objMod.Rating, + intTotalBonusSpeed += await ParseBonusAsync(objMod.Bonus["speed"]?.InnerText, + await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalSpeed, "Speed", token: token).ConfigureAwait(false); intTotalBonusOffroadSpeed += await ParseBonusAsync(objMod.Bonus["offroadspeed"]?.InnerText, - objMod.Rating, intTotalSpeed, "OffroadSpeed", token: token).ConfigureAwait(false); - if (IsDrone && _objCharacter.Settings.DroneMods) - intTemp += await ParseBonusAsync(objMod.Bonus["armor"]?.InnerText, objMod.Rating, + await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalSpeed, "OffroadSpeed", token: token) + .ConfigureAwait(false); + if (IsDrone && await _objCharacter.Settings.GetDroneModsAsync(token).ConfigureAwait(false)) + intTemp += await ParseBonusAsync(objMod.Bonus["armor"]?.InnerText, + await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", token: token).ConfigureAwait(false); } if (objMod.WirelessOn && objMod.WirelessBonus != null) { intTotalBonusSpeed += await ParseBonusAsync(objMod.WirelessBonus["speed"]?.InnerText, - objMod.Rating, + await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalSpeed, "Speed", token: token).ConfigureAwait(false); intTotalBonusOffroadSpeed += await ParseBonusAsync( - objMod.WirelessBonus["offroadspeed"]?.InnerText, - objMod.Rating, intTotalSpeed, "OffroadSpeed", token: token).ConfigureAwait(false); - if (IsDrone && _objCharacter.Settings.DroneMods) - intTemp += await ParseBonusAsync(objMod.WirelessBonus["armor"]?.InnerText, objMod.Rating, + objMod.WirelessBonus["offroadspeed"]?.InnerText, + await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalSpeed, "OffroadSpeed", token: token) + .ConfigureAwait(false); + if (IsDrone && await _objCharacter.Settings.GetDroneModsAsync(token).ConfigureAwait(false)) + intTemp += await ParseBonusAsync(objMod.WirelessBonus["armor"]?.InnerText, + await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", token: token).ConfigureAwait(false); } @@ -3160,27 +3196,32 @@ public string TotalAccel string strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["accel"]?.InnerText ?? objMod.Bonus?["accel"]?.InnerText : objMod.Bonus?["accel"]?.InnerText; - intTotalAccel = Math.Max(ParseBonus(strBonus, objMod.Rating, Accel, "Accel", false), - intTotalAccel); + if (!string.IsNullOrEmpty(strBonus)) + intTotalAccel = Math.Max(ParseBonus(strBonus, objMod.Rating, Accel, "Accel", false), + intTotalAccel); strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["offroadaccel"]?.InnerText ?? objMod.Bonus?["offroadaccel"]?.InnerText : objMod.Bonus?["offroadaccel"]?.InnerText; - intBaseOffroadAccel = - Math.Max(ParseBonus(strBonus, objMod.Rating, OffroadAccel, "OffroadAccel", false), - intTotalAccel); + if (!string.IsNullOrEmpty(strBonus)) + intBaseOffroadAccel = + Math.Max(ParseBonus(strBonus, objMod.Rating, OffroadAccel, "OffroadAccel", false), + intTotalAccel); if (IsDrone && _objCharacter.Settings.DroneMods) { strBonus = objMod.Bonus?["armor"]?.InnerText; - intTotalArmor = Math.Max(ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), - intTotalArmor); + if (!string.IsNullOrEmpty(strBonus)) + intTotalArmor = Math.Max( + ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), + intTotalArmor); if (objMod.WirelessOn && objMod.WirelessBonus != null) { strBonus = objMod.WirelessBonus["armor"]?.InnerText; - intTotalArmor = - Math.Max(ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), - intTotalArmor); + if (!string.IsNullOrEmpty(strBonus)) + intTotalArmor = + Math.Max(ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), + intTotalArmor); } } } @@ -3254,35 +3295,41 @@ public async Task GetTotalAccelAsync(CancellationToken token = default) string strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["accel"]?.InnerText ?? objMod.Bonus?["accel"]?.InnerText : objMod.Bonus?["accel"]?.InnerText; - intTotalAccel = Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, Accel, "Accel", false, token) - .ConfigureAwait(false), - intTotalAccel); + if (!string.IsNullOrEmpty(strBonus)) + intTotalAccel = Math.Max( + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), Accel, "Accel", false, token) + .ConfigureAwait(false), + intTotalAccel); strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["offroadaccel"]?.InnerText ?? objMod.Bonus?["offroadaccel"]?.InnerText : objMod.Bonus?["offroadaccel"]?.InnerText; - intBaseOffroadAccel = - Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, OffroadAccel, "OffroadAccel", false, token) - .ConfigureAwait(false), - intTotalAccel); + if (!string.IsNullOrEmpty(strBonus)) + intBaseOffroadAccel = + Math.Max( + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), OffroadAccel, "OffroadAccel", false, + token) + .ConfigureAwait(false), + intTotalAccel); if (IsDrone && _objCharacter.Settings.DroneMods) { strBonus = objMod.Bonus?["armor"]?.InnerText; - intTotalArmor = - Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, token) - .ConfigureAwait(false), - intTotalArmor); - if (objMod.WirelessOn && objMod.WirelessBonus != null) - { - strBonus = objMod.WirelessBonus["armor"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) intTotalArmor = Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, token) + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", false, token) .ConfigureAwait(false), intTotalArmor); + if (objMod.WirelessOn && objMod.WirelessBonus != null) + { + strBonus = objMod.WirelessBonus["armor"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) + intTotalArmor = + Math.Max( + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", false, + token) + .ConfigureAwait(false), + intTotalArmor); } } }, token).ConfigureAwait(false); @@ -3297,25 +3344,25 @@ await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, to int intTemp = 0; if (objMod.Bonus != null) { - intTotalBonusAccel += await ParseBonusAsync(objMod.Bonus["accel"]?.InnerText, objMod.Rating, + intTotalBonusAccel += await ParseBonusAsync(objMod.Bonus["accel"]?.InnerText, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalAccel, "Accel", token: token).ConfigureAwait(false); intTotalBonusOffroadAccel += await ParseBonusAsync(objMod.Bonus["offroadaccel"]?.InnerText, - objMod.Rating, intTotalAccel, "OffroadAccel", token: token).ConfigureAwait(false); - if (IsDrone && _objCharacter.Settings.DroneMods) - intTemp += await ParseBonusAsync(objMod.Bonus["armor"]?.InnerText, objMod.Rating, + await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalAccel, "OffroadAccel", token: token).ConfigureAwait(false); + if (IsDrone && await _objCharacter.Settings.GetDroneModsAsync(token).ConfigureAwait(false)) + intTemp += await ParseBonusAsync(objMod.Bonus["armor"]?.InnerText, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", token: token).ConfigureAwait(false); } if (objMod.WirelessOn && objMod.WirelessBonus != null) { intTotalBonusAccel += await ParseBonusAsync(objMod.WirelessBonus["accel"]?.InnerText, - objMod.Rating, + await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalAccel, "Accel", token: token).ConfigureAwait(false); intTotalBonusOffroadAccel += await ParseBonusAsync( objMod.WirelessBonus["offroadaccel"]?.InnerText, - objMod.Rating, intTotalAccel, "OffroadAccel", token: token).ConfigureAwait(false); + await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalAccel, "OffroadAccel", token: token).ConfigureAwait(false); if (IsDrone && _objCharacter.Settings.DroneMods) - intTemp += await ParseBonusAsync(objMod.WirelessBonus["armor"]?.InnerText, objMod.Rating, + intTemp += await ParseBonusAsync(objMod.WirelessBonus["armor"]?.InnerText, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", token: token).ConfigureAwait(false); } @@ -3348,57 +3395,66 @@ await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, to /// /// Total Body of the Vehicle including Modifications. /// - public int TotalBody + public int TotalBody => GetTotalBody(); + + public int GetTotalBody(VehicleMod objExcludeMod = null) { - get + using (_objCharacter.LockObject.EnterReadLock()) { - using (_objCharacter.LockObject.EnterReadLock()) + return Body + Mods.Sum(x => !x.IncludedInVehicle && x.Equipped && x != objExcludeMod, objMod => { - int intBody = Body; - - foreach (VehicleMod objMod in Mods) + int intTemp = 0; + // Add the Modification's Body to the Vehicle's base Body. + string strBonus = objMod.Bonus?["body"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) + intTemp += ParseBonus(strBonus, objMod.Rating, Body, "Body"); + if (objMod.WirelessOn && objMod.WirelessBonus != null) { - if (objMod.IncludedInVehicle || !objMod.Equipped) - continue; - // Add the Modification's Body to the Vehicle's base Body. - intBody += ParseBonus(objMod.Bonus?["body"]?.InnerText, objMod.Rating, Body, "Body"); - if (objMod.WirelessOn && objMod.WirelessBonus != null) - { - intBody += ParseBonus(objMod.WirelessBonus?["body"]?.InnerText, objMod.Rating, Body, + strBonus = objMod.WirelessBonus?["body"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) + intTemp += ParseBonus(strBonus, objMod.Rating, Body, "Body"); - } } - return intBody; - } + return intTemp; + }); } } /// /// Total Body of the Vehicle including Modifications. /// - public async Task GetTotalBodyAsync(CancellationToken token = default) + public Task GetTotalBodyAsync(CancellationToken token = default) => GetTotalBodyAsync(null, token); + + /// + /// Total Body of the Vehicle including Modifications. + /// + public async Task GetTotalBodyAsync(VehicleMod objExcludeMod, CancellationToken token = default) { IAsyncDisposable objLocker = await _objCharacter.LockObject.EnterReadLockAsync(token).ConfigureAwait(false); try { token.ThrowIfCancellationRequested(); - int intBody = Body; - await Mods.ForEachAsync(async objMod => - { - if (!objMod.IncludedInVehicle && objMod.Equipped) + return Body + await Mods.SumAsync(x => !x.IncludedInVehicle && x.Equipped && x != objExcludeMod, + async objMod => { + int intTemp = 0; // Add the Modification's Body to the Vehicle's base Body. - intBody += await ParseBonusAsync(objMod.Bonus?["body"]?.InnerText, objMod.Rating, Body, "Body", - token: token).ConfigureAwait(false); + string strBonus = objMod.Bonus?["body"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) + intTemp += await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), Body, "Body", + token: token).ConfigureAwait(false); if (objMod.WirelessOn && objMod.WirelessBonus != null) { - intBody += await ParseBonusAsync(objMod.WirelessBonus?["body"]?.InnerText, objMod.Rating, - Body, "Body", token: token).ConfigureAwait(false); + strBonus = objMod.WirelessBonus?["body"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) + intTemp += await ParseBonusAsync(strBonus, + await objMod.GetRatingAsync(token).ConfigureAwait(false), + Body, "Body", token: token).ConfigureAwait(false); } - } - }, token).ConfigureAwait(false); - return intBody; + + return intTemp; + }, token).ConfigureAwait(false); } finally { @@ -3428,27 +3484,32 @@ public string TotalHandling string strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["handling"]?.InnerText ?? objMod.Bonus?["handling"]?.InnerText : objMod.Bonus?["handling"]?.InnerText; - intBaseHandling = Math.Max(ParseBonus(strBonus, objMod.Rating, Handling, "Handling", false), - intBaseHandling); + if (!string.IsNullOrEmpty(strBonus)) + intBaseHandling = Math.Max(ParseBonus(strBonus, objMod.Rating, Handling, "Handling", false), + intBaseHandling); strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["offroadhandling"]?.InnerText ?? objMod.Bonus?["offroadhandling"]?.InnerText : objMod.Bonus?["offroadhandling"]?.InnerText; - intBaseOffroadHandling = - Math.Max(ParseBonus(strBonus, objMod.Rating, OffroadHandling, "OffroadHandling", false), - intBaseOffroadHandling); + if (!string.IsNullOrEmpty(strBonus)) + intBaseOffroadHandling = + Math.Max(ParseBonus(strBonus, objMod.Rating, OffroadHandling, "OffroadHandling", false), + intBaseOffroadHandling); if (IsDrone && _objCharacter.Settings.DroneMods) { strBonus = objMod.Bonus?["armor"]?.InnerText; - intTotalArmor = Math.Max(ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), - intTotalArmor); + if (!string.IsNullOrEmpty(strBonus)) + intTotalArmor = Math.Max( + ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), + intTotalArmor); if (objMod.WirelessOn && objMod.WirelessBonus != null) { strBonus = objMod.WirelessBonus["armor"]?.InnerText; - intTotalArmor = - Math.Max(ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), - intTotalArmor); + if (!string.IsNullOrEmpty(strBonus)) + intTotalArmor = + Math.Max(ParseBonus(strBonus, objMod.Rating, intTotalArmor, "Armor", false), + intTotalArmor); } } } @@ -3525,36 +3586,42 @@ public async Task GetTotalHandlingAsync(CancellationToken token = defaul string strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["handling"]?.InnerText ?? objMod.Bonus?["handling"]?.InnerText : objMod.Bonus?["handling"]?.InnerText; - intBaseHandling = - Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, Handling, "Handling", false, token) - .ConfigureAwait(false), - intBaseHandling); + if (!string.IsNullOrEmpty(strBonus)) + intBaseHandling = + Math.Max( + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), Handling, "Handling", false, token) + .ConfigureAwait(false), + intBaseHandling); strBonus = objMod.WirelessOn ? objMod.WirelessBonus?["offroadhandling"]?.InnerText ?? objMod.Bonus?["offroadhandling"]?.InnerText : objMod.Bonus?["offroadhandling"]?.InnerText; - intBaseOffroadHandling = - Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, OffroadHandling, "OffroadHandling", false, - token).ConfigureAwait(false), intBaseOffroadHandling); - if (IsDrone && _objCharacter.Settings.DroneMods) + if (!string.IsNullOrEmpty(strBonus)) + intBaseOffroadHandling = + Math.Max( + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), OffroadHandling, "OffroadHandling", + false, + token).ConfigureAwait(false), intBaseOffroadHandling); + if (IsDrone && await _objCharacter.Settings.GetDroneModsAsync(token).ConfigureAwait(false)) { strBonus = objMod.Bonus?["armor"]?.InnerText; - intTotalArmor = - Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, token) - .ConfigureAwait(false), - intTotalArmor); - if (objMod.WirelessOn && objMod.WirelessBonus != null) - { - strBonus = objMod.WirelessBonus["armor"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) intTotalArmor = Math.Max( - await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, token) + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", false, token) .ConfigureAwait(false), intTotalArmor); + if (objMod.WirelessOn && objMod.WirelessBonus != null) + { + strBonus = objMod.WirelessBonus["armor"]?.InnerText; + if (!string.IsNullOrEmpty(strBonus)) + intTotalArmor = + Math.Max( + await ParseBonusAsync(strBonus, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", false, + token) + .ConfigureAwait(false), + intTotalArmor); } } }, token).ConfigureAwait(false); @@ -3570,14 +3637,14 @@ await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, to if (objMod.Bonus != null) { intTotalBonusHandling += await ParseBonusAsync(objMod.Bonus["handling"]?.InnerText, - objMod.Rating, + await objMod.GetRatingAsync(token).ConfigureAwait(false), intBaseOffroadHandling, "Handling", token: token).ConfigureAwait(false); intTotalBonusOffroadHandling += await ParseBonusAsync( objMod.Bonus["offroadhandling"]?.InnerText, - objMod.Rating, intBaseOffroadHandling, "OffroadHandling", token: token) + await objMod.GetRatingAsync(token).ConfigureAwait(false), intBaseOffroadHandling, "OffroadHandling", token: token) .ConfigureAwait(false); - if (IsDrone && _objCharacter.Settings.DroneMods) - intTemp += await ParseBonusAsync(objMod.Bonus["armor"]?.InnerText, objMod.Rating, + if (IsDrone && await _objCharacter.Settings.GetDroneModsAsync(token).ConfigureAwait(false)) + intTemp += await ParseBonusAsync(objMod.Bonus["armor"]?.InnerText, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", token: token).ConfigureAwait(false); } @@ -3585,12 +3652,12 @@ await ParseBonusAsync(strBonus, objMod.Rating, intTotalArmor, "Armor", false, to if (objMod.WirelessOn && objMod.WirelessBonus != null) { intTotalBonusHandling += await ParseBonusAsync(objMod.WirelessBonus["handling"]?.InnerText, - objMod.Rating, intBaseOffroadHandling, "Handling", token: token).ConfigureAwait(false); + await objMod.GetRatingAsync(token).ConfigureAwait(false), intBaseOffroadHandling, "Handling", token: token).ConfigureAwait(false); intTotalBonusOffroadHandling += await ParseBonusAsync( - objMod.WirelessBonus["offroadhandling"]?.InnerText, objMod.Rating, intBaseOffroadHandling, + objMod.WirelessBonus["offroadhandling"]?.InnerText, await objMod.GetRatingAsync(token).ConfigureAwait(false), intBaseOffroadHandling, "OffroadHandling", token: token).ConfigureAwait(false); - if (IsDrone && _objCharacter.Settings.DroneMods) - intTemp += await ParseBonusAsync(objMod.WirelessBonus["armor"]?.InnerText, objMod.Rating, + if (IsDrone && await _objCharacter.Settings.GetDroneModsAsync(token).ConfigureAwait(false)) + intTemp += await ParseBonusAsync(objMod.WirelessBonus["armor"]?.InnerText, await objMod.GetRatingAsync(token).ConfigureAwait(false), intTotalArmor, "Armor", token: token).ConfigureAwait(false); } @@ -3640,11 +3707,13 @@ public int TotalArmor continue; string strLoop = objMod.Bonus?["armor"]?.InnerText; - intArmor = Math.Max(intArmor, ParseBonus(strLoop, objMod.Rating, intArmor, "Armor", false)); + if (!string.IsNullOrEmpty(strLoop)) + intArmor = Math.Max(intArmor, ParseBonus(strLoop, objMod.Rating, intArmor, "Armor", false)); if (!objMod.WirelessOn || objMod.WirelessBonus == null) continue; strLoop = objMod.WirelessBonus?["armor"]?.InnerText; - intArmor = Math.Max(intArmor, ParseBonus(strLoop, objMod.Rating, intArmor, "Armor", false)); + if (!string.IsNullOrEmpty(strLoop)) + intArmor = Math.Max(intArmor, ParseBonus(strLoop, objMod.Rating, intArmor, "Armor", false)); } int intModArmor = 0; @@ -3656,11 +3725,13 @@ public int TotalArmor continue; string strLoop = objMod.Bonus?["armor"]?.InnerText; - intModArmor += ParseBonus(strLoop, objMod.Rating, intArmor, "Armor"); + if (!string.IsNullOrEmpty(strLoop)) + intModArmor += ParseBonus(strLoop, objMod.Rating, intArmor, "Armor"); if (!objMod.WirelessOn || objMod.WirelessBonus == null) continue; strLoop = objMod.WirelessBonus?["armor"]?.InnerText; - intModArmor += ParseBonus(strLoop, objMod.Rating, intArmor, "Armor"); + if (!string.IsNullOrEmpty(strLoop)) + intModArmor += ParseBonus(strLoop, objMod.Rating, intArmor, "Armor"); } return Math.Min(MaxArmor, intModArmor + intArmor); @@ -3687,15 +3758,19 @@ public async Task GetTotalArmorAsync(CancellationToken token = default) return; string strLoop = objMod.Bonus?["armor"]?.InnerText; - intArmor = Math.Max(intArmor, - await ParseBonusAsync(strLoop, objMod.Rating, intArmor, "Armor", false, token) - .ConfigureAwait(false)); + if (!string.IsNullOrEmpty(strLoop)) + intArmor = Math.Max(intArmor, + await ParseBonusAsync(strLoop, await objMod.GetRatingAsync(token).ConfigureAwait(false), intArmor, "Armor", false, + token) + .ConfigureAwait(false)); if (!objMod.WirelessOn || objMod.WirelessBonus == null) return; strLoop = objMod.WirelessBonus?["armor"]?.InnerText; - intArmor = Math.Max(intArmor, - await ParseBonusAsync(strLoop, objMod.Rating, intArmor, "Armor", false, token) - .ConfigureAwait(false)); + if (!string.IsNullOrEmpty(strLoop)) + intArmor = Math.Max(intArmor, + await ParseBonusAsync(strLoop, await objMod.GetRatingAsync(token).ConfigureAwait(false), intArmor, "Armor", false, + token) + .ConfigureAwait(false)); }, token).ConfigureAwait(false); // Add the Modification's Armor to the Vehicle's base Armor. @@ -3705,13 +3780,18 @@ await ParseBonusAsync(strLoop, objMod.Rating, intArmor, "Armor", false, token) return 0; string strLoop = objMod.Bonus?["armor"]?.InnerText; - int intTemp = await ParseBonusAsync(strLoop, objMod.Rating, intArmor, "Armor", token: token) - .ConfigureAwait(false); + int intTemp = 0; + if (!string.IsNullOrEmpty(strLoop)) + intTemp += await ParseBonusAsync(strLoop, await objMod.GetRatingAsync(token).ConfigureAwait(false), intArmor, + "Armor", token: token) + .ConfigureAwait(false); if (objMod.WirelessOn && objMod.WirelessBonus != null) { strLoop = objMod.WirelessBonus?["armor"]?.InnerText; - intTemp += await ParseBonusAsync(strLoop, objMod.Rating, intArmor, "Armor", token: token) - .ConfigureAwait(false); + if (!string.IsNullOrEmpty(strLoop)) + intTemp += await ParseBonusAsync(strLoop, await objMod.GetRatingAsync(token).ConfigureAwait(false), intArmor, + "Armor", token: token) + .ConfigureAwait(false); } return intTemp; @@ -3768,13 +3848,13 @@ public async Task GetMaxArmorAsync(CancellationToken token = default) CharacterSettings objSettings = await _objCharacter.GetSettingsAsync(token).ConfigureAwait(false); // Drones have no theoretical armor cap in the optional rules, otherwise, it's capped - if (IsDrone && objSettings.DroneMods) + if (IsDrone && await objSettings.GetDroneModsAsync(token).ConfigureAwait(false)) return int.MaxValue; // Rigger 5 says max extra armor is Body + starting Armor, p159 // When you need to use a 0 for the math, use 0.5 instead int intReturn = IsDrone && await objSettings.GetDroneArmorMultiplierEnabledAsync(token).ConfigureAwait(false) - ? ((Math.Max(Body, 0.5m) + Armor) * objSettings.DroneArmorMultiplier).StandardRound() + ? ((Math.Max(Body, 0.5m) + Armor) * await objSettings.GetDroneArmorMultiplierAsync(token).ConfigureAwait(false)).StandardRound() : Math.Max(Body + Armor, 1); return intReturn; diff --git a/Chummer/Backend/Equipment/VehicleMod.cs b/Chummer/Backend/Equipment/VehicleMod.cs index 6edbf053b6..eba7c5cd3a 100644 --- a/Chummer/Backend/Equipment/VehicleMod.cs +++ b/Chummer/Backend/Equipment/VehicleMod.cs @@ -50,7 +50,7 @@ public sealed class VehicleMod : IHasInternalId, IHasName, IHasSourceId, IHasXml private string _strSlots = "0"; private int _intRating; private string _strRatingLabel = "String_Rating"; - private string _strMaxRating = "0"; + private string _strMaxRating = string.Empty; private string _strCost = string.Empty; private decimal _decMarkup; private string _strAvail = string.Empty; @@ -212,19 +212,16 @@ private void ChildrenWeaponsOnCollectionChanged(object sender, NotifyCollectionC _colNotes = ColorTranslator.FromHtml(sNotesColor); objXmlMod.TryGetStringFieldQuickly("capacity", ref _strCapacity); objXmlMod.TryGetStringFieldQuickly("rating", ref _strMaxRating); - switch (_strMaxRating) + switch (_strMaxRating.ToUpperInvariant()) { - case "qty": + case "QTY": _strRatingLabel = "Label_Qty"; break; - case "seats": + case "SEATS": _strRatingLabel = "Label_Seats"; break; } - if (string.IsNullOrEmpty(_strMaxRating)) - _intRating = 0; - else - _intRating = Math.Max(intRating, 1); + _intRating = string.IsNullOrEmpty(_strMaxRating) ? 0 : Math.Min(Math.Max(intRating, 1), blnSync ? MaxRating : await GetMaxRatingAsync(token).ConfigureAwait(false)); objXmlMod.TryGetStringFieldQuickly("ratinglabel", ref _strRatingLabel); objXmlMod.TryGetInt32FieldQuickly("conditionmonitor", ref _intConditionMonitor); objXmlMod.TryGetStringFieldQuickly("weaponmountcategories", ref _strWeaponMountCategories); @@ -764,34 +761,169 @@ public string Capacity /// public int Rating { - get => _intRating; + get => Math.Min(_intRating, MaxRating); set { - int intNewRating = Math.Max(0, value); + int intNewRating = Math.Min(Math.Max(1, value), MaxRating); if (Interlocked.Exchange(ref _intRating, intNewRating) != intNewRating && !IncludedInVehicle && Equipped) { - if (_objParent != null && (Bonus?["sensor"] != null || (WirelessOn && WirelessBonus?["sensor"] != null))) + if (Parent != null && (Bonus?["sensor"] != null || (WirelessOn && WirelessBonus?["sensor"] != null))) { // Any time any vehicle mod is changed, update our sensory array's rating, just in case - Gear objGear = _objParent.GearChildren + Gear objGear = Parent.GearChildren .FirstOrDefault(x => x.Category == "Sensors" && x.Name == "Sensor Array" && x.IncludedInParent); if (objGear != null) - objGear.Rating = _objParent.CalculatedSensor; + objGear.Rating = Parent.CalculatedSensor; } - if (_objCharacter.IsAI && _objCharacter.HomeNode is Vehicle objVehicle && objVehicle == _objParent) + if (_objCharacter.IsAI && _objCharacter.HomeNode is Vehicle objVehicle && objVehicle == Parent) _objCharacter.OnPropertyChanged(nameof(Character.PhysicalCM)); } } } + public async Task GetRatingAsync(CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + return Math.Min(_intRating, await GetMaxRatingAsync(token).ConfigureAwait(false)); + } + + public async Task SetRatingAsync(int value, CancellationToken token = default) + { + int intNewRating = Math.Min(Math.Max(1, value), await GetMaxRatingAsync(token).ConfigureAwait(false)); + if (Interlocked.Exchange(ref _intRating, intNewRating) != intNewRating && !IncludedInVehicle && Equipped) + { + if (Parent != null && (Bonus?["sensor"] != null || (WirelessOn && WirelessBonus?["sensor"] != null))) + { + // Any time any vehicle mod is changed, update our sensory array's rating, just in case + Gear objGear = await _objParent.GearChildren + .FirstOrDefaultAsync(x => + x.Category == "Sensors" && x.Name == "Sensor Array" && x.IncludedInParent, token: token).ConfigureAwait(false); + if (objGear != null) + await objGear.SetRatingAsync(await Parent.GetCalculatedSensorAsync(token).ConfigureAwait(false), token).ConfigureAwait(false); + } + if (await _objCharacter.GetIsAIAsync(token).ConfigureAwait(false) && await _objCharacter.GetHomeNodeAsync(token).ConfigureAwait(false) is Vehicle objVehicle && objVehicle == Parent) + await _objCharacter.OnPropertyChangedAsync(nameof(Character.PhysicalCM), token).ConfigureAwait(false); + } + } + /// /// Maximum Rating. /// - public string MaxRating + public int MaxRating { - get => _strMaxRating; - set => _strMaxRating = value; + get + { + string strText = _strMaxRating.ToUpperInvariant(); + if (string.IsNullOrEmpty(strText)) + return 0; + int intReturn = 0; + switch (strText) + { + case "QTY": + intReturn = Vehicle.MaxWheels; + break; + case "SEATS": + intReturn = Parent.GetTotalSeats(this); + break; + case "BODY": + intReturn = Parent.GetTotalBody(this); + break; + default: + int.TryParse(strText, out intReturn); + break; + } + + int intMaxValue = int.MaxValue; + if (Name.StartsWith("Armor", StringComparison.OrdinalIgnoreCase)) + intMaxValue = Parent.MaxArmor; + else + { + switch (Category.ToUpperInvariant()) + { + case "HANDLING": + intMaxValue = Parent.MaxHandling; + break; + case "SPEED": + intMaxValue = Parent.MaxSpeed; + break; + case "ACCELERATION": + intMaxValue = Parent.MaxAcceleration; + break; + case "SENSOR": + intMaxValue = Parent.MaxSensor; + break; + case "PILOT": + intMaxValue = Parent.MaxPilot; + break; + default: + if (Name.StartsWith("Pilot Program", StringComparison.OrdinalIgnoreCase)) + { + intMaxValue = Parent.MaxPilot; + } + break; + } + } + + return Math.Min(intReturn, intMaxValue); + } + } + + public async Task GetMaxRatingAsync(CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + string strText = _strMaxRating.ToUpperInvariant(); + if (string.IsNullOrEmpty(strText)) + return 0; + int intReturn = 0; + switch (strText) + { + case "QTY": + intReturn = Vehicle.MaxWheels; + break; + case "SEATS": + intReturn = await Parent.GetTotalSeatsAsync(this, token).ConfigureAwait(false); + break; + case "BODY": + intReturn = await Parent.GetTotalBodyAsync(this, token).ConfigureAwait(false); + break; + default: + int.TryParse(strText, out intReturn); + break; + } + + int intMaxValue = int.MaxValue; + if (Name.StartsWith("Armor", StringComparison.OrdinalIgnoreCase)) + intMaxValue = await Parent.GetMaxArmorAsync(token).ConfigureAwait(false); + else + { + switch (Category.ToUpperInvariant()) + { + case "HANDLING": + intMaxValue = await Parent.GetMaxHandlingAsync(token).ConfigureAwait(false); + break; + case "SPEED": + intMaxValue = await Parent.GetMaxSpeedAsync(token).ConfigureAwait(false); + break; + case "ACCELERATION": + intMaxValue = await Parent.GetMaxAccelerationAsync(token).ConfigureAwait(false); + break; + case "SENSOR": + intMaxValue = await Parent.GetMaxSensorAsync(token).ConfigureAwait(false); + break; + case "PILOT": + intMaxValue = await Parent.GetMaxPilotAsync(token).ConfigureAwait(false); + break; + default: + if (Name.StartsWith("Pilot Program", StringComparison.OrdinalIgnoreCase)) + { + intMaxValue = await Parent.GetMaxPilotAsync(token).ConfigureAwait(false); + } + break; + } + } + + return Math.Min(intReturn, intMaxValue); } public string RatingLabel @@ -1180,7 +1312,7 @@ public AvailabilityValue TotalAvailTuple(bool blnCheckChildren = true) if (strAvail.StartsWith("Range(", StringComparison.Ordinal)) { // If the Availability code is based on the current Rating of the item, separate the Availability string into an array and find the first bracket that the Rating is lower than or equal to. - string[] strValues = strAvail.Replace("MaxRating", MaxRating).TrimStartOnce("Range(", true).TrimEndOnce(')').Split(',', StringSplitOptions.RemoveEmptyEntries); + string[] strValues = strAvail.CheapReplace("MaxRating", () => MaxRating.ToString(GlobalSettings.InvariantCultureInfo)).TrimStartOnce("Range(", true).TrimEndOnce(')').Split(',', StringSplitOptions.RemoveEmptyEntries); foreach (string strValue in strValues) { string[] astrValue = strValue.Split('['); @@ -1292,7 +1424,11 @@ public async Task TotalAvailTupleAsync(bool blnCheckChildren if (strAvail.StartsWith("Range(", StringComparison.Ordinal)) { // If the Availability code is based on the current Rating of the item, separate the Availability string into an array and find the first bracket that the Rating is lower than or equal to. - string[] strValues = strAvail.Replace("MaxRating", MaxRating).TrimStartOnce("Range(", true).TrimEndOnce(')').Split(',', StringSplitOptions.RemoveEmptyEntries); + string[] strValues = + (await strAvail.CheapReplaceAsync("MaxRating", + async () => (await GetMaxRatingAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.InvariantCultureInfo), + token: token).ConfigureAwait(false)).TrimStartOnce("Range(", true).TrimEndOnce(')') + .Split(',', StringSplitOptions.RemoveEmptyEntries); foreach (string strValue in strValues) { string[] astrValue = strValue.Split('['); @@ -1488,7 +1624,7 @@ public async Task GetCalculatedCapacityAsync(CancellationToken token = d if (strReturn.StartsWith("FixedValues(", StringComparison.Ordinal)) { string[] strValues = strReturn.TrimStartOnce("FixedValues(", true).TrimEndOnce(')').Split(',', StringSplitOptions.RemoveEmptyEntries); - strReturn = strValues[Math.Max(Math.Min(Rating, strValues.Length) - 1, 0)]; + strReturn = strValues[Math.Max(Math.Min(await GetRatingAsync(token).ConfigureAwait(false), strValues.Length) - 1, 0)]; } int intPos = strReturn.IndexOf("/[", StringComparison.Ordinal); @@ -1508,12 +1644,12 @@ public async Task GetCalculatedCapacityAsync(CancellationToken token = d if (strFirstHalf.StartsWith("FixedValues(", StringComparison.Ordinal)) { string[] strValues = strFirstHalf.TrimStartOnce("FixedValues(", true).TrimEndOnce(')').Split(',', StringSplitOptions.RemoveEmptyEntries); - strFirstHalf = strValues[Math.Max(Math.Min(Rating, strValues.Length) - 1, 0)]; + strFirstHalf = strValues[Math.Max(Math.Min(await GetRatingAsync(token).ConfigureAwait(false), strValues.Length) - 1, 0)]; } try { - (bool blnIsSuccess, object objProcess) = await CommonFunctions.EvaluateInvariantXPathAsync(strFirstHalf.Replace("Rating", Rating.ToString(GlobalSettings.InvariantCultureInfo)), token).ConfigureAwait(false); + (bool blnIsSuccess, object objProcess) = await CommonFunctions.EvaluateInvariantXPathAsync(strFirstHalf.Replace("Rating", (await GetRatingAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.InvariantCultureInfo)), token).ConfigureAwait(false); strReturn = blnIsSuccess ? ((double)objProcess).ToString("#,0.##", GlobalSettings.CultureInfo) : strFirstHalf; } catch (OverflowException) // Result is text and not a double @@ -1534,7 +1670,7 @@ public async Task GetCalculatedCapacityAsync(CancellationToken token = d strSecondHalf = strSecondHalf.Trim('[', ']'); try { - (bool blnIsSuccess, object objProcess) = await CommonFunctions.EvaluateInvariantXPathAsync(strSecondHalf.Replace("Rating", Rating.ToString(GlobalSettings.InvariantCultureInfo)), token).ConfigureAwait(false); + (bool blnIsSuccess, object objProcess) = await CommonFunctions.EvaluateInvariantXPathAsync(strSecondHalf.Replace("Rating", (await GetRatingAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.InvariantCultureInfo)), token).ConfigureAwait(false); strSecondHalf = '[' + (blnIsSuccess ? ((double)objProcess).ToString("#,0.##", GlobalSettings.CultureInfo) : strSecondHalf) + ']'; } catch (OverflowException) // Result is text and not a double @@ -1557,7 +1693,7 @@ public async Task GetCalculatedCapacityAsync(CancellationToken token = d string strCapacity = strReturn; if (blnSquareBrackets) strCapacity = strCapacity.Substring(1, strCapacity.Length - 2); - (bool blnIsSuccess, object objProcess) = await CommonFunctions.EvaluateInvariantXPathAsync(strCapacity.Replace("Rating", Rating.ToString(GlobalSettings.InvariantCultureInfo)), token).ConfigureAwait(false); + (bool blnIsSuccess, object objProcess) = await CommonFunctions.EvaluateInvariantXPathAsync(strCapacity.Replace("Rating", (await GetRatingAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.InvariantCultureInfo)), token).ConfigureAwait(false); strReturn = blnIsSuccess ? ((double)objProcess).ToString("#,0.##", GlobalSettings.CultureInfo) : strCapacity; if (blnSquareBrackets) strReturn = '[' + strReturn + ']'; @@ -1707,13 +1843,13 @@ public async Task TotalCostInMountCreation(int intSlots, CancellationTo { string[] strValues = strCostExpr.TrimStartOnce("FixedValues(", true).TrimEndOnce(')') .Split(',', StringSplitOptions.RemoveEmptyEntries); - strCostExpr = strValues[Math.Max(Math.Min(Rating, strValues.Length) - 1, 0)]; + strCostExpr = strValues[Math.Max(Math.Min(await GetRatingAsync(token).ConfigureAwait(false), strValues.Length) - 1, 0)]; } using (new FetchSafelyFromPool(Utils.StringBuilderPool, out StringBuilder sbdCost)) { sbdCost.Append(strCostExpr.TrimStart('+')); - sbdCost.Replace("Rating", Rating.ToString(GlobalSettings.InvariantCultureInfo)); + sbdCost.Replace("Rating", (await GetRatingAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.InvariantCultureInfo)); await _objCharacter.AttributeSection.ProcessAttributesInXPathAsync(sbdCost, strCostExpr, token: token).ConfigureAwait(false); @@ -1860,13 +1996,13 @@ public async Task GetOwnCostAsync(CancellationToken token = default) { string[] strValues = strCostExpr.TrimStartOnce("FixedValues(", true).TrimEndOnce(')') .Split(',', StringSplitOptions.RemoveEmptyEntries); - strCostExpr = strValues[Math.Max(Math.Min(Rating, strValues.Length) - 1, 0)]; + strCostExpr = strValues[Math.Max(Math.Min(await GetRatingAsync(token).ConfigureAwait(false), strValues.Length) - 1, 0)]; } using (new FetchSafelyFromPool(Utils.StringBuilderPool, out StringBuilder sbdCost)) { sbdCost.Append(strCostExpr.TrimStart('+')); - sbdCost.Replace("Rating", Rating.ToString(GlobalSettings.InvariantCultureInfo)); + sbdCost.Replace("Rating", (await GetRatingAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.InvariantCultureInfo)); await _objCharacter.AttributeSection.ProcessAttributesInXPathAsync(sbdCost, strCostExpr, token: token).ConfigureAwait(false); await sbdCost.CheapReplaceAsync(strCostExpr, "Vehicle Cost", async () => Parent != null @@ -1978,13 +2114,13 @@ public async Task GetCalculatedSlotsAsync(CancellationToken token = default { string[] strValues = strSlotsExpression.TrimStartOnce("FixedValues(", true).TrimEndOnce(')') .Split(',', StringSplitOptions.RemoveEmptyEntries); - strSlotsExpression = strValues[Math.Max(Math.Min(Rating, strValues.Length) - 1, 0)]; + strSlotsExpression = strValues[Math.Max(Math.Min(await GetRatingAsync(token).ConfigureAwait(false), strValues.Length) - 1, 0)]; } using (new FetchSafelyFromPool(Utils.StringBuilderPool, out StringBuilder sbdReturn)) { sbdReturn.Append(strSlotsExpression.TrimStart('+')); - sbdReturn.Replace("Rating", Rating.ToString(GlobalSettings.InvariantCultureInfo)); + sbdReturn.Replace("Rating", (await GetRatingAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.InvariantCultureInfo)); await _objCharacter.AttributeSection.ProcessAttributesInXPathAsync(sbdReturn, strSlotsExpression, token: token).ConfigureAwait(false); await sbdReturn.CheapReplaceAsync(strSlotsExpression, "Vehicle Cost", async () => Parent != null @@ -2058,8 +2194,9 @@ public string DisplayName(CultureInfo objCulture, string strLanguage) string strSpace = LanguageManager.GetString("String_Space", strLanguage); if (!string.IsNullOrEmpty(Extra)) strReturn += strSpace + '(' + _objCharacter.TranslateExtra(Extra, strLanguage) + ')'; - if (Rating > 0) - strReturn += strSpace + '(' + LanguageManager.GetString(RatingLabel, strLanguage) + strSpace + Rating.ToString(objCulture) + ')'; + int intRating = Rating; + if (intRating > 0) + strReturn += strSpace + '(' + LanguageManager.GetString(RatingLabel, strLanguage) + strSpace + intRating.ToString(objCulture) + ')'; return strReturn; } @@ -2072,8 +2209,9 @@ public async Task DisplayNameAsync(CultureInfo objCulture, string strLan string strSpace = await LanguageManager.GetStringAsync("String_Space", strLanguage, token: token).ConfigureAwait(false); if (!string.IsNullOrEmpty(Extra)) strReturn += strSpace + '(' + await _objCharacter.TranslateExtraAsync(Extra, strLanguage, token: token).ConfigureAwait(false) + ')'; - if (Rating > 0) - strReturn += strSpace + '(' + await LanguageManager.GetStringAsync(RatingLabel, strLanguage, token: token).ConfigureAwait(false) + strSpace + Rating.ToString(objCulture) + ')'; + int intRating = await GetRatingAsync(token).ConfigureAwait(false); + if (intRating > 0) + strReturn += strSpace + '(' + await LanguageManager.GetStringAsync(RatingLabel, strLanguage, token: token).ConfigureAwait(false) + strSpace + intRating.ToString(objCulture) + ')'; return strReturn; } diff --git a/Chummer/Forms/Character Forms/CharacterCareer.cs b/Chummer/Forms/Character Forms/CharacterCareer.cs index 3ed28f0bd9..45b871079b 100644 --- a/Chummer/Forms/Character Forms/CharacterCareer.cs +++ b/Chummer/Forms/Character Forms/CharacterCareer.cs @@ -11049,74 +11049,6 @@ private async void tsVehicleAddMod_Click(object sender, EventArgs e) }; await objMod.CreateAsync(objXmlMod, frmPickVehicleMod.MyForm.SelectedRating, objVehicle, frmPickVehicleMod.MyForm.Markup, token: GenericToken).ConfigureAwait(false); - // Make sure that the Armor Rating does not exceed the maximum allowed by the Vehicle. - if (objMod.Name.StartsWith("Armor", StringComparison.Ordinal)) - { - int intMaxArmor = await objVehicle.GetMaxArmorAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxArmor) - { - objMod.Rating = intMaxArmor; - } - } - else - { - switch (objMod.Category) - { - case "Handling": - { - int intMaxHandling = await objVehicle.GetMaxHandlingAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxHandling) - { - objMod.Rating = intMaxHandling; - } - - break; - } - case "Speed": - { - int intMaxSpeed = await objVehicle.GetMaxSpeedAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxSpeed) - { - objMod.Rating = intMaxSpeed; - } - - break; - } - case "Acceleration": - { - int intMaxAcceleration = await objVehicle.GetMaxAccelerationAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxAcceleration) - { - objMod.Rating = intMaxAcceleration; - } - - break; - } - case "Sensor": - { - int intMaxSensor = await objVehicle.GetMaxSensorAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxSensor) - { - objMod.Rating = intMaxSensor; - } - - break; - } - default: - { - if (objMod.Name.StartsWith("Pilot Program", StringComparison.Ordinal)) - { - int intMaxPilot = await objVehicle.GetMaxPilotAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxPilot) - { - objMod.Rating = intMaxPilot; - } - } - - break; - } - } - } // Check the item's Cost and make sure the character can afford it. decimal decOriginalCost = await objVehicle.GetTotalCostAsync(GenericToken).ConfigureAwait(false); @@ -25007,48 +24939,26 @@ await cmdDeleteVehicle.DoThreadSafeAsync(x => x.Enabled = !objMod.IncludedInVehi .ConfigureAwait(false); await lblVehicleCategory.DoThreadSafeAsync(x => x.Text = strText, token) .ConfigureAwait(false); - if (!objMod.MaxRating.Equals("qty", StringComparison.OrdinalIgnoreCase)) - { - if (objMod.MaxRating.Equals("seats", StringComparison.OrdinalIgnoreCase)) - { - objMod.MaxRating = (await objMod.Parent.GetTotalSeatsAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.CultureInfo); - } - else if (objMod.MaxRating.Equals("body", StringComparison.OrdinalIgnoreCase)) - { - objMod.MaxRating = (await objMod.Parent.GetTotalBodyAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.CultureInfo); - } - - token.ThrowIfCancellationRequested(); - if (Convert.ToInt32(objMod.MaxRating, GlobalSettings.InvariantCultureInfo) > 0) - { - await lblVehicleRatingLabel.DoThreadSafeAsync(x => x.Visible = true, token) - .ConfigureAwait(false); - await lblVehicleRating.DoThreadSafeAsync(x => - { - x.Text = objMod.Rating.ToString(GlobalSettings - .CultureInfo); - x.Visible = true; - }, token).ConfigureAwait(false); - } - else - { - await lblVehicleRatingLabel.DoThreadSafeAsync(x => x.Visible = false, token) - .ConfigureAwait(false); - await lblVehicleRating.DoThreadSafeAsync(x => x.Visible = false, token) - .ConfigureAwait(false); - } - } - else + int intMaxRating = await objMod.GetMaxRatingAsync(token).ConfigureAwait(false); + if (intMaxRating > 0) { await lblVehicleRatingLabel.DoThreadSafeAsync(x => x.Visible = true, token) - .ConfigureAwait(false); + .ConfigureAwait(false); + int intRating = await objMod.GetRatingAsync(token).ConfigureAwait(false); await lblVehicleRating.DoThreadSafeAsync(x => { - x.Text = objMod.Rating.ToString( - GlobalSettings.CultureInfo); + x.Text = intRating.ToString(GlobalSettings + .CultureInfo); x.Visible = true; }, token).ConfigureAwait(false); } + else + { + await lblVehicleRatingLabel.DoThreadSafeAsync(x => x.Visible = false, token) + .ConfigureAwait(false); + await lblVehicleRating.DoThreadSafeAsync(x => x.Visible = false, token) + .ConfigureAwait(false); + } token.ThrowIfCancellationRequested(); await lblVehicleGearQtyLabel.DoThreadSafeAsync(x => x.Visible = false, token) diff --git a/Chummer/Forms/Character Forms/CharacterCreate.cs b/Chummer/Forms/Character Forms/CharacterCreate.cs index 12f5511957..4ecdb4bcde 100644 --- a/Chummer/Forms/Character Forms/CharacterCreate.cs +++ b/Chummer/Forms/Character Forms/CharacterCreate.cs @@ -7312,75 +7312,6 @@ XmlNode objXmlMod await objMod.CreateAsync(objXmlMod, frmPickVehicleMod.MyForm.SelectedRating, objVehicle, frmPickVehicleMod.MyForm.Markup, token: GenericToken).ConfigureAwait(false); - // Make sure that the Armor Rating does not exceed the maximum allowed by the Vehicle. - if (objMod.Name.StartsWith("Armor", StringComparison.Ordinal)) - { - int intMaxArmor = await objVehicle.GetMaxArmorAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxArmor) - { - objMod.Rating = intMaxArmor; - } - } - else - { - switch (objMod.Category) - { - case "Handling": - { - int intMaxHandling = await objVehicle.GetMaxHandlingAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxHandling) - { - objMod.Rating = intMaxHandling; - } - - break; - } - case "Speed": - { - int intMaxSpeed = await objVehicle.GetMaxSpeedAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxSpeed) - { - objMod.Rating = intMaxSpeed; - } - - break; - } - case "Acceleration": - { - int intMaxAcceleration = await objVehicle.GetMaxAccelerationAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxAcceleration) - { - objMod.Rating = intMaxAcceleration; - } - - break; - } - case "Sensor": - { - int intMaxSensor = await objVehicle.GetMaxSensorAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxSensor) - { - objMod.Rating = intMaxSensor; - } - - break; - } - default: - { - if (objMod.Name.StartsWith("Pilot Program", StringComparison.Ordinal)) - { - int intMaxPilot = await objVehicle.GetMaxPilotAsync(GenericToken).ConfigureAwait(false); - if (objMod.Rating > intMaxPilot) - { - objMod.Rating = intMaxPilot; - } - } - - break; - } - } - } - // Check the item's Cost and make sure the character can afford it. if (frmPickVehicleMod.MyForm.FreeCost) objMod.Cost = "0"; @@ -12084,8 +12015,9 @@ private async void nudVehicleRating_ValueChanged(object sender, EventArgs e) { case VehicleMod objMod: { - objMod.Rating = await nudVehicleRating.DoThreadSafeFuncAsync(x => x.ValueAsInt, GenericToken) - .ConfigureAwait(false); + await objMod.SetRatingAsync(await nudVehicleRating + .DoThreadSafeFuncAsync(x => x.ValueAsInt, GenericToken) + .ConfigureAwait(false), GenericToken).ConfigureAwait(false); string strText = await objMod.GetCurrentDisplayNameAsync(GenericToken).ConfigureAwait(false); await treVehicles.DoThreadSafeAsync(() => objSelectedNode.Text = strText, GenericToken) .ConfigureAwait(false); @@ -19081,63 +19013,33 @@ await lblVehicleName.DoThreadSafeAsync(x => x.Text = strName, token) .ConfigureAwait(false); await lblVehicleCategory.DoThreadSafeAsync(x => x.Text = strText, token) .ConfigureAwait(false); - if (!objMod.MaxRating.Equals("qty", StringComparison.OrdinalIgnoreCase)) + int intMaxRating = await objMod.GetMaxRatingAsync(token).ConfigureAwait(false); + if (intMaxRating > 0) { - if (objMod.MaxRating.Equals("seats", StringComparison.OrdinalIgnoreCase)) - { - objMod.MaxRating = (await objMod.Parent.GetTotalSeatsAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.CultureInfo); - } - else if (objMod.MaxRating.Equals("body", StringComparison.OrdinalIgnoreCase)) - { - objMod.MaxRating = (await objMod.Parent.GetTotalBodyAsync(token).ConfigureAwait(false)).ToString(GlobalSettings.CultureInfo); - } - - token.ThrowIfCancellationRequested(); - if (int.TryParse(objMod.MaxRating, NumberStyles.Any, - GlobalSettings.InvariantCultureInfo, - out int intMaxRating) && intMaxRating > 0) - { - await lblVehicleRatingLabel.DoThreadSafeAsync(x => x.Visible = true, token) - .ConfigureAwait(false); - if (objMod.Name.StartsWith("Armor", StringComparison.Ordinal)) - intMaxRating = Math.Min(intMaxRating, await objMod.Parent.GetMaxArmorAsync(token).ConfigureAwait(false)); - // If the Mod is Armor, use the lower of the Mod's maximum Rating and MaxArmor value for the Vehicle instead. - await nudVehicleRating.DoThreadSafeAsync(x => - { - x.Maximum = intMaxRating; - x.Minimum = Math.Min(1, intMaxRating); - x.Visible = true; - x.Value = objMod.Rating; - x.Increment = 1; - x.Enabled = !objMod.IncludedInVehicle; - }, token).ConfigureAwait(false); - } - else + await lblVehicleRatingLabel.DoThreadSafeAsync(x => x.Visible = true, token) + .ConfigureAwait(false); + int intRating = await objMod.GetRatingAsync(token).ConfigureAwait(false); + await nudVehicleRating.DoThreadSafeAsync(x => { - await lblVehicleRatingLabel.DoThreadSafeAsync(x => x.Visible = false, token) - .ConfigureAwait(false); - await nudVehicleRating.DoThreadSafeAsync(x => - { - x.Minimum = 0; - x.Increment = 1; - x.Maximum = 0; - x.Enabled = false; - x.Visible = false; - }, token).ConfigureAwait(false); - } + x.Maximum = intMaxRating; + x.Minimum = Math.Min(1, intMaxRating); + x.Visible = true; + x.Value = intRating; + x.Increment = 1; + x.Enabled = !objMod.IncludedInVehicle; + }, token).ConfigureAwait(false); } else { - await lblVehicleRatingLabel.DoThreadSafeAsync(x => x.Visible = true, token) - .ConfigureAwait(false); + await lblVehicleRatingLabel.DoThreadSafeAsync(x => x.Visible = false, token) + .ConfigureAwait(false); await nudVehicleRating.DoThreadSafeAsync(x => { - x.Visible = true; - x.Minimum = 1; - x.Maximum = Vehicle.MaxWheels; - x.Value = objMod.Rating; + x.Minimum = 0; x.Increment = 1; - x.Enabled = !objMod.IncludedInVehicle; + x.Maximum = 0; + x.Enabled = false; + x.Visible = false; }, token).ConfigureAwait(false); } diff --git a/Chummer/Forms/Creation Forms/CreatePACKSKit.cs b/Chummer/Forms/Creation Forms/CreatePACKSKit.cs index fc95ae17ff..3e74f2c1a4 100644 --- a/Chummer/Forms/Creation Forms/CreatePACKSKit.cs +++ b/Chummer/Forms/Creation Forms/CreatePACKSKit.cs @@ -802,11 +802,12 @@ await WriteGear(objWriter, objAccessory.GearChildren) await objWriter.WriteStartElementAsync("mod").ConfigureAwait(false); await objWriter.WriteElementStringAsync("name", objVehicleMod.Name) .ConfigureAwait(false); - if (objVehicleMod.Rating > 0) + int intRating = await objVehicleMod.GetRatingAsync().ConfigureAwait(false); + if (intRating > 0) await objWriter .WriteElementStringAsync( "rating", - objVehicleMod.Rating.ToString( + intRating.ToString( GlobalSettings.InvariantCultureInfo)).ConfigureAwait(false); // await objWriter.WriteEndElementAsync().ConfigureAwait(false);