mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-14 16:27:21 +00:00
Add slotview stuff
future implementation
This commit is contained in:
parent
4f1375ad6a
commit
f60926d200
9 changed files with 424 additions and 59 deletions
13
PKHeX.Core/Editing/Saves/SlotView/SlotArray.cs
Normal file
13
PKHeX.Core/Editing/Saves/SlotView/SlotArray.cs
Normal 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) { }
|
||||
}
|
||||
}
|
13
PKHeX.Core/Editing/Saves/SlotView/SlotBoxes.cs
Normal file
13
PKHeX.Core/Editing/Saves/SlotView/SlotBoxes.cs
Normal 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) { }
|
||||
}
|
||||
}
|
39
PKHeX.Core/Editing/Saves/SlotView/SlotDaycare.cs
Normal file
39
PKHeX.Core/Editing/Saves/SlotView/SlotDaycare.cs
Normal 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;
|
||||
}
|
||||
}
|
14
PKHeX.Core/Editing/Saves/SlotView/SlotList.cs
Normal file
14
PKHeX.Core/Editing/Saves/SlotView/SlotList.cs
Normal 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;
|
||||
}
|
||||
}
|
191
PKHeX.Core/Editing/Saves/SlotView/SlotView.cs
Normal file
191
PKHeX.Core/Editing/Saves/SlotView/SlotView.cs
Normal 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
|
||||
}
|
||||
}
|
88
PKHeX.Core/Editing/Saves/SlotView/SlotViewSet.cs
Normal file
88
PKHeX.Core/Editing/Saves/SlotView/SlotViewSet.cs
Normal 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);
|
||||
}
|
||||
}
|
23
PKHeX.Core/Editing/Saves/Slots/ISlotViewer.cs
Normal file
23
PKHeX.Core/Editing/Saves/Slots/ISlotViewer.cs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
43
PKHeX.Core/Editing/Saves/Slots/SlotPublisher.cs
Normal file
43
PKHeX.Core/Editing/Saves/Slots/SlotPublisher.cs
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue