Add slotview stuff

future implementation
This commit is contained in:
Kurt 2019-01-12 20:53:50 -08:00
parent 4f1375ad6a
commit f60926d200
9 changed files with 424 additions and 59 deletions

View file

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace PKHeX.Core
{
public class SlotArray : SlotList
{
public SlotArray(SaveFile sav, IReadOnlyList<StorageSlotOffset> slots, bool isReadOnly)
: base(sav, slots, isReadOnly) { }
protected override void ShiftDown(int startIndex) { }
protected override void ShiftUp(int startIndex) { }
}
}

View file

@ -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) { }
}
}

View file

@ -0,0 +1,39 @@
using System.Collections.Generic;
namespace PKHeX.Core
{
public class SlotDaycare : SlotView
{
private readonly List<StorageSlotOffset> Slots = new List<StorageSlotOffset>();
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;
}
}

View file

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace PKHeX.Core
{
public class SlotList : SlotView
{
private readonly IReadOnlyList<StorageSlotOffset> Slots;
public SlotList(SaveFile sav, IReadOnlyList<StorageSlotOffset> slots, bool isReadOnly)
: base(sav, slots.Count, isReadOnly) => Slots = slots;
protected override int GetOffset(int index) => Slots[index].Offset;
}
}

View file

@ -0,0 +1,191 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
public abstract class SlotView : IList<PKM>
{
public static readonly SlotArray Empty = new SlotArray(null, Array.Empty<StorageSlotOffset>(), 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<int> Indexes => Enumerable.Range(0, Capacity);
public IEnumerable<int> Offsets => Indexes.Select(GetOffset);
public IEnumerable<PKM> Stream => Indexes.Select(z => this[z]);
public PKM[] Items => Stream.ToArray();
internal List<SlotChange> 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<PKM> 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);
}
/// <summary>
/// Adds the item to the first empty index.
/// </summary>
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
}
}

View file

@ -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<SlotChange> Changes = new List<SlotChange>(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<SlotView>().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<SlotView> Viewers
{
get
{
yield return Box;
yield return Party;
yield return BattleBox;
yield return Daycare;
foreach (var o in Other)
yield return o;
}
}
public IEnumerable<PKM> AllPKM => Viewers.SelectMany(z => z).Where(z => z.Species > 0 && z.ChecksumValid);
}
}

View file

@ -0,0 +1,23 @@
namespace PKHeX.Core
{
public interface ISlotViewer
{
/// <summary>
/// Current index the viewer is viewing.
/// </summary>
int ViewIndex { get; }
/// <summary>
/// Notification that the <see cref="previous"/> slot is no longer the last interacted slot.
/// </summary>
/// <param name="previous">Last interacted slot</param>
void NotifySlotOld(SlotChange previous);
/// <summary>
/// Notification that the <see cref="slot"/> has just been interacted with.
/// </summary>
/// <param name="slot">Last interacted slot</param>
/// <param name="type">Last interacted slot interaction type</param>
void NotifySlotChanged(SlotChange slot, SlotTouchType type);
}
}

View file

@ -2,65 +2,6 @@
namespace PKHeX.Core
{
/// <summary>
/// Pushes slot update notifications out to all subscribers.
/// </summary>
public sealed class SlotPublisher
{
/// <summary>
/// All <see cref="ISlotViewer"/> instances that provide a view on individual <see cref="StorageSlotOffset"/> content.
/// </summary>
public List<ISlotViewer> Subscribers { get; } = new List<ISlotViewer>();
private SlotChange Previous;
private SlotTouchType PreviousType = SlotTouchType.None;
/// <summary>
/// Notifies all <see cref="Subscribers"/> with the latest slot change details.
/// </summary>
/// <param name="slot">Last interacted slot</param>
/// <param name="type">Last interacted slot interaction type</param>
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
{
/// <summary>
/// Current index the viewer is viewing.
/// </summary>
int ViewIndex { get; }
/// <summary>
/// Notification that the <see cref="previous"/> slot is no longer the last interacted slot.
/// </summary>
/// <param name="previous">Last interacted slot</param>
void NotifySlotOld(SlotChange previous);
/// <summary>
/// Notification that the <see cref="slot"/> has just been interacted with.
/// </summary>
/// <param name="slot">Last interacted slot</param>
/// <param name="type">Last interacted slot interaction type</param>
void NotifySlotChanged(SlotChange slot, SlotTouchType type);
}
/// <summary>
/// Facilitates interaction with a <see cref="SaveFile"/> or other data location's slot data.

View file

@ -0,0 +1,43 @@
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <summary>
/// Pushes slot update notifications out to all subscribers.
/// </summary>
public sealed class SlotPublisher
{
/// <summary>
/// All <see cref="ISlotViewer"/> instances that provide a view on individual <see cref="StorageSlotOffset"/> content.
/// </summary>
public List<ISlotViewer> Subscribers { get; } = new List<ISlotViewer>();
private SlotChange Previous;
private SlotTouchType PreviousType = SlotTouchType.None;
/// <summary>
/// Notifies all <see cref="Subscribers"/> with the latest slot change details.
/// </summary>
/// <param name="slot">Last interacted slot</param>
/// <param name="type">Last interacted slot interaction type</param>
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);
}
}