From 164a26e757e7a9546212b59eeb2df002fee0212b Mon Sep 17 00:00:00 2001 From: Kurt Date: Fri, 21 Sep 2018 21:20:28 -0700 Subject: [PATCH] Misc reorg + skeleton for slot editor SlotChangeManager is pretty glue-y, might eventually shift to this implementation --- .../Saves/Slots}/Extensions.cs | 0 .../Saves/Slots}/SlotChange.cs | 2 + .../Saves/Slots}/SlotChangeInfo.cs | 0 PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs | 224 ++++++++++++++++++ .../Editing/Saves/Slots/SlotTouchResult.cs | 33 +++ .../Editing/Saves/Slots/SlotTouchType.cs | 11 + .../Saves/Slots}/StorageSlotOffset.cs | 0 .../Saves/Slots}/StorageSlotType.cs | 0 PKHeX.Core/Saves/SAV7.cs | 4 +- 9 files changed, 272 insertions(+), 2 deletions(-) rename PKHeX.Core/{Saves/Storage => Editing/Saves/Slots}/Extensions.cs (100%) rename PKHeX.Core/{Saves/Storage => Editing/Saves/Slots}/SlotChange.cs (90%) rename PKHeX.Core/{Saves/Storage => Editing/Saves/Slots}/SlotChangeInfo.cs (100%) create mode 100644 PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs create mode 100644 PKHeX.Core/Editing/Saves/Slots/SlotTouchResult.cs create mode 100644 PKHeX.Core/Editing/Saves/Slots/SlotTouchType.cs rename PKHeX.Core/{Saves/Storage => Editing/Saves/Slots}/StorageSlotOffset.cs (100%) rename PKHeX.Core/{Saves/Storage => Editing/Saves/Slots}/StorageSlotType.cs (100%) diff --git a/PKHeX.Core/Saves/Storage/Extensions.cs b/PKHeX.Core/Editing/Saves/Slots/Extensions.cs similarity index 100% rename from PKHeX.Core/Saves/Storage/Extensions.cs rename to PKHeX.Core/Editing/Saves/Slots/Extensions.cs diff --git a/PKHeX.Core/Saves/Storage/SlotChange.cs b/PKHeX.Core/Editing/Saves/Slots/SlotChange.cs similarity index 90% rename from PKHeX.Core/Saves/Storage/SlotChange.cs rename to PKHeX.Core/Editing/Saves/Slots/SlotChange.cs index 032e4accd..95fd31518 100644 --- a/PKHeX.Core/Saves/Storage/SlotChange.cs +++ b/PKHeX.Core/Editing/Saves/Slots/SlotChange.cs @@ -25,5 +25,7 @@ public SlotChange(SlotChange info, SaveFile sav) Offset = info.Offset; PKM = sav.GetStoredSlot(info.Offset); } + + public SlotChange GetInverseData(SaveFile sav) => new SlotChange(this, sav); } } \ No newline at end of file diff --git a/PKHeX.Core/Saves/Storage/SlotChangeInfo.cs b/PKHeX.Core/Editing/Saves/Slots/SlotChangeInfo.cs similarity index 100% rename from PKHeX.Core/Saves/Storage/SlotChangeInfo.cs rename to PKHeX.Core/Editing/Saves/Slots/SlotChangeInfo.cs diff --git a/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs b/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs new file mode 100644 index 000000000..7b720a945 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs @@ -0,0 +1,224 @@ +using System.Collections.Generic; + +namespace PKHeX.Core +{ + /// + /// Pushes slot update notifications out to all subscribers. + /// + public sealed class SlotPublisher + { + /// + /// All instances that provide a view on individual content. + /// + public List Subscribers { get; } = new List(); + + private SlotChange Previous; + private SlotTouchType PreviousType = SlotTouchType.None; + + /// + /// Notifies all with the latest slot change details. + /// + /// Last interacted slot + /// Last interacted slot interaction type + public void NotifySlotChanged(SlotChange slot, SlotTouchType type) + { + foreach (var sub in Subscribers) + ResetView(sub, slot, type); + Previous = slot; + PreviousType = type; + } + + private void ResetView(ISlotViewer sub, SlotChange slot, SlotTouchType type) + { + if (Previous != null) + sub.NotifySlotOld(Previous); + + int index = sub.ViewIndex; + if (index == slot.Box) + sub.NotifySlotChanged(slot, type); + } + + public void ResetView(ISlotViewer sub) => ResetView(sub, Previous, PreviousType); + } + + public interface ISlotViewer + { + /// + /// Current index the viewer is viewing. + /// + int ViewIndex { get; } + + /// + /// Notification that the slot is no longer the last interacted slot. + /// + /// Last interacted slot + void NotifySlotOld(SlotChange previous); + + /// + /// Notification that the has just been interacted with. + /// + /// Last interacted slot + /// Last interacted slot interaction type + void NotifySlotChanged(SlotChange slot, SlotTouchType type); + } + + /// + /// Facilitates interaction with a or other data location's slot data. + /// + public sealed class SlotEditor + { + private readonly SaveFile SAV; + public SlotPublisher Publisher { get; } = new SlotPublisher(); + + private readonly Stack UndoStack = new Stack(); + private readonly Stack RedoStack = new Stack(); + + public SlotEditor(SaveFile sav) => SAV = sav; + private void NotifySlotChanged(SlotChange slot, SlotTouchType type) => Publisher.NotifySlotChanged(slot, type); + + /// + /// Gets data from a slot. + /// + /// Slot to retrieve from. + /// Operation succeeded or not via enum value. + public PKM Get(SlotChange slot) + { + // Reading from a slot is always allowed. + var pk = ReadSlot(slot); + NotifySlotChanged(slot, SlotTouchType.Get); + return pk; + } + + /// + /// Sets data to a slot. + /// + /// Slot to be set to. + /// Data to set. + /// Operation succeeded or not via enum value. + public SlotTouchResult Set(SlotChange slot, PKM pkm) + { + if (CantWrite(slot)) + return SlotTouchResult.FailWrite; + + WriteSlot(slot, pkm); + NotifySlotChanged(slot, SlotTouchType.Set); + + return SlotTouchResult.Success; + } + + /// + /// Deletes a slot. + /// + /// Slot to be deleted. + /// Operation succeeded or not via enum value. + public SlotTouchResult Delete(SlotChange slot) + { + if (CantWrite(slot)) + return SlotTouchResult.FailDelete; + + DeleteSlot(slot); + NotifySlotChanged(slot, SlotTouchType.Delete); + + return SlotTouchResult.Success; + } + + /// + /// Swaps two slots. + /// + /// Source slot to be switched with . + /// Destination slot to be switched with . + /// Operation succeeded or not via enum value. + public SlotTouchResult Swap(SlotChange source, SlotChange dest) + { + if (CantWrite(source)) + return SlotTouchResult.FailSource; + if (CantWrite(dest)) + return SlotTouchResult.FailDestination; + + NotifySlotChanged(source, SlotTouchType.None); + NotifySlotChanged(dest, SlotTouchType.Swap); + + return SlotTouchResult.Success; + } + + public bool CantWrite(SlotChange c) + { + if (c.Type > StorageSlotType.Party) + return true; + return SAV.IsSlotLocked(c.Box, c.Slot); + } + + private PKM ReadSlot(StorageSlotOffset slot) => SAV.GetPKM(slot); + + private void WriteSlot(SlotChange slot, PKM pkm) + { + if (slot.IsParty) + { + int count = SAV.PartyCount; + if (slot.Slot > count) + slot.Slot = count; + SAV.SetPartySlot(pkm, slot.Offset); + } + else + { + AddUndo(slot); + SAV.SetStoredSlot(pkm, slot.Offset); + } + } + + private void DeleteSlot(SlotChange slot) + { + var pkm = slot.PKM; + if (slot.IsParty) + { + SAV.SetPartySlot(pkm, slot.Offset); + slot.Slot = SAV.PartyCount; + } + else + { + AddUndo(slot); + SAV.SetStoredSlot(pkm, slot.Offset); + } + } + + public bool CanUndo => UndoStack.Count != 0; + public bool CanRedo => RedoStack.Count != 0; + + public void Undo() + { + if (UndoStack.Count == 0) + return; + + var change = UndoStack.Pop(); + if (change.Box < 0) + return; + AddRedo(change); + NotifySlotChanged(change, SlotTouchType.Set); + } + + public void Redo() + { + if (RedoStack.Count == 0) + return; + + var change = RedoStack.Pop(); + if (change.Box < 0) + return; + AddUndo(change); + NotifySlotChanged(change, SlotTouchType.Set); + } + + private void AddRedo(SlotChange change) + { + var slotChange = change.GetInverseData(SAV); + RedoStack.Push(slotChange); + } + + private void AddUndo(SlotChange change) + { + var slotChange = change.GetInverseData(SAV); + UndoStack.Push(slotChange); + RedoStack.Clear(); + } + } +} diff --git a/PKHeX.Core/Editing/Saves/Slots/SlotTouchResult.cs b/PKHeX.Core/Editing/Saves/Slots/SlotTouchResult.cs new file mode 100644 index 000000000..b6e1def76 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Slots/SlotTouchResult.cs @@ -0,0 +1,33 @@ +namespace PKHeX.Core +{ + /// + /// Result indicators for modifying a Slot within a or other data location. + /// + public enum SlotTouchResult + { + /// + /// Slot interaction was successful. + /// + Success, + + /// + /// Slot interaction failed to apply the data. + /// + FailWrite, + + /// + /// Slot interaction failed to delete the data. + /// + FailDelete, + + /// + /// Slot interaction failed due to a bad/unmodifiable source. + /// + FailSource, + + /// + /// Slot interaction failed due to a bad/unmodifiable destination. + /// + FailDestination, + } +} \ No newline at end of file diff --git a/PKHeX.Core/Editing/Saves/Slots/SlotTouchType.cs b/PKHeX.Core/Editing/Saves/Slots/SlotTouchType.cs new file mode 100644 index 000000000..ce141fb9b --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Slots/SlotTouchType.cs @@ -0,0 +1,11 @@ +namespace PKHeX.Core +{ + public enum SlotTouchType + { + None, + Get, + Set, + Delete, + Swap, + } +} \ No newline at end of file diff --git a/PKHeX.Core/Saves/Storage/StorageSlotOffset.cs b/PKHeX.Core/Editing/Saves/Slots/StorageSlotOffset.cs similarity index 100% rename from PKHeX.Core/Saves/Storage/StorageSlotOffset.cs rename to PKHeX.Core/Editing/Saves/Slots/StorageSlotOffset.cs diff --git a/PKHeX.Core/Saves/Storage/StorageSlotType.cs b/PKHeX.Core/Editing/Saves/Slots/StorageSlotType.cs similarity index 100% rename from PKHeX.Core/Saves/Storage/StorageSlotType.cs rename to PKHeX.Core/Editing/Saves/Slots/StorageSlotType.cs diff --git a/PKHeX.Core/Saves/SAV7.cs b/PKHeX.Core/Saves/SAV7.cs index 562d14b8c..1b7423a50 100644 --- a/PKHeX.Core/Saves/SAV7.cs +++ b/PKHeX.Core/Saves/SAV7.cs @@ -1230,7 +1230,7 @@ public override int PartyCount public override bool IsSlotLocked(int box, int slot) { - if (slot >= 30 || box >= BoxCount) + if ((uint)slot >= 30 || (uint)box >= BoxCount) return false; int slotIndex = slot + (BoxSlotCount * box); @@ -1239,7 +1239,7 @@ public override bool IsSlotLocked(int box, int slot) public override bool IsSlotInBattleTeam(int box, int slot) { - if (slot >= 30 || box >= BoxCount) + if ((uint)slot >= 30 || (uint)box >= BoxCount) return false; int slotIndex = slot + (BoxSlotCount * box);