fix(vt): I widen tDouble-declared distance fields from float to double
Item I (slice-1 fix round, finishing item 1). CaptureMatchesDeclaredSettingTypeAndValue's ~1.5e-7 float-round-trip tolerance was masking a real cause, not a real value change: every tDouble-declared distance setting whose live field was actually a float (CombatSettings.MaximumRange/MinimumRange/ ApproachDistance/RingDistance/ArcRange/TargetSelectAngleRange/ PetCustomRange/CollisionProjectileRadius/CollisionStepDistance/ SpellRangeFudge, Looting.CorpseApproachRange/CorpseMinimumApproachRange, VitalPlan.HelperHealthDistance/HelperStaminaDistance/HelperManaDistance) lost precision below float's ~7-significant-digit guarantee on every load, since a loaded .usd's real value is a double. - All 15 fields widened from float to double, matching their declared tDouble type. Every physics/combat call site that genuinely needs a float (IProjectileAutomation.EvaluatePath/EvaluatePathWithDiagnostics, ICombatAutomation.CaptureHostileTargets/CaptureCorpses, the fellow-distance Lowest() helper) now casts explicitly at that one use site (CombatController.cs, Looting.cs, VitalRecharge.cs, MossTankCommands.cs, PetAutomation.cs) instead of the field itself being narrowed everywhere it's stored. - MossTankProfileStore's JSON DTOs (InventoryProfileDocument. CorpseApproachRange, CombatProfileDocument.MaximumRange/ ApproachDistance/TargetSelectAngleRange/ArcRange/RingDistance/ PetCustomRange) widened to match, so MossTank's own persisted profiles keep full precision too — their Apply()-side Math.Clamp calls needed no changes (the float literal bounds like 2f/100f already widen to the double overload implicitly). - VtankSettingsProfileSerializer.Apply's `(float)(cell.AsDouble() * 240d)` casts and `cell.AsFloat()` calls for these 15 settings are now plain `cell.AsDouble()` / `cell.AsDouble() * 240d` — no narrowing at all. - ValuesEqual's "d"/"f" branch dropped DoublesEqual/FloatRoundTripTolerance entirely: `a.AsDouble() == b.AsDouble()`, exact, matching every other tag. CaptureMatchesDeclaredSettingTypeAndValue's own test-side tolerance (a second, independently-tolerant comparison) removed the same way — all 135 catalog names now pass under Assert.Equal(exact) with zero special-casing. Full MossTank suite: 581/581 (no count change — this is a precision fix, not new coverage; CaptureMatchesDeclaredSettingTypeAndValue's own 135 cases already existed and now pass exactly instead of within tolerance). App.Tests (Plugin|LaunchOptions filter): 84/84. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
a3b4a8e2e5
commit
8fd3999616
10 changed files with 79 additions and 87 deletions
|
|
@ -243,11 +243,11 @@ internal sealed class CombatController
|
|||
_untilScan -= Math.Max(0d, elapsedSeconds);
|
||||
if (_untilScan <= 0d)
|
||||
{
|
||||
float acquisitionRange = navigationEnabled
|
||||
double acquisitionRange = navigationEnabled
|
||||
? Math.Max(_settings.MaximumRange, _settings.ApproachDistance)
|
||||
: _settings.MaximumRange;
|
||||
_targets = _host.Automation.Combat.CaptureHostileTargets(
|
||||
acquisitionRange);
|
||||
(float)acquisitionRange);
|
||||
foreach (uint ghost in _failures.ObserveTargets(
|
||||
_targets,
|
||||
_now,
|
||||
|
|
@ -588,7 +588,7 @@ internal sealed class CombatController
|
|||
|
||||
float maximumRange = spell.BaseRangeConstant
|
||||
+ (spell.BaseRangeModifier * skill.Current)
|
||||
- _settings.SpellRangeFudge;
|
||||
- (float)_settings.SpellRangeFudge;
|
||||
return maximumRange <= 0f
|
||||
|| target.ObjectId == 0u
|
||||
|| target.Distance <= MathF.Min(75f, maximumRange);
|
||||
|
|
@ -1087,15 +1087,15 @@ internal sealed class CombatController
|
|||
targetObjectId,
|
||||
kind,
|
||||
height,
|
||||
_settings.CollisionProjectileRadius,
|
||||
_settings.CollisionStepDistance,
|
||||
(float)_settings.CollisionProjectileRadius,
|
||||
(float)_settings.CollisionStepDistance,
|
||||
_settings.MaximumCollisionChecksPerTick)
|
||||
: _host.Automation.Projectiles.EvaluatePath(
|
||||
targetObjectId,
|
||||
kind,
|
||||
height,
|
||||
_settings.CollisionProjectileRadius,
|
||||
_settings.CollisionStepDistance,
|
||||
(float)_settings.CollisionProjectileRadius,
|
||||
(float)_settings.CollisionStepDistance,
|
||||
_settings.MaximumCollisionChecksPerTick);
|
||||
if (_settings.ShowCollisionDebug && result.DebugSamples.Count > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,25 +69,30 @@ internal sealed class CombatSettings
|
|||
public bool Enabled { get; set; } = true;
|
||||
/// <summary>VTank's hunt-cast skill margin.</summary>
|
||||
public int HuntSkillExcessOverDifficulty { get; set; } = 25;
|
||||
public float MaximumRange { get; set; } = 5f;
|
||||
// Declared tDouble in VTank's own Settings table (VtankOptionCatalog):
|
||||
// double, not float, so a loaded .usd round-trips this setting exactly
|
||||
// instead of losing precision below float's ~7-digit guarantee (item I,
|
||||
// slice-1 fix round). Physics/combat consumers that want a float cast
|
||||
// at their own use site.
|
||||
public double MaximumRange { get; set; } = 5d;
|
||||
/// <summary>
|
||||
/// Monsters nearer than this are not valid attack targets. VTank applies
|
||||
/// this before priority and angle/range ranking.
|
||||
/// </summary>
|
||||
public float MinimumRange { get; set; }
|
||||
public double MinimumRange { get; set; }
|
||||
/// <summary>
|
||||
/// VTank's Approach Distance. Zero disables monster approach; otherwise
|
||||
/// navigation may close a selected target from this range down to
|
||||
/// <see cref="MaximumRange"/>.
|
||||
/// </summary>
|
||||
public float ApproachDistance { get; set; }
|
||||
public double ApproachDistance { get; set; }
|
||||
public bool IdlePeaceMode { get; set; }
|
||||
public bool StopMacroOnDeath { get; set; } = true;
|
||||
public bool JumpOutWandCasting { get; set; }
|
||||
public bool DoJiggle { get; set; }
|
||||
public TargetSelectionMethod SelectionMethod { get; set; } =
|
||||
TargetSelectionMethod.Both;
|
||||
public float TargetSelectAngleRange { get; set; } = 5f;
|
||||
public double TargetSelectAngleRange { get; set; } = 5d;
|
||||
public bool TargetLock { get; set; }
|
||||
public PluginAttackHeight AttackHeight { get; set; } =
|
||||
PluginAttackHeight.Medium;
|
||||
|
|
@ -101,15 +106,15 @@ internal sealed class CombatSettings
|
|||
public double DebuffPrecastSeconds { get; set; } = 5d;
|
||||
public bool SwitchWandsToDebuff { get; set; }
|
||||
public UseArcsMode UseArcs { get; set; } = UseArcsMode.AtRange;
|
||||
public float SpellRangeFudge { get; set; } = 1f;
|
||||
public double SpellRangeFudge { get; set; } = 1d;
|
||||
public bool UseBreakableTurnTo { get; set; } = true;
|
||||
public bool UseProjectileAwareness { get; set; } = true;
|
||||
public float CollisionProjectileRadius { get; set; } = 0.4f;
|
||||
public float CollisionStepDistance { get; set; } = 0.7f;
|
||||
public double CollisionProjectileRadius { get; set; } = 0.4d;
|
||||
public double CollisionStepDistance { get; set; } = 0.7d;
|
||||
public bool ShowCollisionDebug { get; set; }
|
||||
public int MaximumCollisionChecksPerTick { get; set; } = 500;
|
||||
public float ArcRange { get; set; } = 5f;
|
||||
public float RingDistance { get; set; } = 5f;
|
||||
public double ArcRange { get; set; } = 5d;
|
||||
public double RingDistance { get; set; } = 5d;
|
||||
public int MinimumRingTargets { get; set; } = 4;
|
||||
public bool DeleteGhostMonsters { get; set; } = true;
|
||||
public int GhostMonsterSpellAttemptCount { get; set; } = 200;
|
||||
|
|
@ -119,7 +124,7 @@ internal sealed class CombatSettings
|
|||
public double GhostDeleteHealthTrackerSeconds { get; set; } = 30d;
|
||||
public bool SummonPets { get; set; } = true;
|
||||
public PetRangeMode PetRangeMode { get; set; } = PetRangeMode.AttackDistance;
|
||||
public float PetCustomRange { get; set; } = 5f;
|
||||
public double PetCustomRange { get; set; } = 5d;
|
||||
public int PetMonsterDensity { get; set; } = 1;
|
||||
public int PetRefillCountIdle { get; set; } = 3;
|
||||
public int PetRefillCountNormal { get; set; } = 1;
|
||||
|
|
|
|||
|
|
@ -89,8 +89,10 @@ internal sealed class LootSettings
|
|||
public bool CombineSalvage { get; set; } = true;
|
||||
public int ManaStoneLootCount { get; set; } = 4;
|
||||
public int ManaTankMinimumMana { get; set; } = 1000;
|
||||
public float CorpseApproachRange { get; set; } = 40f;
|
||||
public float CorpseMinimumApproachRange { get; set; } = 3.36f;
|
||||
// Declared tDouble in VTank's own Settings table (item I, slice-1 fix
|
||||
// round) — see CombatSettings.MaximumRange's doc comment for why.
|
||||
public double CorpseApproachRange { get; set; } = 40d;
|
||||
public double CorpseMinimumApproachRange { get; set; } = 3.36d;
|
||||
public double CorpseOpenTimeoutSeconds { get; set; } = 1.5d;
|
||||
public double CorpseItemAppearanceTimeoutSeconds { get; set; } = 6d;
|
||||
public double CorpseItemIdentifyTimeoutSeconds { get; set; } = 60d;
|
||||
|
|
@ -524,7 +526,7 @@ internal sealed class LootController
|
|||
_scanRemaining = Math.Clamp(_settings.ScanIntervalSeconds, 0.05d, 5d);
|
||||
|
||||
IReadOnlyList<PluginLootContainer> corpses = loot.CaptureCorpses(
|
||||
Math.Clamp(_settings.CorpseApproachRange, 2f, 100f));
|
||||
(float)Math.Clamp(_settings.CorpseApproachRange, 2d, 100d));
|
||||
PruneCorpseCache();
|
||||
foreach (PluginLootContainer seen in corpses)
|
||||
_corpseFirstSeen.TryAdd(seen.ObjectId, _lifetime);
|
||||
|
|
|
|||
|
|
@ -754,9 +754,9 @@ internal sealed partial class MossTankPanel
|
|||
private void TestPet()
|
||||
{
|
||||
IReadOnlyList<PluginCombatTarget> targets = _host.Automation.Combat
|
||||
.CaptureHostileTargets(_combatSettings.PetRangeMode == PetRangeMode.Custom
|
||||
.CaptureHostileTargets((float)(_combatSettings.PetRangeMode == PetRangeMode.Custom
|
||||
? _combatSettings.PetCustomRange
|
||||
: _combatSettings.MaximumRange);
|
||||
: _combatSettings.MaximumRange));
|
||||
PetAutomationChoice choice = PetAutomation.Select(
|
||||
_host.Automation.Items.CaptureOwnedItems(),
|
||||
targets,
|
||||
|
|
|
|||
|
|
@ -430,7 +430,7 @@ internal sealed class MossTankProfileStore
|
|||
public bool CombineSalvage { get; set; } = true;
|
||||
public int ManaStoneLootCount { get; set; } = 4;
|
||||
public int ManaTankMinimumMana { get; set; } = 1000;
|
||||
public float CorpseApproachRange { get; set; } = 40f;
|
||||
public double CorpseApproachRange { get; set; } = 40d;
|
||||
public double CorpseOpenTimeoutSeconds { get; set; } = 1.5d;
|
||||
public int BlacklistCorpseOpenAttemptCount { get; set; } = 30;
|
||||
public double BlacklistCorpseOpenTimeoutSeconds { get; set; } = 200d;
|
||||
|
|
@ -597,12 +597,12 @@ internal sealed class MossTankProfileStore
|
|||
private sealed class CombatProfileDocument
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
public float MaximumRange { get; set; } = 5f;
|
||||
public float ApproachDistance { get; set; }
|
||||
public double MaximumRange { get; set; } = 5d;
|
||||
public double ApproachDistance { get; set; }
|
||||
public bool IdlePeaceMode { get; set; }
|
||||
public TargetSelectionMethod SelectionMethod { get; set; } =
|
||||
TargetSelectionMethod.Both;
|
||||
public float TargetSelectAngleRange { get; set; } = 5f;
|
||||
public double TargetSelectAngleRange { get; set; } = 5d;
|
||||
public bool TargetLock { get; set; }
|
||||
public PluginAttackHeight AttackHeight { get; set; } =
|
||||
PluginAttackHeight.Medium;
|
||||
|
|
@ -616,8 +616,8 @@ internal sealed class MossTankProfileStore
|
|||
public double DebuffPrecastSeconds { get; set; } = 5d;
|
||||
public bool SwitchWandsToDebuff { get; set; }
|
||||
public UseArcsMode UseArcs { get; set; } = UseArcsMode.AtRange;
|
||||
public float ArcRange { get; set; } = 5f;
|
||||
public float RingDistance { get; set; } = 5f;
|
||||
public double ArcRange { get; set; } = 5d;
|
||||
public double RingDistance { get; set; } = 5d;
|
||||
public int MinimumRingTargets { get; set; } = 4;
|
||||
public bool DeleteGhostMonsters { get; set; } = true;
|
||||
public int GhostMonsterSpellAttemptCount { get; set; } = 200;
|
||||
|
|
@ -627,7 +627,7 @@ internal sealed class MossTankProfileStore
|
|||
public double GhostDeleteHealthTrackerSeconds { get; set; } = 30d;
|
||||
public bool SummonPets { get; set; } = true;
|
||||
public PetRangeMode PetRangeMode { get; set; } = PetRangeMode.AttackDistance;
|
||||
public float PetCustomRange { get; set; } = 5f;
|
||||
public double PetCustomRange { get; set; } = 5d;
|
||||
public int PetMonsterDensity { get; set; } = 1;
|
||||
public int PetRefillCountIdle { get; set; } = 3;
|
||||
public int PetRefillCountNormal { get; set; } = 1;
|
||||
|
|
|
|||
|
|
@ -117,9 +117,9 @@ internal sealed class PetAutomation
|
|||
if (!settings.SummonPets || activeOwnedPetCount > 0)
|
||||
return PetAutomationChoice.None;
|
||||
|
||||
float range = settings.PetRangeMode == PetRangeMode.Custom
|
||||
float range = (float)(settings.PetRangeMode == PetRangeMode.Custom
|
||||
? settings.PetCustomRange
|
||||
: settings.MaximumRange;
|
||||
: settings.MaximumRange);
|
||||
int density = Math.Max(1, settings.PetMonsterDensity);
|
||||
var eligible = new List<(PluginCombatTarget Target, ResolvedMonsterRule Rule)>();
|
||||
foreach (PluginCombatTarget target in targets)
|
||||
|
|
|
|||
|
|
@ -35,9 +35,11 @@ public sealed class VitalSettings
|
|||
public double HelperHealth { get; set; } = 0.20;
|
||||
public double HelperStamina { get; set; } = 0.01;
|
||||
public double HelperMana { get; set; } = 0.01;
|
||||
public float HelperHealthDistance { get; set; } = 59.6f;
|
||||
public float HelperStaminaDistance { get; set; } = 59.6f;
|
||||
public float HelperManaDistance { get; set; } = 32f;
|
||||
// Declared tDouble in VTank's own Settings table (item I, slice-1 fix
|
||||
// round) — see CombatSettings.MaximumRange's doc comment for why.
|
||||
public double HelperHealthDistance { get; set; } = 59.6d;
|
||||
public double HelperStaminaDistance { get; set; } = 59.6d;
|
||||
public double HelperManaDistance { get; set; } = 32d;
|
||||
|
||||
public bool HelpOthers { get; set; } = true;
|
||||
public bool UseHealersHeart { get; set; } = true;
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ internal static class VitalRechargePlanner
|
|||
|
||||
IReadOnlyList<PluginFellowMember> members =
|
||||
automation.Fellowship.CaptureMembers();
|
||||
foreach ((VitalKind vital, double threshold, float distance, uint baseSpell)
|
||||
foreach ((VitalKind vital, double threshold, double distance, uint baseSpell)
|
||||
in new[]
|
||||
{
|
||||
(VitalKind.Health, settings.HelperHealth,
|
||||
|
|
@ -158,7 +158,7 @@ internal static class VitalRechargePlanner
|
|||
settings.HelperManaDistance, (uint)SpellId.GiftOfEssence),
|
||||
})
|
||||
{
|
||||
PluginFellowMember? target = Lowest(members, vital, threshold, distance);
|
||||
PluginFellowMember? target = Lowest(members, vital, threshold, (float)distance);
|
||||
if (target is not { } fellow)
|
||||
continue;
|
||||
if (vital == VitalKind.Health
|
||||
|
|
|
|||
|
|
@ -147,17 +147,17 @@ internal static class VtankSettingsProfileSerializer
|
|||
};
|
||||
|
||||
/// <summary>
|
||||
/// Exact compare on the cell's own type — a <see cref="Capture"/> tag
|
||||
/// mismatch is now a real bug (fixed above), not something to paper
|
||||
/// over with a tolerance. The one narrow exception: several distance
|
||||
/// settings (AttackDistance, ArcRange, …) are declared <c>tDouble</c>
|
||||
/// but this port still stores their live value in a <c>float</c> field
|
||||
/// (CombatSettings.MaximumRange etc.) for the physics/combat math that
|
||||
/// consumes them; round-tripping through that float costs precision
|
||||
/// below the 15th significant digit, which is exactly float's ~7-digit
|
||||
/// guarantee, not a real value change. That residual is bounded by
|
||||
/// <see cref="FloatRoundTripTolerance"/>, not an arbitrary fudge factor.
|
||||
/// Every other tag (bool/int/enum/string/uint) compares byte-exact.
|
||||
/// Exact compare on the cell's own type, with no tolerance anywhere —
|
||||
/// a <see cref="Capture"/> tag mismatch is a real bug, not something to
|
||||
/// paper over. Item I (slice-1 fix round) removed the one narrow
|
||||
/// exception this used to carry: every <c>tDouble</c>-declared distance
|
||||
/// setting (AttackDistance, ArcRange, …) that used to round-trip
|
||||
/// through a <c>float</c>-typed live field (CombatSettings.MaximumRange
|
||||
/// etc., costing precision below float's ~7-digit guarantee) now stores
|
||||
/// that value as a real <c>double</c> — the field widened to match its
|
||||
/// declared type instead of the compare being loosened to match the
|
||||
/// field. Physics/combat consumers that want a <c>float</c> cast it at
|
||||
/// their own use site.
|
||||
/// </summary>
|
||||
private static bool ValuesEqual(VtankCell a, VtankCell b)
|
||||
{
|
||||
|
|
@ -169,23 +169,11 @@ internal static class VtankSettingsProfileSerializer
|
|||
"s" => a.AsString() == b.AsString(),
|
||||
"i" => a.AsInt() == b.AsInt(),
|
||||
"u" => a.AsUInt() == b.AsUInt(),
|
||||
"d" or "f" => DoublesEqual(a.AsDouble(), b.AsDouble()),
|
||||
"d" or "f" => a.AsDouble() == b.AsDouble(),
|
||||
_ => ReferenceEquals(a, b),
|
||||
};
|
||||
}
|
||||
|
||||
private static bool DoublesEqual(double left, double right)
|
||||
{
|
||||
double tolerance = Math.Max(Math.Abs(left), Math.Abs(right)) * FloatRoundTripTolerance;
|
||||
return Math.Abs(left - right) <= tolerance;
|
||||
}
|
||||
|
||||
// ~1.2e-7 is float's relative machine epsilon (2^-23); a value that has
|
||||
// been through one float round trip (Apply's (float)(... * 240d) cast)
|
||||
// can differ from its original double by up to this much and still be
|
||||
// the SAME value VTank wrote.
|
||||
private const double FloatRoundTripTolerance = 1.5e-7;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Apply: .usd cell -> live setting.
|
||||
// ------------------------------------------------------------------
|
||||
|
|
@ -219,18 +207,18 @@ internal static class VtankSettingsProfileSerializer
|
|||
case "recharge-helper-stam": v.HelperStamina = cell.AsDouble() / 100d; break;
|
||||
case "recharge-helper-mana": v.HelperMana = cell.AsDouble() / 100d; break;
|
||||
case "dohelp": v.HelpOthers = cell.AsBool(); break;
|
||||
case "attackdistance": c.MaximumRange = (float)(cell.AsDouble() * 240d); break;
|
||||
case "attackminimumdistance": c.MinimumRange = (float)(cell.AsDouble() * 240d); break;
|
||||
case "approachdistance": c.ApproachDistance = (float)(cell.AsDouble() * 240d); break;
|
||||
case "ringdistance": c.RingDistance = (float)(cell.AsDouble() * 240d); break;
|
||||
case "corpseapproachrange-max": i.Loot.CorpseApproachRange = (float)(cell.AsDouble() * 240d); break;
|
||||
case "corpseapproachrange-min": i.Loot.CorpseMinimumApproachRange = (float)(cell.AsDouble() * 240d); break;
|
||||
case "attackdistance": c.MaximumRange = cell.AsDouble() * 240d; break;
|
||||
case "attackminimumdistance": c.MinimumRange = cell.AsDouble() * 240d; break;
|
||||
case "approachdistance": c.ApproachDistance = cell.AsDouble() * 240d; break;
|
||||
case "ringdistance": c.RingDistance = cell.AsDouble() * 240d; break;
|
||||
case "corpseapproachrange-max": i.Loot.CorpseApproachRange = cell.AsDouble() * 240d; break;
|
||||
case "corpseapproachrange-min": i.Loot.CorpseMinimumApproachRange = cell.AsDouble() * 240d; break;
|
||||
case "navclosestoprange": n.MinimumDistanceMeters = cell.AsDouble() * 240d; break;
|
||||
case "navfarstoprange": n.MaximumDistanceMeters = cell.AsDouble() * 240d; break;
|
||||
case "useportaldistance": n.PortalUseDistanceMeters = cell.AsDouble() * 240d; break;
|
||||
case "helperdistancehitp": v.HelperHealthDistance = (float)(cell.AsDouble() * 240d); break;
|
||||
case "helperdistancestam": v.HelperStaminaDistance = (float)(cell.AsDouble() * 240d); break;
|
||||
case "helperdistancemana": v.HelperManaDistance = (float)(cell.AsDouble() * 240d); break;
|
||||
case "helperdistancehitp": v.HelperHealthDistance = cell.AsDouble() * 240d; break;
|
||||
case "helperdistancestam": v.HelperStaminaDistance = cell.AsDouble() * 240d; break;
|
||||
case "helperdistancemana": v.HelperManaDistance = cell.AsDouble() * 240d; break;
|
||||
case "minimumringtargets": c.MinimumRingTargets = cell.AsInt(); break;
|
||||
case "defaultmeleeattackheight": c.AttackHeight = (PluginAttackHeight)cell.AsInt(); break;
|
||||
case "castdispelself": v.CastDispelSelf = cell.AsBool(); break;
|
||||
|
|
@ -252,9 +240,9 @@ internal static class VtankSettingsProfileSerializer
|
|||
case "targetlock": c.TargetLock = cell.AsBool(); break;
|
||||
case "stopmacroondeath": c.StopMacroOnDeath = cell.AsBool(); break;
|
||||
case "usearcs": c.UseArcs = (UseArcsMode)cell.AsInt(); break;
|
||||
case "arcrange": c.ArcRange = (float)(cell.AsDouble() * 240d); break;
|
||||
case "arcrange": c.ArcRange = cell.AsDouble() * 240d; break;
|
||||
case "targetselectmethod": c.SelectionMethod = (TargetSelectionMethod)(cell.AsInt() - 1); break;
|
||||
case "targetselectanglerange": c.TargetSelectAngleRange = (float)(cell.AsDouble() * 240d); break;
|
||||
case "targetselectanglerange": c.TargetSelectAngleRange = cell.AsDouble() * 240d; break;
|
||||
case "idlebufftopoff": b.IdleBuffTopoff = cell.AsBool(); break;
|
||||
case "idlebufftopofftimeseconds": b.IdleBuffTopoffSeconds = cell.AsDouble(); break;
|
||||
case "rebufftimeremainingseconds": b.RebuffWhenUnderSeconds = cell.AsDouble(); break;
|
||||
|
|
@ -323,7 +311,7 @@ internal static class VtankSettingsProfileSerializer
|
|||
case "blacklistcorpseopentimeoutseconds": i.Loot.BlacklistCorpseOpenTimeoutSeconds = cell.AsDouble(); break;
|
||||
case "summonpets": c.SummonPets = cell.AsBool(); break;
|
||||
case "petrangemode": c.PetRangeMode = (PetRangeMode)cell.AsInt(); break;
|
||||
case "petcustomrange": c.PetCustomRange = (float)(cell.AsDouble() * 240d); break;
|
||||
case "petcustomrange": c.PetCustomRange = cell.AsDouble() * 240d; break;
|
||||
case "petrefillcount-idle": c.PetRefillCountIdle = cell.AsInt(); break;
|
||||
case "petrefillcount-normal": c.PetRefillCountNormal = cell.AsInt(); break;
|
||||
case "corpseopentimeoutseconds": i.Loot.CorpseOpenTimeoutSeconds = cell.AsDouble(); break;
|
||||
|
|
@ -332,11 +320,11 @@ internal static class VtankSettingsProfileSerializer
|
|||
case "fastcastbuffs": b.FastCastBuffs = cell.AsBool(); break;
|
||||
case "usebreakableturnto": c.UseBreakableTurnTo = cell.AsBool(); break;
|
||||
case "useprojectileawareness": c.UseProjectileAwareness = cell.AsBool(); break;
|
||||
case "collisionprojectileradius": c.CollisionProjectileRadius = cell.AsFloat(); break;
|
||||
case "collisionstepdistance": c.CollisionStepDistance = cell.AsFloat(); break;
|
||||
case "collisionprojectileradius": c.CollisionProjectileRadius = cell.AsDouble(); break;
|
||||
case "collisionstepdistance": c.CollisionStepDistance = cell.AsDouble(); break;
|
||||
case "showcollisiondebug": c.ShowCollisionDebug = cell.AsBool(); break;
|
||||
case "maximumcollisioncheckspertick": c.MaximumCollisionChecksPerTick = cell.AsInt(); break;
|
||||
case "spellrangefudge": c.SpellRangeFudge = cell.AsFloat(); break;
|
||||
case "spellrangefudge": c.SpellRangeFudge = cell.AsDouble(); break;
|
||||
case "buffwithuntrained-item": b.BuffWithUntrainedItemSkill = cell.AsInt(); break;
|
||||
case "buffwithuntrained-creature": b.BuffWithUntrainedCreatureSkill = cell.AsInt(); break;
|
||||
case "buffwithuntrained-life": b.BuffWithUntrainedLifeSkill = cell.AsInt(); break;
|
||||
|
|
|
|||
|
|
@ -170,19 +170,14 @@ public sealed class VtankSettingsProfileSerializerTests
|
|||
break;
|
||||
case "d":
|
||||
case "f":
|
||||
// A handful of distance settings are declared tDouble but
|
||||
// still round-trip through a CombatSettings `float` field
|
||||
// for the physics math that consumes them (Apply's
|
||||
// `(float)(cell.AsDouble() * 240d)`); that costs precision
|
||||
// below float's ~7-significant-digit guarantee, not a real
|
||||
// value change. Bounded exactly like
|
||||
// Item I (slice-1 fix round): every tDouble-declared
|
||||
// distance setting's live field was widened from float to
|
||||
// double (CombatSettings.MaximumRange etc.), so this is now
|
||||
// a real exact compare — no tolerance, matching
|
||||
// VtankSettingsProfileSerializer's own ValuesEqual.
|
||||
double expected = fixtureCell.AsDouble();
|
||||
double actual = captured.AsDouble();
|
||||
double tolerance = Math.Max(Math.Abs(expected), Math.Abs(actual)) * 1.5e-7;
|
||||
Assert.True(
|
||||
Math.Abs(expected - actual) <= tolerance,
|
||||
$"{name}: fixture={expected} captured={actual}");
|
||||
Assert.Equal(
|
||||
fixtureCell.AsDouble(),
|
||||
captured.AsDouble());
|
||||
break;
|
||||
default:
|
||||
Assert.Fail($"{name}: unexpected tag '{fixtureCell.Tag}'.");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue