From f60926d200752ad4583759b0907a603f10f0d793 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 12 Jan 2019 20:53:50 -0800 Subject: [PATCH] Add slotview stuff future implementation --- .../Editing/Saves/SlotView/SlotArray.cs | 13 ++ .../Editing/Saves/SlotView/SlotBoxes.cs | 13 ++ .../Editing/Saves/SlotView/SlotDaycare.cs | 39 ++++ PKHeX.Core/Editing/Saves/SlotView/SlotList.cs | 14 ++ PKHeX.Core/Editing/Saves/SlotView/SlotView.cs | 191 ++++++++++++++++++ .../Editing/Saves/SlotView/SlotViewSet.cs | 88 ++++++++ PKHeX.Core/Editing/Saves/Slots/ISlotViewer.cs | 23 +++ PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs | 59 ------ .../Editing/Saves/Slots/SlotPublisher.cs | 43 ++++ 9 files changed, 424 insertions(+), 59 deletions(-) create mode 100644 PKHeX.Core/Editing/Saves/SlotView/SlotArray.cs create mode 100644 PKHeX.Core/Editing/Saves/SlotView/SlotBoxes.cs create mode 100644 PKHeX.Core/Editing/Saves/SlotView/SlotDaycare.cs create mode 100644 PKHeX.Core/Editing/Saves/SlotView/SlotList.cs create mode 100644 PKHeX.Core/Editing/Saves/SlotView/SlotView.cs create mode 100644 PKHeX.Core/Editing/Saves/SlotView/SlotViewSet.cs create mode 100644 PKHeX.Core/Editing/Saves/Slots/ISlotViewer.cs create mode 100644 PKHeX.Core/Editing/Saves/Slots/SlotPublisher.cs diff --git a/PKHeX.Core/Editing/Saves/SlotView/SlotArray.cs b/PKHeX.Core/Editing/Saves/SlotView/SlotArray.cs new file mode 100644 index 000000000..e48d2af4a --- /dev/null +++ b/PKHeX.Core/Editing/Saves/SlotView/SlotArray.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace PKHeX.Core +{ + public class SlotArray : SlotList + { + public SlotArray(SaveFile sav, IReadOnlyList slots, bool isReadOnly) + : base(sav, slots, isReadOnly) { } + + protected override void ShiftDown(int startIndex) { } + protected override void ShiftUp(int startIndex) { } + } +} \ No newline at end of file diff --git a/PKHeX.Core/Editing/Saves/SlotView/SlotBoxes.cs b/PKHeX.Core/Editing/Saves/SlotView/SlotBoxes.cs new file mode 100644 index 000000000..cba21347f --- /dev/null +++ b/PKHeX.Core/Editing/Saves/SlotView/SlotBoxes.cs @@ -0,0 +1,13 @@ +namespace PKHeX.Core +{ + public class SlotBoxes : SlotView + { + public SlotBoxes(SaveFile sav) + : base(sav, sav.SlotCount, false) { } + + protected override int GetOffset(int index) => SAV.GetBoxSlotOffset(index); + + protected override void ShiftDown(int startIndex) { } + protected override void ShiftUp(int startIndex) { } + } +} \ No newline at end of file diff --git a/PKHeX.Core/Editing/Saves/SlotView/SlotDaycare.cs b/PKHeX.Core/Editing/Saves/SlotView/SlotDaycare.cs new file mode 100644 index 000000000..ca4ee2252 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/SlotView/SlotDaycare.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; + +namespace PKHeX.Core +{ + public class SlotDaycare : SlotView + { + private readonly List Slots = new List(); + + public SlotDaycare(SaveFile sav, bool locked) + : base(sav, (int) 2, locked) + { + ReloadDaycareSlots(); + } + + private void ReloadDaycareSlots() + { + var s1 = SAV.GetDaycareSlotOffset(CurrentDaycare, 0); + if (s1 < 0) + return; + Slots.Add(new StorageSlotOffset { IsPartyFormat = false, Offset = s1, Type = StorageSlotType.Daycare }); + var s2 = SAV.GetDaycareSlotOffset(CurrentDaycare, 1); + if (s2 < 0) + return; + Slots.Add(new StorageSlotOffset { IsPartyFormat = false, Offset = s2, Type = StorageSlotType.Daycare }); + Capacity = Slots.Count; + } + + public int CurrentDaycare { get; private set; } + public bool CanSwitch => SAV.HasTwoDaycares; + + public void SwitchCurrent() + { + CurrentDaycare ^= 1; + ReloadDaycareSlots(); + } + + protected override int GetOffset(int index) => Slots[index].Offset; + } +} \ No newline at end of file diff --git a/PKHeX.Core/Editing/Saves/SlotView/SlotList.cs b/PKHeX.Core/Editing/Saves/SlotView/SlotList.cs new file mode 100644 index 000000000..2c8e9c833 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/SlotView/SlotList.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace PKHeX.Core +{ + public class SlotList : SlotView + { + private readonly IReadOnlyList Slots; + + public SlotList(SaveFile sav, IReadOnlyList slots, bool isReadOnly) + : base(sav, slots.Count, isReadOnly) => Slots = slots; + + protected override int GetOffset(int index) => Slots[index].Offset; + } +} \ No newline at end of file diff --git a/PKHeX.Core/Editing/Saves/SlotView/SlotView.cs b/PKHeX.Core/Editing/Saves/SlotView/SlotView.cs new file mode 100644 index 000000000..ebe9e4b4d --- /dev/null +++ b/PKHeX.Core/Editing/Saves/SlotView/SlotView.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace PKHeX.Core +{ + public abstract class SlotView : IList + { + public static readonly SlotArray Empty = new SlotArray(null, Array.Empty(), true); + + protected readonly SaveFile SAV; + public StorageSlotType Type { get; internal set; } + public bool IsParty { get; internal set; } + + public int Capacity { get; protected set; } + public bool IsReadOnly { get; } + + public bool Full => Count == Capacity; + public decimal PercentFull => Count / (decimal)Capacity; + public IEnumerable Indexes => Enumerable.Range(0, Capacity); + public IEnumerable Offsets => Indexes.Select(GetOffset); + public IEnumerable Stream => Indexes.Select(z => this[z]); + public PKM[] Items => Stream.ToArray(); + + internal List Changes { private get; set; } + + protected abstract int GetOffset(int index); + + protected bool IsPKMPresent(int index) + { + int offset = GetOffset(index); + return SAV.IsPKMPresent(offset); + } + + public int FirstEmptyIndex(int start = 0) + { + for (int i = start; i < Capacity; i++) + { + int offset = GetOffset(i); + if (!IsPKMPresent(offset)) + return i; + } + return -1; + } + + protected SlotView(SaveFile sav, int capacity, bool isReadOnly) + { + SAV = sav; + IsReadOnly = isReadOnly; + Capacity = capacity; + } + + #region IList Implementation + + public virtual int Count => Offsets.Count(SAV.IsPKMPresent); + public bool Contains(PKM item) => IndexOf(item) > 0; + public IEnumerator GetEnumerator() => Stream.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => Stream.GetEnumerator(); + + public PKM this[int index] + { + get => GetIndex(index); + set => SetIndex(value, index); + } + + protected void SetIndex(PKM value, int index) + { + if (IsReadOnly) + throw new ArgumentException(nameof(IsReadOnly)); + + int offset = GetOffset(index); + + Changes.Add(GetUndo(index, offset)); + + if (!IsParty) + { + SAV.SetStoredSlot(value, offset); + return; + } + + value.ForcePartyData(); + SAV.SetPartySlot(value, offset); + } + + private SlotChange GetUndo(int index, int offset) + { + return new SlotChange + { + IsPartyFormat = IsParty, + Offset = offset, + Editable = IsReadOnly, + PKM = GetFromOffset(offset), + Box = index, + Type = Type, + }; + } + + private PKM GetIndex(int index) => GetFromOffset(GetOffset(index)); + private PKM GetFromOffset(int offset) => IsParty ? SAV.GetPartySlot(offset) : SAV.GetStoredSlot(offset); + + public void Insert(int index, PKM item) + { + int offset = GetOffset(index); + if (SAV.IsPKMPresent(offset)) + ShiftDown(index); + this[index] = item; + } + + public void RemoveAt(int index) + { + int offset = GetOffset(index); + if (SAV.IsPKMPresent(offset)) + this[index] = SAV.BlankPKM; + ShiftUp(index); + } + + /// + /// Adds the item to the first empty index. + /// + public void Add(PKM item) + { + var index = FirstEmptyIndex(); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index)); + this[index] = item; + } + + public void Clear() + { + var blank = SAV.BlankPKM; + if (blank == null) + throw new ArgumentException(nameof(blank)); + for (int i = 0; i < Capacity; i++) + this[i] = blank; + } + + public bool Remove(PKM item) + { + var index = IndexOf(item); + if (index < 0) + return false; + RemoveAt(index); + return true; + } + + public virtual void CopyTo(PKM[] array, int arrayIndex) + { + for (int i = arrayIndex, ctr = 0; i < Capacity; i++, ctr++) + array[i] = this[ctr]; + } + + public int IndexOf(PKM item) + { + int ctr = 0; + foreach (var obj in Stream) + { + if (obj.Equals(item)) + return ctr; + ctr++; + } + return -1; + } + + protected virtual void ShiftUp(int startIndex) + { + // something was deleted, move all slots up starting at index + var end = FirstEmptyIndex(startIndex + 1); + if (end < 0) + end = Capacity - 1; + + for (int i = startIndex; i < end; i++) + this[i] = this[i + 1]; + this[Capacity - 1] = SAV.BlankPKM; + } + + protected virtual void ShiftDown(int startIndex) + { + // something needs to be inserted, move all slots down starting at index + var end = FirstEmptyIndex(startIndex + 1); + if (end < 0) + end = Capacity - 1; + + for (int i = end; i >= startIndex; i--) + this[i + 1] = this[i]; + this[Capacity - 1] = SAV.BlankPKM; + } + + #endregion + } +} diff --git a/PKHeX.Core/Editing/Saves/SlotView/SlotViewSet.cs b/PKHeX.Core/Editing/Saves/SlotView/SlotViewSet.cs new file mode 100644 index 000000000..10c35000d --- /dev/null +++ b/PKHeX.Core/Editing/Saves/SlotView/SlotViewSet.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Linq; + +namespace PKHeX.Core +{ + public class SlotViewSet + { + public readonly SlotView Box = SlotView.Empty; + public readonly SlotView Party = SlotView.Empty; + public readonly SlotView BattleBox = SlotView.Empty; + public readonly SlotView Daycare = SlotView.Empty; + public readonly SlotView[] Other; + + public readonly SaveFile SAV; + + private const int StackMax = 10; + private readonly List Changes = new List(StackMax); + + public SlotViewSet(SaveFile sav, bool HaX = false) + { + SAV = sav; + + if (sav.HasBox) + Box = new SlotBoxes(sav) { Type = StorageSlotType.Box }; + + if (sav.HasParty) + Party = GetPartyList(sav); + + if (sav.HasBattleBox) + BattleBox = GetBattleBox(sav); + + if (sav.HasDaycare) + Daycare = new SlotDaycare(sav, !sav.BattleBoxLocked); + Other = GetOtherSlots(sav, HaX); + + SetChangeOutput(); + } + + private void SetChangeOutput() + { + foreach (var v in Viewers) + v.Changes = Changes; + } + + private static SlotView[] GetOtherSlots(SaveFile sav, bool HaX) + { + return sav.GetExtraSlots(HaX).GroupBy(z => z.Type) + .Select(z => new SlotArray(sav, z.ToArray(), true) { Type = z.Key }) + .Cast().ToArray(); + } + + private static SlotList GetBattleBox(SaveFile sav) + { + var party = Enumerable.Range(0, 6).Select(sav.GetBattleBoxOffset) + .Select(z => GetStorageDetails(z, false, StorageSlotType.BattleBox)) + .ToArray(); + return new SlotList(sav, party, !sav.BattleBoxLocked) { Type = StorageSlotType.BattleBox }; + } + + private static SlotList GetPartyList(SaveFile sav) + { + var party = Enumerable.Range(0, 6).Select(sav.GetPartyOffset) + .Select(z => GetStorageDetails(z, true, StorageSlotType.Party)) + .ToArray(); + return new SlotList(sav, party, false) { Type = StorageSlotType.Party }; + } + + private static StorageSlotOffset GetStorageDetails(int z, bool party, StorageSlotType type) + { + return new StorageSlotOffset {IsPartyFormat = party, Offset = z, Type = type}; + } + + public IEnumerable Viewers + { + get + { + yield return Box; + yield return Party; + yield return BattleBox; + yield return Daycare; + foreach (var o in Other) + yield return o; + } + } + + public IEnumerable AllPKM => Viewers.SelectMany(z => z).Where(z => z.Species > 0 && z.ChecksumValid); + } +} \ No newline at end of file diff --git a/PKHeX.Core/Editing/Saves/Slots/ISlotViewer.cs b/PKHeX.Core/Editing/Saves/Slots/ISlotViewer.cs new file mode 100644 index 000000000..f32506a3d --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Slots/ISlotViewer.cs @@ -0,0 +1,23 @@ +namespace PKHeX.Core +{ + 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); + } +} \ No newline at end of file diff --git a/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs b/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs index 7838c2e3a..219450193 100644 --- a/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs +++ b/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs @@ -2,65 +2,6 @@ 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. diff --git a/PKHeX.Core/Editing/Saves/Slots/SlotPublisher.cs b/PKHeX.Core/Editing/Saves/Slots/SlotPublisher.cs new file mode 100644 index 000000000..73c9925d8 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Slots/SlotPublisher.cs @@ -0,0 +1,43 @@ +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); + } +}