diff --git a/Models/ReactionTest.cs b/Models/ReactionTest.cs --- a/Models/ReactionTest.cs +++ b/Models/ReactionTest.cs @@ -24,29 +24,51 @@ namespace DesertPaintCodex.Models public Reagent Reagent1 { get; } public Reagent Reagent2 { get; } - private Reagent? _bufferReagent; - public Reagent? BufferReagent + private Reagent? _bufferReagentFirst; + public Reagent? BufferReagentFirst { - get => _bufferReagent; + get => _bufferReagentFirst; + set + { + if (_bufferReagentFirst == value) return; + _bufferReagentFirst = value; + + UpdateRecipe(); + + NotifyPropertyChanged(nameof(BufferReagentFirst)); + NotifyPropertyChanged(nameof(CanScan)); + } + } + + private Reagent? _bufferReagentLast; + public Reagent? BufferReagentLast + { + get => _bufferReagentLast; set { - if (_bufferReagent == value) return; - _bufferReagent = value; - _recipe.Clear(); - if (_bufferReagent == null) - { - _recipe.AddReagent(Reagent1.Name); - _recipe.AddReagent(Reagent2.Name); - } - else - { - _recipe.AddReagent(_bufferReagent.Name); - _recipe.AddReagent(Reagent1.Name); - _recipe.AddReagent(Reagent2.Name); - } - NotifyPropertyChanged(nameof(BufferReagent)); + if (_bufferReagentLast == value) return; + _bufferReagentLast = value; + + UpdateRecipe(); + + NotifyPropertyChanged(nameof(BufferReagentLast)); NotifyPropertyChanged(nameof(CanScan)); - UpdateHypotheticalColor(); + } + } + + private bool _useFirstBuffer; + + public bool UseFirstBuffer + { + get => _useFirstBuffer; + set + { + _useFirstBuffer = value; + UpdateRecipe(); + NotifyPropertyChanged(nameof(UseFirstBuffer)); + NotifyPropertyChanged(nameof(CanScan)); + NotifyPropertyChanged(nameof(ShowFirstBuffer)); + NotifyPropertyChanged(nameof(ShowLastBuffer)); } } @@ -90,13 +112,15 @@ namespace DesertPaintCodex.Models NotifyPropertyChanged(nameof(CanSave)); NotifyPropertyChanged(nameof(NoLab)); NotifyPropertyChanged(nameof(CanPickBuffer)); + NotifyPropertyChanged(nameof(ShowFirstBuffer)); + NotifyPropertyChanged(nameof(ShowLastBuffer)); } } public bool Requires3Way => (State == TestState.ClippedResult) || IsAllCatalysts; - public bool CanScan => (State is TestState.Untested or TestState.LabNotFound) || ((State == TestState.ClippedResult) && (BufferReagent != null)); + public bool CanScan => (State is TestState.Untested or TestState.LabNotFound) || ((State == TestState.ClippedResult) && BufferIsSelected); public bool IsScanning => State == TestState.Scanning; @@ -112,6 +136,10 @@ namespace DesertPaintCodex.Models public bool CanPickBuffer => (State == TestState.ClippedResult) || IsAllCatalysts; + public bool ShowFirstBuffer => Requires3Way && UseFirstBuffer; + + public bool ShowLastBuffer => Requires3Way && !UseFirstBuffer; + private PaintColor? _hypotheticalColor; public PaintColor? HypotheticalColor { get => _hypotheticalColor; set { _hypotheticalColor = value; NotifyPropertyChanged(nameof(HypotheticalColor)); } } @@ -128,6 +156,7 @@ namespace DesertPaintCodex.Models { Reagent1 = reagent1; Reagent2 = reagent2; + UseFirstBuffer = true; IsAllCatalysts = reagent1.IsCatalyst && reagent2.IsCatalyst; Clipped = clipType; Reaction = reaction; @@ -154,6 +183,7 @@ namespace DesertPaintCodex.Models ObservedColor = ReactionScannerService.Instance.RecordedColor; if (_observedColor != null) { + // Handle the normal case. Clipped = _observedColor.Red switch { 0 => ClipType.RedLow, @@ -232,7 +262,7 @@ namespace DesertPaintCodex.Models profile.Save(); State = TestState.Saved; } - + #endregion #region Internals @@ -243,11 +273,23 @@ namespace DesertPaintCodex.Models if (HypotheticalColor == null) return null; if (ObservedColor == null) return null; - if (BufferReagent != null) + if (_useFirstBuffer) { - return ReactionScannerService.Calculate3WayReaction(ProfileManager.CurrentProfile, HypotheticalColor, - ObservedColor, BufferReagent, Reagent1, Reagent2); + if (BufferReagentFirst != null) + { + return ReactionScannerService.Calculate3WayReaction(ProfileManager.CurrentProfile, HypotheticalColor, + ObservedColor, BufferReagentFirst, Reagent1, Reagent2, true); + } } + else + { + if (BufferReagentLast != null) + { + return ReactionScannerService.Calculate3WayReaction(ProfileManager.CurrentProfile, HypotheticalColor, + ObservedColor, Reagent1, Reagent2, BufferReagentLast, false); + } + } + return ReactionScannerService.CalculateReaction(HypotheticalColor, ObservedColor); } @@ -255,7 +297,45 @@ namespace DesertPaintCodex.Models private void UpdateHypotheticalColor() { HypotheticalColor = null; - HypotheticalColor = (IsAllCatalysts && (BufferReagent == null)) ? null : _recipe.BaseColor; + HypotheticalColor = (IsAllCatalysts && !BufferIsSelected) ? null : _recipe.BaseColor; + } + + private bool BufferIsSelected => UseFirstBuffer ? (BufferReagentFirst != null) : (BufferReagentLast != null); + + + private void UpdateRecipe() + { + _recipe.Clear(); + if (_useFirstBuffer) + { + if (_bufferReagentFirst == null) + { + _recipe.AddReagent(Reagent1.Name); + _recipe.AddReagent(Reagent2.Name); + } + else + { + _recipe.AddReagent(_bufferReagentFirst.Name); + _recipe.AddReagent(Reagent1.Name); + _recipe.AddReagent(Reagent2.Name); + } + } + else + { + if (_bufferReagentLast == null) + { + _recipe.AddReagent(Reagent1.Name); + _recipe.AddReagent(Reagent2.Name); + } + else + { + _recipe.AddReagent(Reagent1.Name); + _recipe.AddReagent(Reagent2.Name); + _recipe.AddReagent(_bufferReagentLast.Name); + } + } + + UpdateHypotheticalColor(); } #endregion diff --git a/Services/ReactionScannerService.cs b/Services/ReactionScannerService.cs --- a/Services/ReactionScannerService.cs +++ b/Services/ReactionScannerService.cs @@ -473,11 +473,22 @@ namespace DesertPaintCodex.Services } - public static Reaction Calculate3WayReaction(PlayerProfile profile, PaintColor expectedColor, PaintColor reactedColor, Reagent reagent0, Reagent reagent1, Reagent reagent2) + public static Reaction Calculate3WayReaction(PlayerProfile profile, PaintColor expectedColor, PaintColor reactedColor, Reagent reagent0, Reagent reagent1, Reagent reagent2, bool firstBuffer) { // A 3-reagent reaction. - Reaction? reaction1 = profile.FindReaction(reagent0, reagent1); - Reaction? reaction2 = profile.FindReaction(reagent0, reagent2); + Reaction? reaction1; + Reaction? reaction2; + + if (firstBuffer) + { + reaction1 = profile.FindReaction(reagent0, reagent1); + reaction2 = profile.FindReaction(reagent0, reagent2); + } + else + { + reaction1 = profile.FindReaction(reagent0, reagent2); + reaction2 = profile.FindReaction(reagent1, reagent2); + } Debug.Assert(reaction1 != null); Debug.Assert(reaction2 != null); diff --git a/ViewModels/ReactionTestViewModel.cs b/ViewModels/ReactionTestViewModel.cs --- a/ViewModels/ReactionTestViewModel.cs +++ b/ViewModels/ReactionTestViewModel.cs @@ -24,8 +24,8 @@ namespace DesertPaintCodex.ViewModels private readonly List _allPigmentList = new(); private readonly List _allReagentList = new(); - public ObservableCollection BufferList { get; } = new(); - + public ObservableCollection FirstBufferList { get; } = new(); + public ObservableCollection LastBufferList { get; } = new(); public ReactionTestViewModel() { @@ -71,16 +71,24 @@ namespace DesertPaintCodex.ViewModels PlayerProfile? profile = ProfileManager.CurrentProfile; if (profile == null) return; ReactionSet reactions = profile.Reactions; - BufferList.Clear(); List bufferList = ReactionTest.IsAllCatalysts ? _allPigmentList : _allReagentList; - foreach (Reagent pigment in bufferList) + foreach (Reagent buffer in bufferList) { - if (pigment == ReactionTest.Reagent1) continue; - if (pigment == ReactionTest.Reagent2) continue; - if (reactions.Find(pigment, ReactionTest.Reagent1) == null) continue; - if (reactions.Find(pigment, ReactionTest.Reagent2) == null) continue; - - BufferList.Add(pigment); + if (buffer == ReactionTest.Reagent1) continue; + if (buffer == ReactionTest.Reagent2) continue; + + + if ((reactions.Find(buffer, ReactionTest.Reagent1) != null) + && (reactions.Find(buffer, ReactionTest.Reagent2) != null)) + { + FirstBufferList.Add(buffer); + } + + if ((reactions.Find(ReactionTest.Reagent1, buffer) != null) + && (reactions.Find(ReactionTest.Reagent2, buffer) != null)) + { + LastBufferList.Add(buffer); + } } // BufferPigmentList.AddRange(_allPigmentList.Where(pigment => @@ -120,6 +128,16 @@ namespace DesertPaintCodex.ViewModels { await ShowInfoBox("Clipped Reactions", "The Pigment Lab is only capable of displaying color channel values in the 0-255 range. However, sometimes, your reactions will push one or more channels outside of that range. When that happens, the value will be clamped either to 0 or 255. In this case, we say that the reaction was \"Clipped.\"\n\nThis is nothing to panic about, though. We solve this issue by using a third (buffer) reagent to offset the extreme value, so that it falls within measurable range, similar to how we test Catalyst+Catalyst reactions. Desert Paint Codex will automatically do the math for this, but it does move clipped reactions towards the end of your test list, because you'll want any reactions between the buffer reagent and your two test reagents already known, prior to running your buffered test."); } + + public void UseFirstBuffer() + { + ReactionTest.UseFirstBuffer = true; + } + + public void UseLastBuffer() + { + ReactionTest.UseFirstBuffer = false; + } private void Test() { diff --git a/Views/ReactionTestView.axaml b/Views/ReactionTestView.axaml --- a/Views/ReactionTestView.axaml +++ b/Views/ReactionTestView.axaml @@ -20,6 +20,10 @@ + +