Finished outlining Pokemon class.
This commit is contained in:
parent
34c64d6b68
commit
e4a515e11a
|
@ -55,11 +55,37 @@ namespace PkmnLibSharp.Battling
|
||||||
return _forme;
|
return _forme;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public Species DisplaySpecies
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_displaySpecies != null) return _displaySpecies;
|
||||||
|
var ptr = Creaturelibbattling.Generated.Creature.GetDisplaySpecies(Ptr);
|
||||||
|
if (TryResolvePointer(ptr, out _displaySpecies))
|
||||||
|
return _displaySpecies;
|
||||||
|
_displaySpecies = new Species(ptr);
|
||||||
|
return _displaySpecies;
|
||||||
|
}
|
||||||
|
set => Creaturelibbattling.Generated.Creature.SetDisplaySpecies(Ptr, value.Ptr);
|
||||||
|
}
|
||||||
|
public Forme DisplayForme
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_displayForme != null) return _displayForme;
|
||||||
|
var ptr = Creaturelibbattling.Generated.Creature.GetDisplayVariant(Ptr);
|
||||||
|
if (TryResolvePointer(ptr, out _displayForme))
|
||||||
|
return _displayForme;
|
||||||
|
_displayForme = new Forme(ptr);
|
||||||
|
return _displayForme;
|
||||||
|
}
|
||||||
|
set => Creaturelibbattling.Generated.Creature.SetDisplayVariant(Ptr, value.Ptr);
|
||||||
|
}
|
||||||
public byte Level => Creaturelibbattling.Generated.Creature.GetLevel(Ptr);
|
public byte Level => Creaturelibbattling.Generated.Creature.GetLevel(Ptr);
|
||||||
public uint Experience => Creaturelibbattling.Generated.Creature.GetExperience(Ptr);
|
public uint Experience => Creaturelibbattling.Generated.Creature.GetExperience(Ptr);
|
||||||
public Gender Gender => (Gender) Creaturelibbattling.Generated.Creature.GetGender(Ptr);
|
public Gender Gender => (Gender) Creaturelibbattling.Generated.Creature.GetGender(Ptr);
|
||||||
public byte Coloring => Creaturelibbattling.Generated.Creature.GetColoring(Ptr);
|
public byte Coloring => Creaturelibbattling.Generated.Creature.GetColoring(Ptr);
|
||||||
public bool IsShiny => Coloring == 1;
|
public bool IsShiny => Pkmnlib.Generated.Pokemon.IsShiny(Ptr) == 1;
|
||||||
public uint CurrentHealth => Creaturelibbattling.Generated.Creature.GetCurrentHealth(Ptr);
|
public uint CurrentHealth => Creaturelibbattling.Generated.Creature.GetCurrentHealth(Ptr);
|
||||||
public uint MaxHealth => Creaturelibbattling.Generated.Creature.GetMaxHealth(Ptr);
|
public uint MaxHealth => Creaturelibbattling.Generated.Creature.GetMaxHealth(Ptr);
|
||||||
|
|
||||||
|
@ -72,13 +98,11 @@ namespace PkmnLibSharp.Battling
|
||||||
return ptr.PtrString();
|
return ptr.PtrString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Change to wrapped
|
// TODO: Change to wrapped
|
||||||
public IntPtr Battle => Creaturelibbattling.Generated.Creature.GetBattle(Ptr);
|
public IntPtr Battle => Creaturelibbattling.Generated.Creature.GetBattle(Ptr);
|
||||||
// TODO: Change to wrapped
|
// TODO: Change to wrapped
|
||||||
public IntPtr BattleSide => Creaturelibbattling.Generated.Creature.GetBattleSide(Ptr);
|
public IntPtr BattleSide => Creaturelibbattling.Generated.Creature.GetBattleSide(Ptr);
|
||||||
public bool IsOnBattleField => Creaturelibbattling.Generated.Creature.IsOnBattleField(Ptr) == 1;
|
public bool IsOnBattleField => Creaturelibbattling.Generated.Creature.IsOnBattleField(Ptr) == 1;
|
||||||
|
|
||||||
public Item HeldItem
|
public Item HeldItem
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -95,7 +119,6 @@ namespace PkmnLibSharp.Battling
|
||||||
Creaturelibbattling.Generated.Creature.SetHeldItemFromItem(Ptr,
|
Creaturelibbattling.Generated.Creature.SetHeldItemFromItem(Ptr,
|
||||||
value?.Ptr ?? IntPtr.Zero);
|
value?.Ptr ?? IntPtr.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Nickname
|
public string Nickname
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -105,18 +128,82 @@ namespace PkmnLibSharp.Battling
|
||||||
return _nickname;
|
return _nickname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public ReadOnlyNativePtrArray<LearnedMove> Moves
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_moves != null) return _moves;
|
||||||
|
var movesLength = Creaturelibbattling.Generated.Creature.GetAttacksCount(Ptr);
|
||||||
|
var movesPtr = Creaturelibbattling.Generated.Creature.GetAttacks(Ptr);
|
||||||
|
_moves = new ReadOnlyNativePtrArray<LearnedMove>(movesPtr, (int) movesLength);
|
||||||
|
return _moves;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sbyte HpBoost => GetStatBoost(Statistic.Health);
|
||||||
|
public sbyte AttackBoost => GetStatBoost(Statistic.Attack);
|
||||||
|
public sbyte DefenseBoost => GetStatBoost(Statistic.Defense);
|
||||||
|
public sbyte SpecialAttackBoost => GetStatBoost(Statistic.SpecialAttack);
|
||||||
|
public sbyte SpecialDefenseBoost => GetStatBoost(Statistic.SpecialDefense);
|
||||||
|
public sbyte SpeedBoost => GetStatBoost(Statistic.Speed);
|
||||||
|
|
||||||
|
public uint BaseHp => GetBaseStat(Statistic.Health);
|
||||||
|
public uint BaseAttack => GetBaseStat(Statistic.Attack);
|
||||||
|
public uint BaseDefense => GetBaseStat(Statistic.Defense);
|
||||||
|
public uint BaseSpecialAttack => GetBaseStat(Statistic.SpecialAttack);
|
||||||
|
public uint BaseSpecialDefense => GetBaseStat(Statistic.SpecialDefense);
|
||||||
|
public uint BaseSpeed => GetBaseStat(Statistic.Speed);
|
||||||
|
|
||||||
|
public uint FlatHp => GetFlatStat(Statistic.Health);
|
||||||
|
public uint FlatAttack => GetFlatStat(Statistic.Attack);
|
||||||
|
public uint FlatDefense => GetFlatStat(Statistic.Defense);
|
||||||
|
public uint FlatSpecialAttack => GetFlatStat(Statistic.SpecialAttack);
|
||||||
|
public uint FlatSpecialDefense => GetFlatStat(Statistic.SpecialDefense);
|
||||||
|
public uint FlatSpeed => GetFlatStat(Statistic.Speed);
|
||||||
|
|
||||||
|
public uint BoostedHp => GetBoostedStat(Statistic.Health);
|
||||||
|
public uint BoostedAttack => GetBoostedStat(Statistic.Attack);
|
||||||
|
public uint BoostedDefense => GetBoostedStat(Statistic.Defense);
|
||||||
|
public uint BoostedSpecialAttack => GetBoostedStat(Statistic.SpecialAttack);
|
||||||
|
public uint BoostedSpecialDefense => GetBoostedStat(Statistic.SpecialDefense);
|
||||||
|
public uint BoostedSpeed => GetBoostedStat(Statistic.Speed);
|
||||||
|
|
||||||
|
public byte HpIv => GetIndividualValue(Statistic.Health);
|
||||||
|
public byte AttackIv => GetIndividualValue(Statistic.Attack);
|
||||||
|
public byte DefenseIv => GetIndividualValue(Statistic.Defense);
|
||||||
|
public byte SpecialAttackIv => GetIndividualValue(Statistic.SpecialAttack);
|
||||||
|
public byte SpecialDefenseIv => GetIndividualValue(Statistic.SpecialDefense);
|
||||||
|
public byte SpeedIv => GetIndividualValue(Statistic.Speed);
|
||||||
|
|
||||||
|
public byte HpEv => GetEffortValue(Statistic.Health);
|
||||||
|
public byte AttackEv => GetEffortValue(Statistic.Attack);
|
||||||
|
public byte DefenseEv => GetEffortValue(Statistic.Defense);
|
||||||
|
public byte SpecialAttackEv => GetEffortValue(Statistic.SpecialAttack);
|
||||||
|
public byte SpecialDefenseEv => GetEffortValue(Statistic.SpecialDefense);
|
||||||
|
public byte SpeedEv => GetEffortValue(Statistic.Speed);
|
||||||
|
|
||||||
|
public Nature Nature
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_nature != null) return _nature;
|
||||||
|
var ptr = Pkmnlib.Generated.Pokemon.GetNature(Ptr);
|
||||||
|
if (TryResolvePointer(ptr, out _nature))
|
||||||
|
return _nature;
|
||||||
|
_nature = new Nature(ptr);
|
||||||
|
return _nature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ChangeForme(Forme forme)
|
public void ChangeForme(Forme forme)
|
||||||
{
|
{
|
||||||
_forme = null;
|
_forme = null;
|
||||||
Creaturelibbattling.Generated.Creature.ChangeVariant(Ptr, forme.Ptr);
|
Creaturelibbattling.Generated.Creature.ChangeVariant(Ptr, forme.Ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasHeldItem(string itemName)
|
public bool HasHeldItem(string itemName)
|
||||||
{
|
{
|
||||||
return Creaturelibbattling.Generated.Creature.HasHeldItem(Ptr, itemName.ToPtr()) == 1;
|
return Creaturelibbattling.Generated.Creature.HasHeldItem(Ptr, itemName.ToPtr()) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetHeldItem(string item)
|
public void SetHeldItem(string item)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(item))
|
if (string.IsNullOrEmpty(item))
|
||||||
|
@ -124,83 +211,95 @@ namespace PkmnLibSharp.Battling
|
||||||
else
|
else
|
||||||
Creaturelibbattling.Generated.Creature.SetHeldItem(Ptr, item.ToPtr());
|
Creaturelibbattling.Generated.Creature.SetHeldItem(Ptr, item.ToPtr());
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasType(string type)
|
public bool HasType(string type)
|
||||||
{
|
{
|
||||||
var typeByte = Library.StaticLibrary.TypeLibrary.GetTypeId(type);
|
var typeByte = Library.StaticLibrary.TypeLibrary.GetTypeId(type);
|
||||||
return Creaturelibbattling.Generated.Creature.HasType(Ptr, typeByte) == 1;
|
return Creaturelibbattling.Generated.Creature.HasType(Ptr, typeByte) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeLevelBy(sbyte amount)
|
public void ChangeLevelBy(sbyte amount)
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.ChangeLevelBy(Ptr, amount).Assert();
|
Creaturelibbattling.Generated.Creature.ChangeLevelBy(Ptr, amount).Assert();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddExperience(uint experience)
|
public void AddExperience(uint experience)
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.AddExperience(Ptr, experience).Assert();
|
Creaturelibbattling.Generated.Creature.AddExperience(Ptr, experience).Assert();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Damage(uint damage, DamageSource source)
|
public void Damage(uint damage, DamageSource source)
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.Damage(Ptr, damage, (Creaturelibbattling.DamageSource) source);
|
Creaturelibbattling.Generated.Creature.Damage(Ptr, damage, (Creaturelibbattling.DamageSource) source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Heal(uint damage, bool canRevive)
|
public void Heal(uint damage, bool canRevive)
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.Heal(Ptr, damage, canRevive.ToNative());
|
Creaturelibbattling.Generated.Creature.Heal(Ptr, damage, canRevive.ToNative());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OverrideActiveAbility(string ability)
|
public void OverrideActiveAbility(string ability)
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.OverrideActiveTalent(Ptr, ability.ToPtr()).Assert();
|
Creaturelibbattling.Generated.Creature.OverrideActiveTalent(Ptr, ability.ToPtr()).Assert();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearVolatileScripts()
|
public void ClearVolatileScripts()
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.ClearVolatileScripts(Ptr).Assert();
|
Creaturelibbattling.Generated.Creature.ClearVolatileScripts(Ptr).Assert();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddVolatileScript(string scriptName)
|
public void AddVolatileScript(string scriptName)
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.AddVolatileScriptByName(Ptr, scriptName.ToPtr()).Assert();
|
Creaturelibbattling.Generated.Creature.AddVolatileScriptByName(Ptr, scriptName.ToPtr()).Assert();
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Change to wrapper
|
//TODO: Change to wrapper
|
||||||
public void AddVolatileScript(IntPtr script)
|
public void AddVolatileScript(IntPtr script)
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.AddVolatileScript(Ptr, script).Assert();
|
Creaturelibbattling.Generated.Creature.AddVolatileScript(Ptr, script).Assert();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveVolatileScript(string scriptName)
|
public void RemoveVolatileScript(string scriptName)
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.RemoveVolatileScriptByName(Ptr, scriptName.ToPtr()).Assert();
|
Creaturelibbattling.Generated.Creature.RemoveVolatileScriptByName(Ptr, scriptName.ToPtr()).Assert();
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Change to wrapper
|
//TODO: Change to wrapper
|
||||||
public void RemoveVolatileScript(IntPtr script)
|
public void RemoveVolatileScript(IntPtr script)
|
||||||
{
|
{
|
||||||
Creaturelibbattling.Generated.Creature.RemoveVolatileScript(Ptr, script).Assert();
|
Creaturelibbattling.Generated.Creature.RemoveVolatileScript(Ptr, script).Assert();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasVolatileScript(string scriptName)
|
public bool HasVolatileScript(string scriptName)
|
||||||
{
|
{
|
||||||
return Creaturelibbattling.Generated.Creature.HasVolatileScript(Ptr, scriptName.ToPtr()) == 1;
|
return Creaturelibbattling.Generated.Creature.HasVolatileScript(Ptr, scriptName.ToPtr()) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativePtrArray<LearnedMove> GetMoves()
|
public sbyte GetStatBoost(Statistic stat)
|
||||||
{
|
{
|
||||||
if (_moves != null) return _moves;
|
return Creaturelibbattling.Generated.Creature.GetStatBoost(Ptr, (Creaturelibbattling.Statistic) stat);
|
||||||
var movesLength = Creaturelibbattling.Generated.Creature.GetAttacksCount(Ptr);
|
}
|
||||||
var movesPtr = Creaturelibbattling.Generated.Creature.GetAttacks(Ptr);
|
public void ChangeStatBoost(Statistic stat, sbyte amount)
|
||||||
_moves = new NativePtrArray<LearnedMove>(movesPtr, (int) movesLength);
|
{
|
||||||
return _moves;
|
Creaturelibbattling.Generated.Creature.ChangeStatBoost(Ptr, (Creaturelibbattling.Statistic) stat, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetBaseStat(Statistic stat)
|
||||||
|
{
|
||||||
|
return Creaturelibbattling.Generated.Creature.GetBaseStat(Ptr, (Creaturelibbattling.Statistic) stat);
|
||||||
|
}
|
||||||
|
public uint GetFlatStat(Statistic stat)
|
||||||
|
{
|
||||||
|
return Creaturelibbattling.Generated.Creature.GetFlatStat(Ptr, (Creaturelibbattling.Statistic) stat);
|
||||||
|
}
|
||||||
|
public uint GetBoostedStat(Statistic stat)
|
||||||
|
{
|
||||||
|
return Creaturelibbattling.Generated.Creature.GetBoostedStat(Ptr, (Creaturelibbattling.Statistic) stat);
|
||||||
|
}
|
||||||
|
public byte GetIndividualValue(Statistic stat)
|
||||||
|
{
|
||||||
|
return Pkmnlib.Generated.Pokemon.GetIndividualValue(Ptr, (Pkmnlib.Statistic) stat);
|
||||||
|
}
|
||||||
|
public byte GetEffortValue(Statistic stat)
|
||||||
|
{
|
||||||
|
return Pkmnlib.Generated.Pokemon.GetEffortValue(Ptr, (Pkmnlib.Statistic) stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Species _displaySpecies;
|
||||||
|
private Forme _displayForme;
|
||||||
private Species _species;
|
private Species _species;
|
||||||
private Forme _forme;
|
private Forme _forme;
|
||||||
private string _nickname;
|
private string _nickname;
|
||||||
private NativePtrArray<LearnedMove> _moves;
|
private ReadOnlyNativePtrArray<LearnedMove> _moves;
|
||||||
|
private Nature _nature;
|
||||||
|
|
||||||
protected override void DeletePtr()
|
protected override void DeletePtr()
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,11 +4,11 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace PkmnLibSharp.Utilities
|
namespace PkmnLibSharp.Utilities
|
||||||
{
|
{
|
||||||
public class NativePtrArray<T> : IList<T> where T : PointerWrapper, new()
|
public class ReadOnlyNativePtrArray<T> : IReadOnlyList<T> where T : PointerWrapper, new()
|
||||||
{
|
{
|
||||||
private readonly IntPtr _ptr;
|
private readonly IntPtr _ptr;
|
||||||
|
|
||||||
internal NativePtrArray(IntPtr ptr, int length)
|
internal ReadOnlyNativePtrArray(IntPtr ptr, int length)
|
||||||
{
|
{
|
||||||
_ptr = ptr;
|
_ptr = ptr;
|
||||||
Count = length;
|
Count = length;
|
||||||
|
@ -27,16 +27,6 @@ namespace PkmnLibSharp.Utilities
|
||||||
return GetEnumerator();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(T item)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(T item)
|
public bool Contains(T item)
|
||||||
{
|
{
|
||||||
foreach (var v in this)
|
foreach (var v in this)
|
||||||
|
@ -53,19 +43,8 @@ namespace PkmnLibSharp.Utilities
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyTo(T[] array, int arrayIndex)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Remove(T item)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count { get; }
|
public int Count { get; }
|
||||||
|
|
||||||
public bool IsReadOnly => true;
|
|
||||||
public int IndexOf(T item)
|
public int IndexOf(T item)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < Count; i++)
|
for (var i = 0; i < Count; i++)
|
||||||
|
@ -79,16 +58,6 @@ namespace PkmnLibSharp.Utilities
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Insert(int index, T item)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveAt(int index)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public T this[int index]
|
public T this[int index]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -96,7 +65,7 @@ namespace PkmnLibSharp.Utilities
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
// Where's your god now?
|
// Where's your god now?
|
||||||
// (We add the offset of the index to the pointer, then dereference the pointer pointer to get the actual pointer.)
|
// (We add the offset of the index to the pointer, then dereference the pointer pointer to get the actual pointer to the object we want.)
|
||||||
var p = new IntPtr(*(void**)IntPtr.Add(_ptr, index * IntPtr.Size).ToPointer());
|
var p = new IntPtr(*(void**)IntPtr.Add(_ptr, index * IntPtr.Size).ToPointer());
|
||||||
if (PointerWrapper.TryResolvePointer(p, out T t))
|
if (PointerWrapper.TryResolvePointer(p, out T t))
|
||||||
return t;
|
return t;
|
||||||
|
@ -105,7 +74,6 @@ namespace PkmnLibSharp.Utilities
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set => throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -44,8 +44,8 @@ namespace PkmnLibSharpTests.Battling
|
||||||
.LearnMove("testMove", MoveLearnMethod.Unknown)
|
.LearnMove("testMove", MoveLearnMethod.Unknown)
|
||||||
.LearnMove("testMove2", MoveLearnMethod.Level)
|
.LearnMove("testMove2", MoveLearnMethod.Level)
|
||||||
.Build();
|
.Build();
|
||||||
Assert.AreEqual("testMove", pokemon.GetMoves()[0].Move.Name);
|
Assert.AreEqual("testMove", pokemon.Moves[0].Move.Name);
|
||||||
Assert.AreEqual("testMove2", pokemon.GetMoves()[1].Move.Name);
|
Assert.AreEqual("testMove2", pokemon.Moves[1].Move.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue