mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
c#10: readonly record structs
Reduces some boilerplate constructors/equality compares
This commit is contained in:
parent
f55c5bea66
commit
88ddc5822e
17 changed files with 67 additions and 290 deletions
|
@ -1,22 +1,3 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public readonly struct BoxManipParam
|
||||
{
|
||||
public readonly int Start;
|
||||
public readonly int Stop;
|
||||
public readonly bool Reverse;
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public BoxManipParam(int start, int stop, bool reverse = false)
|
||||
{
|
||||
Start = start;
|
||||
Stop = stop;
|
||||
Reverse = reverse;
|
||||
}
|
||||
|
||||
public bool Equals(BoxManipParam p) => p.Start == Start && p.Stop == Stop && p.Reverse == Reverse;
|
||||
public override bool Equals(object obj) => obj is BoxManipParam p && Equals(p);
|
||||
public override int GetHashCode() => -1;
|
||||
public static bool operator ==(BoxManipParam left, BoxManipParam right) => left.Equals(right);
|
||||
public static bool operator !=(BoxManipParam left, BoxManipParam right) => !(left == right);
|
||||
}
|
||||
}
|
||||
public readonly record struct BoxManipParam(int Start, int Stop, bool Reverse = false);
|
||||
|
|
|
@ -123,9 +123,9 @@ namespace PKHeX.Core
|
|||
return Array.Empty<string>();
|
||||
var ew = ((SAV7b)S1).Blocks.EventWork;
|
||||
|
||||
var fOn = SetFlags.Select(z => new FlagSummary(z, ew).ToString());
|
||||
var fOff = ClearedFlags.Select(z => new FlagSummary(z, ew).ToString());
|
||||
var wt = WorkChanged.Select((z, i) => new WorkSummary(z, ew, WorkDiff[i]).ToString());
|
||||
var fOn = SetFlags.Select(z => FlagSummary.Get(z, ew).ToString());
|
||||
var fOff = ClearedFlags.Select(z => FlagSummary.Get(z, ew).ToString());
|
||||
var wt = WorkChanged.Select((z, i) => WorkSummary.Get(z, ew, WorkDiff[i]).ToString());
|
||||
|
||||
var list = new List<string> { "Flags: ON", "=========" };
|
||||
list.AddRange(fOn);
|
||||
|
@ -149,37 +149,25 @@ namespace PKHeX.Core
|
|||
return list;
|
||||
}
|
||||
|
||||
private readonly struct FlagSummary
|
||||
private readonly record struct FlagSummary(EventVarType Type, int Index, int Raw)
|
||||
{
|
||||
private EventVarType Type { get; }
|
||||
private int Index { get; }
|
||||
private int Raw { get; }
|
||||
|
||||
public override string ToString() => $"{Raw:0000}\t{false}\t{Index:0000}\t{Type}";
|
||||
|
||||
public FlagSummary(int rawIndex, EventWork7b ew)
|
||||
|
||||
public static FlagSummary Get(int rawIndex, EventWork7b ew)
|
||||
{
|
||||
Type = ew.GetFlagType(rawIndex, out var subIndex);
|
||||
Index = subIndex;
|
||||
Raw = rawIndex;
|
||||
var type = ew.GetFlagType(rawIndex, out var subIndex);
|
||||
return new FlagSummary(type, subIndex, rawIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct WorkSummary
|
||||
private readonly record struct WorkSummary(EventVarType Type, int Index, int Raw, string Text)
|
||||
{
|
||||
private EventVarType Type { get; }
|
||||
private int Index { get; }
|
||||
private int Raw { get; }
|
||||
private string Text { get; }
|
||||
|
||||
public override string ToString() => $"{Raw:000}\t{Text}\t{Index:000}\t{Type}";
|
||||
|
||||
public WorkSummary(int rawIndex, EventWork7b ew, string text)
|
||||
public static WorkSummary Get(int rawIndex, EventWork7b ew, string text)
|
||||
{
|
||||
Type = ew.GetFlagType(rawIndex, out var subIndex);
|
||||
Index = subIndex;
|
||||
Raw = rawIndex;
|
||||
Text = text;
|
||||
var type = ew.GetFlagType(rawIndex, out var subIndex);
|
||||
return new WorkSummary(type, subIndex, rawIndex, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,30 +254,14 @@ namespace PKHeX.Core
|
|||
return list;
|
||||
}
|
||||
|
||||
private readonly struct FlagSummary
|
||||
private readonly record struct FlagSummary(int Raw)
|
||||
{
|
||||
private int Raw { get; }
|
||||
|
||||
public override string ToString() => $"{Raw:0000}\t{false}";
|
||||
|
||||
public FlagSummary(int rawIndex)
|
||||
{
|
||||
Raw = rawIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct WorkSummary
|
||||
private readonly record struct WorkSummary(int Raw, string Text)
|
||||
{
|
||||
private int Raw { get; }
|
||||
private string Text { get; }
|
||||
|
||||
public override string ToString() => $"{Raw:000}\t{Text}";
|
||||
|
||||
public WorkSummary(int rawIndex, string text)
|
||||
{
|
||||
Raw = rawIndex;
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,16 +102,6 @@ namespace PKHeX.Core
|
|||
result[i] = 0;
|
||||
}
|
||||
|
||||
private readonly struct MoveOrder
|
||||
{
|
||||
public readonly ushort Move;
|
||||
public readonly byte Source;
|
||||
|
||||
public MoveOrder(ushort move, byte source)
|
||||
{
|
||||
Move = move;
|
||||
Source = source;
|
||||
}
|
||||
}
|
||||
private readonly record struct MoveOrder(ushort Move, byte Source);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,24 +20,4 @@ namespace PKHeX.Core
|
|||
Source = source;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct LearnVersion
|
||||
{
|
||||
public readonly GameVersion Game;
|
||||
public readonly int Level;
|
||||
|
||||
public LearnVersion(int lv, GameVersion game = GameVersion.Any)
|
||||
{
|
||||
Game = game;
|
||||
Level = lv;
|
||||
}
|
||||
|
||||
public bool IsLevelUp => Level >= 0;
|
||||
public bool Equals(LearnVersion v) => v.Game == Game && v.Level == Level;
|
||||
|
||||
public override bool Equals(object obj) => obj is LearnVersion v && Equals(v);
|
||||
public override int GetHashCode() => -1;
|
||||
public static bool operator ==(LearnVersion left, LearnVersion right) => left.Equals(right);
|
||||
public static bool operator !=(LearnVersion left, LearnVersion right) => !(left == right);
|
||||
}
|
||||
}
|
||||
|
|
6
PKHeX.Core/Legality/Moves/LearnVersion.cs
Normal file
6
PKHeX.Core/Legality/Moves/LearnVersion.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
public readonly record struct LearnVersion(int Level, GameVersion Game = GameVersion.Any)
|
||||
{
|
||||
public bool IsLevelUp => Level >= 0;
|
||||
}
|
|
@ -5,23 +5,8 @@ namespace PKHeX.Core
|
|||
/// <summary>
|
||||
/// Seed result value storage for passing frame seeds & conditions.
|
||||
/// </summary>
|
||||
public readonly struct SeedInfo
|
||||
public readonly record struct SeedInfo(uint Seed, bool Charm3 = false)
|
||||
{
|
||||
public readonly uint Seed;
|
||||
public readonly bool Charm3;
|
||||
|
||||
private SeedInfo(uint seed, bool charm3 = false)
|
||||
{
|
||||
Seed = seed;
|
||||
Charm3 = charm3;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => obj is SeedInfo s && Equals(s);
|
||||
public bool Equals(SeedInfo s) => s.Charm3 == Charm3 && s.Seed == Seed;
|
||||
public override int GetHashCode() => -1;
|
||||
public static bool operator ==(SeedInfo left, SeedInfo right) => left.Equals(right);
|
||||
public static bool operator !=(SeedInfo left, SeedInfo right) => !(left == right);
|
||||
|
||||
/// <summary>
|
||||
/// Yields an enumerable list of seeds until another valid PID breaks the chain.
|
||||
/// </summary>
|
||||
|
|
|
@ -83,17 +83,7 @@ namespace PKHeX.Core
|
|||
};
|
||||
}
|
||||
|
||||
private readonly struct Range
|
||||
{
|
||||
internal readonly uint Min;
|
||||
internal readonly uint Max;
|
||||
|
||||
internal Range(uint min, uint max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
}
|
||||
private readonly record struct Range(uint Min, uint Max);
|
||||
|
||||
private static Range[] GetRanges(params uint[] rates)
|
||||
{
|
||||
|
|
|
@ -68,19 +68,8 @@ namespace PKHeX.Core
|
|||
return true;
|
||||
}
|
||||
|
||||
private readonly struct PIDIVGroup
|
||||
private readonly record struct PIDIVGroup(uint PID, uint IV1, uint IV2)
|
||||
{
|
||||
private readonly uint PID;
|
||||
private readonly uint IV1;
|
||||
private readonly uint IV2;
|
||||
|
||||
public PIDIVGroup(uint pid, uint iv1, uint iv2)
|
||||
{
|
||||
PID = pid;
|
||||
IV1 = iv1;
|
||||
IV2 = iv2;
|
||||
}
|
||||
|
||||
public bool Equals(uint pid, uint iv1, uint iv2) => PID == pid && IV1 == iv1 && IV2 == iv2;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace PKHeX.Core
|
|||
/// <summary>
|
||||
/// Locks associated to a given NPC PKM that appears before a <see cref="EncounterStaticShadow"/>.
|
||||
/// </summary>
|
||||
public readonly struct NPCLock
|
||||
public readonly record struct NPCLock
|
||||
{
|
||||
private readonly int Species;
|
||||
private readonly byte Nature;
|
||||
|
@ -46,11 +46,6 @@ namespace PKHeX.Core
|
|||
return true;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => false;
|
||||
public override int GetHashCode() => 0;
|
||||
public static bool operator ==(NPCLock left, NPCLock right) => left.Equals(right);
|
||||
public static bool operator !=(NPCLock left, NPCLock right) => !(left == right);
|
||||
|
||||
#if DEBUG
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
|
@ -1,17 +1,6 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Message Passing for frame results.
|
||||
/// </summary>
|
||||
internal readonly struct SeedFrame
|
||||
{
|
||||
public readonly uint PID;
|
||||
public readonly int FrameID;
|
||||
namespace PKHeX.Core;
|
||||
|
||||
internal SeedFrame(uint pid, int frame)
|
||||
{
|
||||
PID = pid;
|
||||
FrameID = frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Message Passing for frame results.
|
||||
/// </summary>
|
||||
internal readonly record struct SeedFrame(uint PID, int FrameID);
|
|
@ -1,45 +1,24 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores details about a <see cref="PKM.EncryptionConstant"/> (PID) and any associated details being traced to a known correlation.
|
||||
/// </summary>
|
||||
/// <param name="Type">Type of PIDIV correlation</param>
|
||||
/// <param name="OriginSeed">The RNG seed which immediately generates the PIDIV (starting with PID or IVs, whichever comes first)</param>
|
||||
public readonly record struct PIDIV(PIDType Type, uint OriginSeed = 0)
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores details about a <see cref="PKM.EncryptionConstant"/> (PID) and any associated details being traced to a known correlation.
|
||||
/// </summary>
|
||||
public readonly struct PIDIV
|
||||
{
|
||||
internal static readonly PIDIV None = new();
|
||||
internal static readonly PIDIV CuteCharm = new(PIDType.CuteCharm);
|
||||
internal static readonly PIDIV Pokewalker = new(PIDType.Pokewalker);
|
||||
internal static readonly PIDIV G5MGShiny = new(PIDType.G5MGShiny);
|
||||
internal static readonly PIDIV None = new();
|
||||
internal static readonly PIDIV CuteCharm = new(PIDType.CuteCharm);
|
||||
internal static readonly PIDIV Pokewalker = new(PIDType.Pokewalker);
|
||||
internal static readonly PIDIV G5MGShiny = new(PIDType.G5MGShiny);
|
||||
|
||||
/// <summary> The RNG seed which immediately generates the PIDIV (starting with PID or IVs, whichever comes first). </summary>
|
||||
public readonly uint OriginSeed;
|
||||
/// <summary> Indicates that there is no <see cref="OriginSeed"/> to refer to. </summary>
|
||||
/// <remarks> Some PIDIVs may be generated without a single seed, but may follow a traceable pattern. </remarks>
|
||||
public bool NoSeed => Type is PIDType.None or PIDType.CuteCharm or PIDType.Pokewalker or PIDType.G5MGShiny;
|
||||
|
||||
/// <summary> Indicates that there is no <see cref="OriginSeed"/> to refer to. </summary>
|
||||
/// <remarks> Some PIDIVs may be generated without a single seed, but may follow a traceable pattern. </remarks>
|
||||
public bool NoSeed => Type is PIDType.None or PIDType.CuteCharm or PIDType.Pokewalker or PIDType.G5MGShiny;
|
||||
|
||||
/// <summary> Type of PIDIV correlation </summary>
|
||||
public readonly PIDType Type;
|
||||
|
||||
private PIDIV(PIDType type)
|
||||
{
|
||||
OriginSeed = 0;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public PIDIV(PIDType type, uint seed)
|
||||
{
|
||||
OriginSeed = seed;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public bool Equals(PIDIV pid) => pid.Type == Type && pid.OriginSeed == OriginSeed;
|
||||
public override bool Equals(object pid) => pid is PIDIV p && Equals(p);
|
||||
public override int GetHashCode() => 0;
|
||||
public static bool operator ==(PIDIV left, PIDIV right) => left.Equals(right);
|
||||
public static bool operator !=(PIDIV left, PIDIV right) => !(left == right);
|
||||
/// <summary> Type of PIDIV correlation </summary>
|
||||
|
||||
#if DEBUG
|
||||
public override string ToString() => NoSeed ? Type.ToString() : $"{Type} - 0x{OriginSeed:X8}";
|
||||
public override string ToString() => NoSeed ? Type.ToString() : $"{Type} - 0x{OriginSeed:X8}";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,17 +31,7 @@ namespace PKHeX.Core
|
|||
{(int)Clobbopus, new(9, (int)Taunt)},
|
||||
};
|
||||
|
||||
private readonly struct MoveEvolution
|
||||
{
|
||||
public readonly int ReferenceIndex;
|
||||
public readonly int Move;
|
||||
|
||||
public MoveEvolution(int referenceIndex, int move)
|
||||
{
|
||||
ReferenceIndex = referenceIndex;
|
||||
Move = move;
|
||||
}
|
||||
}
|
||||
private readonly record struct MoveEvolution(int ReferenceIndex, int Move);
|
||||
|
||||
private static readonly int[] FairyMoves =
|
||||
{
|
||||
|
|
|
@ -1,39 +1,11 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public readonly record struct MemoryVariableSet(string Handler, int MemoryID, int Variable, int Intensity, int Feeling)
|
||||
{
|
||||
public readonly struct MemoryVariableSet
|
||||
public static MemoryVariableSet Read(ITrainerMemories pkm, int handler) => handler switch
|
||||
{
|
||||
public readonly string Handler;
|
||||
|
||||
public readonly int MemoryID;
|
||||
public readonly int Variable;
|
||||
public readonly int Intensity;
|
||||
public readonly int Feeling;
|
||||
|
||||
private MemoryVariableSet(string handler, int m, int v, int i, int f)
|
||||
{
|
||||
Handler = handler;
|
||||
MemoryID = m;
|
||||
Variable = v;
|
||||
Intensity = i;
|
||||
Feeling = f;
|
||||
}
|
||||
|
||||
public static MemoryVariableSet Read(ITrainerMemories pkm, int handler) => handler switch
|
||||
{
|
||||
0 => new MemoryVariableSet(LegalityCheckStrings.L_XOT, pkm.OT_Memory, pkm.OT_TextVar, pkm.OT_Intensity, pkm.OT_Feeling), // OT
|
||||
1 => new MemoryVariableSet(LegalityCheckStrings.L_XHT, pkm.HT_Memory, pkm.HT_TextVar, pkm.HT_Intensity, pkm.HT_Feeling), // HT
|
||||
_ => new MemoryVariableSet(LegalityCheckStrings.L_XOT, 0, 0, 0, 0),
|
||||
};
|
||||
|
||||
public bool Equals(MemoryVariableSet v) => v.Handler == Handler
|
||||
&& v.MemoryID == MemoryID
|
||||
&& v.Variable == Variable
|
||||
&& v.Intensity == Intensity
|
||||
&& v.Feeling == Feeling;
|
||||
|
||||
public override bool Equals(object obj) => obj is MemoryVariableSet v && Equals(v);
|
||||
public override int GetHashCode() => -1;
|
||||
public static bool operator ==(MemoryVariableSet left, MemoryVariableSet right) => left.Equals(right);
|
||||
public static bool operator !=(MemoryVariableSet left, MemoryVariableSet right) => !(left == right);
|
||||
}
|
||||
0 => new MemoryVariableSet(LegalityCheckStrings.L_XOT, pkm.OT_Memory, pkm.OT_TextVar, pkm.OT_Intensity, pkm.OT_Feeling), // OT
|
||||
1 => new MemoryVariableSet(LegalityCheckStrings.L_XHT, pkm.HT_Memory, pkm.HT_TextVar, pkm.HT_Intensity, pkm.HT_Feeling), // HT
|
||||
_ => new MemoryVariableSet(LegalityCheckStrings.L_XOT, 0, 0, 0, 0),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<PackageIconUrl>https://github.com/kwsch/PKHeX/blob/master/PKHeX.WinForms/icon.ico</PackageIconUrl>
|
||||
<PackageProjectUrl />
|
||||
<RepositoryUrl>https://github.com/kwsch/PKHeX</RepositoryUrl>
|
||||
<LangVersion>9</LangVersion>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -12,13 +12,9 @@
|
|||
MegaEvolve = 8,
|
||||
}
|
||||
|
||||
public readonly struct TurnActionInstruction
|
||||
public readonly record struct TurnActionInstruction(int PlayerID, int Count, int Bit)
|
||||
{
|
||||
public readonly int PlayerID;
|
||||
public readonly int Count;
|
||||
public readonly int Bit;
|
||||
|
||||
public TurnActionInstruction(byte Op)
|
||||
public TurnActionInstruction(byte Op) : this()
|
||||
{
|
||||
PlayerID = Op >> 5;
|
||||
Bit = (Op >> 4) & 1;
|
||||
|
@ -26,10 +22,5 @@
|
|||
}
|
||||
|
||||
public byte GetRawValue => (byte)((Count & 0xF) | ((byte)Bit << 4) | (PlayerID << 5));
|
||||
|
||||
public override bool Equals(object obj) => obj is TurnStartInstruction t && t.GetRawValue == GetRawValue;
|
||||
public override int GetHashCode() => GetRawValue;
|
||||
public static bool operator ==(TurnActionInstruction left, TurnActionInstruction right) => left.Equals(right);
|
||||
public static bool operator !=(TurnActionInstruction left, TurnActionInstruction right) => !(left == right);
|
||||
}
|
||||
}
|
|
@ -1,21 +1,13 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public readonly struct TurnStartInstruction
|
||||
public readonly record struct TurnStartInstruction(TurnStartCode TurnCode, int Count)
|
||||
{
|
||||
public readonly TurnStartCode TurnCode;
|
||||
public readonly int Count;
|
||||
|
||||
public TurnStartInstruction(byte Op)
|
||||
public TurnStartInstruction(byte Op) : this()
|
||||
{
|
||||
TurnCode = (TurnStartCode)(Op >> 4);
|
||||
Count = Op & 0xF;
|
||||
}
|
||||
|
||||
public byte GetRawValue => (byte) ((Count & 0xF) | ((byte) TurnCode << 4));
|
||||
|
||||
public override bool Equals(object obj) => obj is TurnStartInstruction t && t.GetRawValue == GetRawValue;
|
||||
public override int GetHashCode() => GetRawValue;
|
||||
public static bool operator ==(TurnStartInstruction left, TurnStartInstruction right) => left.Equals(right);
|
||||
public static bool operator !=(TurnStartInstruction left, TurnStartInstruction right) => !(left == right);
|
||||
}
|
||||
}
|
|
@ -725,21 +725,12 @@ namespace PKHeX.Core
|
|||
/// <summary>
|
||||
/// Indicates which <see cref="Zukan8Type"/> block will store the entry, and at what index.
|
||||
/// </summary>
|
||||
public readonly struct Zukan8Index
|
||||
/// <param name="DexType">Which block stores the Pokédex entry.</param>
|
||||
/// <param name="Index">Index that the Pokédex entry is stored at.</param>
|
||||
public readonly record struct Zukan8Index(Zukan8Type DexType, int Index)
|
||||
{
|
||||
/// <summary> Index that the Pokédex entry is stored at. </summary>
|
||||
public readonly int Index;
|
||||
/// <summary> Which block stores the Pokédex entry. </summary>
|
||||
public readonly Zukan8Type DexType;
|
||||
|
||||
public override string ToString() => $"{Index:000} - {DexType}";
|
||||
|
||||
public Zukan8Index(Zukan8Type dexType, int index)
|
||||
{
|
||||
DexType = dexType;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
private int GetSavedIndex()
|
||||
{
|
||||
var index = Index;
|
||||
|
@ -794,25 +785,10 @@ namespace PKHeX.Core
|
|||
{
|
||||
return $"{DexPrefix}.{Index:000} - {speciesNames[species]}";
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => obj is Zukan8Index x && x.Equals(this);
|
||||
public bool Equals(Zukan8Index obj) => obj.Index == Index && obj.DexType == DexType;
|
||||
public override int GetHashCode() => (int)DexType ^ (Index << 3);
|
||||
public static bool operator ==(Zukan8Index left, Zukan8Index right) => left.Equals(right);
|
||||
public static bool operator !=(Zukan8Index left, Zukan8Index right) => !(left == right);
|
||||
}
|
||||
|
||||
public sealed class Zukan8EntryInfo
|
||||
public readonly record struct Zukan8EntryInfo(int Species, Zukan8Index Entry)
|
||||
{
|
||||
public readonly int Species;
|
||||
public readonly Zukan8Index Entry;
|
||||
|
||||
public Zukan8EntryInfo(int species, Zukan8Index entry)
|
||||
{
|
||||
Species = species;
|
||||
Entry = entry;
|
||||
}
|
||||
|
||||
public string GetEntryName(IReadOnlyList<string> speciesNames) => Entry.GetEntryName(speciesNames, Species);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue