Style cleanup
This commit is contained in:
parent
c0bc905c46
commit
284ab3079c
|
@ -47,7 +47,7 @@ public static class AbilityDataLoader
|
||||||
var objects = LoadAbilitiesData(stream);
|
var objects = LoadAbilitiesData(stream);
|
||||||
if (objects == null)
|
if (objects == null)
|
||||||
throw new InvalidDataException("Ability data is empty.");
|
throw new InvalidDataException("Ability data is empty.");
|
||||||
|
|
||||||
var abilities = objects.Select(x => DeserializeAbility(x.Key, x.Value));
|
var abilities = objects.Select(x => DeserializeAbility(x.Key, x.Value));
|
||||||
foreach (var a in abilities)
|
foreach (var a in abilities)
|
||||||
library.Add(a);
|
library.Add(a);
|
||||||
|
@ -60,7 +60,7 @@ public static class AbilityDataLoader
|
||||||
var parameters = serialized.Parameters.ToDictionary(x => (StringKey)x.Key, x => x.Value.ToParameter());
|
var parameters = serialized.Parameters.ToDictionary(x => (StringKey)x.Key, x => x.Value.ToParameter());
|
||||||
|
|
||||||
StringKey? effectName = effect == null ? null! : new StringKey(effect);
|
StringKey? effectName = effect == null ? null! : new StringKey(effect);
|
||||||
|
|
||||||
var flags = serialized.Flags.Select(x => new StringKey(x)).ToImmutableHashSet();
|
var flags = serialized.Flags.Select(x => new StringKey(x)).ToImmutableHashSet();
|
||||||
|
|
||||||
var ability = new AbilityImpl(name, effectName, parameters, flags);
|
var ability = new AbilityImpl(name, effectName, parameters, flags);
|
||||||
|
|
|
@ -26,15 +26,15 @@ public static class ItemDataLoader
|
||||||
library.Add(i);
|
library.Add(i);
|
||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate IItem ItemFactoryDelegate(SerializedItem serialized, StringKey name, ItemCategory type,
|
public delegate IItem ItemFactoryDelegate(SerializedItem serialized, StringKey name, ItemCategory type,
|
||||||
BattleItemCategory battleType, int price, ImmutableHashSet<StringKey> flags,
|
BattleItemCategory battleType, int price, ImmutableHashSet<StringKey> flags, ISecondaryEffect? effect,
|
||||||
ISecondaryEffect? effect, ISecondaryEffect? battleTriggerEffect, byte flingPower);
|
ISecondaryEffect? battleTriggerEffect, byte flingPower);
|
||||||
|
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static ItemFactoryDelegate ItemConstructor { get; set; } = (_, name, type, battleType, price, flags, effect,
|
public static ItemFactoryDelegate ItemConstructor { get; set; } =
|
||||||
battleTriggerEffect, flingPower) =>
|
(_, name, type, battleType, price, flags, effect, battleTriggerEffect, flingPower) => new ItemImpl(name, type,
|
||||||
new ItemImpl(name, type, battleType, price, flags, effect, battleTriggerEffect, flingPower);
|
battleType, price, flags, effect, battleTriggerEffect, flingPower);
|
||||||
|
|
||||||
private static IItem DeserializeItem(SerializedItem serialized)
|
private static IItem DeserializeItem(SerializedItem serialized)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace PkmnLib.Dataloader;
|
||||||
|
|
||||||
internal static class JsonOptions
|
internal static class JsonOptions
|
||||||
{
|
{
|
||||||
public static JsonSerializerOptions DefaultOptions => new JsonSerializerOptions()
|
public static JsonSerializerOptions DefaultOptions => new()
|
||||||
{
|
{
|
||||||
PropertyNameCaseInsensitive = true,
|
PropertyNameCaseInsensitive = true,
|
||||||
AllowTrailingCommas = true,
|
AllowTrailingCommas = true,
|
||||||
|
|
|
@ -14,7 +14,6 @@ public class SerializedItem
|
||||||
public byte FlingPower { get; set; }
|
public byte FlingPower { get; set; }
|
||||||
public SerializedMoveEffect? Effect { get; set; }
|
public SerializedMoveEffect? Effect { get; set; }
|
||||||
public SerializedMoveEffect? BattleEffect { get; set; }
|
public SerializedMoveEffect? BattleEffect { get; set; }
|
||||||
|
|
||||||
[JsonExtensionData]
|
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||||
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
|
||||||
}
|
}
|
|
@ -22,9 +22,8 @@ public class SerializedMove
|
||||||
public string Category { get; set; } = null!;
|
public string Category { get; set; } = null!;
|
||||||
public string[] Flags { get; set; } = null!;
|
public string[] Flags { get; set; } = null!;
|
||||||
public SerializedMoveEffect? Effect { get; set; }
|
public SerializedMoveEffect? Effect { get; set; }
|
||||||
|
|
||||||
[JsonExtensionData]
|
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||||
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SerializedMoveEffect
|
public class SerializedMoveEffect
|
||||||
|
|
|
@ -20,9 +20,8 @@ public class SerializedSpecies
|
||||||
public string[] Flags { get; set; } = [];
|
public string[] Flags { get; set; } = [];
|
||||||
public Dictionary<string, SerializedForm> Formes { get; set; } = null!;
|
public Dictionary<string, SerializedForm> Formes { get; set; } = null!;
|
||||||
public SerializedEvolution[] Evolutions { get; set; } = [];
|
public SerializedEvolution[] Evolutions { get; set; } = [];
|
||||||
|
|
||||||
[JsonExtensionData]
|
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||||
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SerializedForm
|
public class SerializedForm
|
||||||
|
@ -38,9 +37,8 @@ public class SerializedForm
|
||||||
public bool IsMega { get; set; }
|
public bool IsMega { get; set; }
|
||||||
public SerializedMoves Moves { get; set; } = null!;
|
public SerializedMoves Moves { get; set; } = null!;
|
||||||
public string[] Flags { get; set; } = [];
|
public string[] Flags { get; set; } = [];
|
||||||
|
|
||||||
[JsonExtensionData]
|
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||||
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SerializedEvolution
|
public class SerializedEvolution
|
||||||
|
|
|
@ -26,11 +26,12 @@ public static class MoveDataLoader
|
||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Func<SerializedMove, StringKey, TypeIdentifier, MoveCategory, byte, byte, byte, MoveTarget, sbyte,
|
public static
|
||||||
ISecondaryEffect?, IEnumerable<StringKey>, MoveDataImpl> MoveConstructor =
|
Func<SerializedMove, StringKey, TypeIdentifier, MoveCategory, byte, byte, byte, MoveTarget, sbyte,
|
||||||
(serialized, name, moveType, category, basePower, accuracy, baseUsages, target, priority, secondaryEffect,
|
ISecondaryEffect?, IEnumerable<StringKey>, MoveDataImpl> MoveConstructor =
|
||||||
flags) => new MoveDataImpl(name, moveType, category, basePower, accuracy, baseUsages, target, priority,
|
(serialized, name, moveType, category, basePower, accuracy, baseUsages, target, priority, secondaryEffect,
|
||||||
secondaryEffect, flags);
|
flags) => new MoveDataImpl(name, moveType, category, basePower, accuracy, baseUsages, target, priority,
|
||||||
|
secondaryEffect, flags);
|
||||||
|
|
||||||
private static MoveDataImpl DeserializeMove(SerializedMove serialized, TypeLibrary typeLibrary)
|
private static MoveDataImpl DeserializeMove(SerializedMove serialized, TypeLibrary typeLibrary)
|
||||||
{
|
{
|
||||||
|
@ -55,9 +56,8 @@ public static class MoveDataLoader
|
||||||
throw new InvalidDataException($"Target {target} is not a valid target.");
|
throw new InvalidDataException($"Target {target} is not a valid target.");
|
||||||
var secondaryEffect = effect.ParseEffect();
|
var secondaryEffect = effect.ParseEffect();
|
||||||
|
|
||||||
var move = MoveConstructor(serialized, serialized.Name, typeIdentifier, categoryEnum, power, accuracy, pp, targetEnum,
|
var move = MoveConstructor(serialized, serialized.Name, typeIdentifier, categoryEnum, power, accuracy, pp,
|
||||||
priority, secondaryEffect, flags.Select(x => (StringKey)x).ToImmutableHashSet());
|
targetEnum, priority, secondaryEffect, flags.Select(x => (StringKey)x).ToImmutableHashSet());
|
||||||
return move;
|
return move;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ public static class NatureDataLoader
|
||||||
public static NatureLibrary LoadNatureLibrary(Stream stream)
|
public static NatureLibrary LoadNatureLibrary(Stream stream)
|
||||||
{
|
{
|
||||||
var library = new NatureLibrary();
|
var library = new NatureLibrary();
|
||||||
|
|
||||||
using var reader = new StreamReader(stream);
|
using var reader = new StreamReader(stream);
|
||||||
var header = reader.ReadLine();
|
var header = reader.ReadLine();
|
||||||
if (header == null)
|
if (header == null)
|
||||||
|
@ -31,7 +31,7 @@ public static class NatureDataLoader
|
||||||
var nature = values[0];
|
var nature = values[0];
|
||||||
var increasedStat = values[1];
|
var increasedStat = values[1];
|
||||||
var decreasedStat = values[2];
|
var decreasedStat = values[2];
|
||||||
|
|
||||||
var increasedModifier = 1.1f;
|
var increasedModifier = 1.1f;
|
||||||
var decreasedModifier = 0.9f;
|
var decreasedModifier = 0.9f;
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ public static class NatureDataLoader
|
||||||
decreasedStat = "Hp";
|
decreasedStat = "Hp";
|
||||||
decreasedModifier = 1.0f;
|
decreasedModifier = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Enum.TryParse<Statistic>(increasedStat, out var increasedStatEnum))
|
if (!Enum.TryParse<Statistic>(increasedStat, out var increasedStatEnum))
|
||||||
throw new InvalidDataException($"Increased stat {increasedStat} is not a valid stat.");
|
throw new InvalidDataException($"Increased stat {increasedStat} is not a valid stat.");
|
||||||
if (!Enum.TryParse<Statistic>(decreasedStat, out var decreasedStatEnum))
|
if (!Enum.TryParse<Statistic>(decreasedStat, out var decreasedStatEnum))
|
||||||
|
|
|
@ -20,10 +20,10 @@ public static class SpeciesDataLoader
|
||||||
var obj = JsonSerializer.Deserialize<JsonObject>(stream, JsonOptions.DefaultOptions);
|
var obj = JsonSerializer.Deserialize<JsonObject>(stream, JsonOptions.DefaultOptions);
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
throw new InvalidDataException("Species data is empty.");
|
throw new InvalidDataException("Species data is empty.");
|
||||||
return obj.Where(x => x.Key != "$schema")
|
return obj.Where(x => x.Key != "$schema").ToDictionary(x => x.Key,
|
||||||
.ToDictionary(x => x.Key, x => x.Value.Deserialize<SerializedSpecies>(JsonOptions.DefaultOptions));
|
x => x.Value.Deserialize<SerializedSpecies>(JsonOptions.DefaultOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpeciesLibrary LoadSpecies(Stream[] streams, IReadOnlyTypeLibrary typeLibrary)
|
public static SpeciesLibrary LoadSpecies(Stream[] streams, IReadOnlyTypeLibrary typeLibrary)
|
||||||
{
|
{
|
||||||
var library = new SpeciesLibrary();
|
var library = new SpeciesLibrary();
|
||||||
|
@ -35,7 +35,7 @@ public static class SpeciesDataLoader
|
||||||
library.Add(s);
|
library.Add(s);
|
||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpeciesLibrary LoadSpecies(Stream stream, IReadOnlyTypeLibrary typeLibrary)
|
public static SpeciesLibrary LoadSpecies(Stream stream, IReadOnlyTypeLibrary typeLibrary)
|
||||||
{
|
{
|
||||||
var library = new SpeciesLibrary();
|
var library = new SpeciesLibrary();
|
||||||
|
@ -48,34 +48,37 @@ public static class SpeciesDataLoader
|
||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Func<SerializedSpecies, ushort, StringKey, float, StringKey, byte, byte, IReadOnlyDictionary<StringKey, IForm>,
|
public static
|
||||||
IEnumerable<StringKey>, IReadOnlyList<IEvolution>, IEnumerable<StringKey>, SpeciesImpl> SpeciesConstructor =
|
Func<SerializedSpecies, ushort, StringKey, float, StringKey, byte, byte, IReadOnlyDictionary<StringKey, IForm>,
|
||||||
(_, id, name, genderRate, growthRate, captureRate, baseHappiness, forms, flags, evolutionData,
|
IEnumerable<StringKey>, IReadOnlyList<IEvolution>, IEnumerable<StringKey>, SpeciesImpl> SpeciesConstructor =
|
||||||
eggGroups) =>
|
(_, id, name, genderRate, growthRate, captureRate, baseHappiness, forms, flags, evolutionData, eggGroups) =>
|
||||||
{
|
{
|
||||||
return new SpeciesImpl(id, name, genderRate, growthRate,
|
return new SpeciesImpl(id, name, genderRate, growthRate, captureRate, baseHappiness, forms, flags,
|
||||||
captureRate, baseHappiness, forms,
|
evolutionData, eggGroups);
|
||||||
flags, evolutionData, eggGroups);
|
};
|
||||||
};
|
|
||||||
|
|
||||||
private static SpeciesImpl DeserializeSpecies(SerializedSpecies serialized, IReadOnlyTypeLibrary typeLibrary)
|
private static SpeciesImpl DeserializeSpecies(SerializedSpecies serialized, IReadOnlyTypeLibrary typeLibrary)
|
||||||
{
|
{
|
||||||
var id = serialized.Id;
|
var id = serialized.Id;
|
||||||
var genderRate = serialized.GenderRatio;
|
var genderRate = serialized.GenderRatio;
|
||||||
if (genderRate < -1.0 || genderRate > 100.0)
|
if (genderRate < -1.0 || genderRate > 100.0)
|
||||||
|
{
|
||||||
throw new InvalidDataException(
|
throw new InvalidDataException(
|
||||||
$"Gender rate for species {id} is invalid: {genderRate}. Must be between -1.0 and 100.0.");
|
$"Gender rate for species {id} is invalid: {genderRate}. Must be between -1.0 and 100.0.");
|
||||||
|
}
|
||||||
|
|
||||||
if (serialized.EggCycles < 0)
|
if (serialized.EggCycles < 0)
|
||||||
|
{
|
||||||
throw new InvalidDataException(
|
throw new InvalidDataException(
|
||||||
$"Egg cycles for species {id} is invalid: {serialized.EggCycles}. Must be greater than or equal to 0.");
|
$"Egg cycles for species {id} is invalid: {serialized.EggCycles}. Must be greater than or equal to 0.");
|
||||||
|
}
|
||||||
|
|
||||||
var forms = serialized.Formes.ToDictionary(x => (StringKey)x.Key,
|
var forms = serialized.Formes.ToDictionary(x => (StringKey)x.Key,
|
||||||
x => DeserializeForm(x.Key, x.Value, typeLibrary));
|
x => DeserializeForm(x.Key, x.Value, typeLibrary));
|
||||||
var evolutions = serialized.Evolutions.Select(DeserializeEvolution).ToList();
|
var evolutions = serialized.Evolutions.Select(DeserializeEvolution).ToList();
|
||||||
|
|
||||||
var species = SpeciesConstructor(serialized, serialized.Id, serialized.Species, genderRate, serialized.GrowthRate,
|
var species = SpeciesConstructor(serialized, serialized.Id, serialized.Species, genderRate,
|
||||||
serialized.CatchRate, serialized.BaseHappiness, forms,
|
serialized.GrowthRate, serialized.CatchRate, serialized.BaseHappiness, forms,
|
||||||
serialized.Flags.Select(x => new StringKey(x)), evolutions, serialized.EggGroups.Select(x => (StringKey)x));
|
serialized.Flags.Select(x => new StringKey(x)), evolutions, serialized.EggGroups.Select(x => (StringKey)x));
|
||||||
return species;
|
return species;
|
||||||
}
|
}
|
||||||
|
@ -85,11 +88,15 @@ public static class SpeciesDataLoader
|
||||||
if (form == null)
|
if (form == null)
|
||||||
throw new ArgumentException("Form data is null.", nameof(form));
|
throw new ArgumentException("Form data is null.", nameof(form));
|
||||||
if (form.Height < 0.0)
|
if (form.Height < 0.0)
|
||||||
|
{
|
||||||
throw new InvalidDataException(
|
throw new InvalidDataException(
|
||||||
$"Height for form {name} is invalid: {form.Height}. Must be greater than or equal to 0.0.");
|
$"Height for form {name} is invalid: {form.Height}. Must be greater than or equal to 0.0.");
|
||||||
|
}
|
||||||
if (form.Weight < 0.0)
|
if (form.Weight < 0.0)
|
||||||
|
{
|
||||||
throw new InvalidDataException(
|
throw new InvalidDataException(
|
||||||
$"Weight for form {name} is invalid: {form.Weight}. Must be greater than or equal to 0.0.");
|
$"Weight for form {name} is invalid: {form.Weight}. Must be greater than or equal to 0.0.");
|
||||||
|
}
|
||||||
var types = form.Types.Select(x =>
|
var types = form.Types.Select(x =>
|
||||||
typeLibrary.TryGetTypeIdentifier(new StringKey(x), out var t)
|
typeLibrary.TryGetTypeIdentifier(new StringKey(x), out var t)
|
||||||
? t
|
? t
|
||||||
|
@ -105,25 +112,26 @@ public static class SpeciesDataLoader
|
||||||
{
|
{
|
||||||
var learnableMoves = new LearnableMovesImpl();
|
var learnableMoves = new LearnableMovesImpl();
|
||||||
if (moves.LevelMoves != null)
|
if (moves.LevelMoves != null)
|
||||||
|
{
|
||||||
foreach (var levelMove in moves.LevelMoves)
|
foreach (var levelMove in moves.LevelMoves)
|
||||||
{
|
{
|
||||||
learnableMoves.AddLevelMove((byte)levelMove.Level, new StringKey(levelMove.Name));
|
learnableMoves.AddLevelMove((byte)levelMove.Level, new StringKey(levelMove.Name));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (moves.EggMoves != null)
|
if (moves.EggMoves != null)
|
||||||
|
{
|
||||||
foreach (var eggMove in moves.EggMoves)
|
foreach (var eggMove in moves.EggMoves)
|
||||||
{
|
{
|
||||||
learnableMoves.AddEggMove(new StringKey(eggMove));
|
learnableMoves.AddEggMove(new StringKey(eggMove));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return learnableMoves;
|
return learnableMoves;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImmutableStatisticSet<ushort> DeserializeStats(SerializedStats stats)
|
private static ImmutableStatisticSet<ushort> DeserializeStats(SerializedStats stats) =>
|
||||||
{
|
new(stats.Hp, stats.Attack, stats.Defense, stats.SpecialAttack, stats.SpecialDefense, stats.Speed);
|
||||||
return new ImmutableStatisticSet<ushort>(stats.Hp, stats.Attack, stats.Defense, stats.SpecialAttack,
|
|
||||||
stats.SpecialDefense, stats.Speed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEvolution DeserializeEvolution(SerializedEvolution evolution)
|
private static IEvolution DeserializeEvolution(SerializedEvolution evolution)
|
||||||
{
|
{
|
||||||
|
@ -223,8 +231,7 @@ public static class SpeciesDataLoader
|
||||||
{
|
{
|
||||||
Name = evolution.Data.AsObject()["type"]?.GetValue<string>() ??
|
Name = evolution.Data.AsObject()["type"]?.GetValue<string>() ??
|
||||||
throw new InvalidDataException("Type is null."),
|
throw new InvalidDataException("Type is null."),
|
||||||
Parameters = evolution.Data.AsObject()
|
Parameters = evolution.Data.AsObject().Where(x => x.Key != "type")
|
||||||
.Where(x => x.Key != "type")
|
|
||||||
.ToDictionary(x => new StringKey(x.Key), x => x.Value!.ToParameter()),
|
.ToDictionary(x => new StringKey(x.Key), x => x.Value!.ToParameter()),
|
||||||
ToSpecies = evolution.Species,
|
ToSpecies = evolution.Species,
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,8 +42,10 @@ public static class TypeDataLoader
|
||||||
{
|
{
|
||||||
var effectiveness = float.Parse(values[i]);
|
var effectiveness = float.Parse(values[i]);
|
||||||
if (effectiveness < 0.0)
|
if (effectiveness < 0.0)
|
||||||
|
{
|
||||||
throw new InvalidDataException(
|
throw new InvalidDataException(
|
||||||
$"Effectiveness for {type} against {types[i]} is invalid: {effectiveness}. Must be greater than or equal to 0.0.");
|
$"Effectiveness for {type} against {types[i]} is invalid: {effectiveness}. Must be greater than or equal to 0.0.");
|
||||||
|
}
|
||||||
library.SetEffectiveness(typeId, (TypeIdentifier)i, effectiveness);
|
library.SetEffectiveness(typeId, (TypeIdentifier)i, effectiveness);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,5 @@ public class PassTurnAI : PokemonAI
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon) => new PassChoice(pokemon);
|
||||||
{
|
|
||||||
return new PassChoice(pokemon);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -11,7 +11,7 @@ namespace PkmnLib.Dynamic.AI;
|
||||||
public class RandomAI : PokemonAI
|
public class RandomAI : PokemonAI
|
||||||
{
|
{
|
||||||
private IRandom _random;
|
private IRandom _random;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public RandomAI() : base("Random")
|
public RandomAI() : base("Random")
|
||||||
{
|
{
|
||||||
|
@ -45,6 +45,4 @@ public class RandomAI : PokemonAI
|
||||||
}
|
}
|
||||||
return new PassChoice(pokemon);
|
return new PassChoice(pokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ public class CaptureAttemptEvent : IEventData
|
||||||
|
|
||||||
public IPokemon Target { get; init; }
|
public IPokemon Target { get; init; }
|
||||||
public CaptureResult Result { get; init; }
|
public CaptureResult Result { get; init; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; }
|
public EventBatchId BatchId { get; init; }
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ public class DialogEvent : IEventData
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; } = new();
|
public EventBatchId BatchId { get; init; } = new();
|
||||||
|
|
||||||
public DialogEvent(string message, Dictionary<string, object>? parameters = null)
|
public DialogEvent(string message, Dictionary<string, object>? parameters = null)
|
||||||
{
|
{
|
||||||
Message = message;
|
Message = message;
|
||||||
|
@ -12,6 +12,6 @@ public class DialogEvent : IEventData
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
|
|
||||||
public Dictionary<string, object>? Parameters { get; set; }
|
public Dictionary<string, object>? Parameters { get; set; }
|
||||||
}
|
}
|
|
@ -10,12 +10,11 @@ public class ExperienceGainEvent : IEventData
|
||||||
PreviousExperience = previousExperience;
|
PreviousExperience = previousExperience;
|
||||||
NewExperience = newExperience;
|
NewExperience = newExperience;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPokemon Pokemon { get; set; }
|
public IPokemon Pokemon { get; set; }
|
||||||
public uint PreviousExperience { get; }
|
public uint PreviousExperience { get; }
|
||||||
public uint NewExperience { get; }
|
public uint NewExperience { get; }
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; }
|
public EventBatchId BatchId { get; init; }
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ public class FaintEvent : IEventData
|
||||||
/// The Pokemon that fainted.
|
/// The Pokemon that fainted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon Pokemon { get; init; }
|
public IPokemon Pokemon { get; init; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; } = new();
|
public EventBatchId BatchId { get; init; } = new();
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ public class FormChangeEvent : IEventData
|
||||||
Pokemon = pokemon;
|
Pokemon = pokemon;
|
||||||
Form = form;
|
Form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; }
|
public EventBatchId BatchId { get; init; }
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ public readonly record struct EventBatchId
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid();
|
Id = Guid.NewGuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The unique identifier for this batch of events.
|
/// The unique identifier for this batch of events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class EventHook
|
||||||
/// The event handler that is called when the event is triggered.
|
/// The event handler that is called when the event is triggered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<IEventData>? Handler;
|
public event EventHandler<IEventData>? Handler;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Triggers the event, calling the handler with the given data.
|
/// Triggers the event, calling the handler with the given data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -30,7 +30,6 @@ public class HealEvent : IEventData
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint NewHealth { get; }
|
public uint NewHealth { get; }
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; } = new();
|
public EventBatchId BatchId { get; init; } = new();
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ public class MoveMissEvent : IEventData
|
||||||
/// Data about the move that missed.
|
/// Data about the move that missed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IExecutingMove ExecutingMove { get; }
|
public IExecutingMove ExecutingMove { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; }
|
public EventBatchId BatchId { get; init; }
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ public class MoveUseEvent : IEventData
|
||||||
{
|
{
|
||||||
ExecutingMove = executingMove;
|
ExecutingMove = executingMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; }
|
public EventBatchId BatchId { get; init; }
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ public class SpeciesChangeEvent : IEventData
|
||||||
Species = species;
|
Species = species;
|
||||||
Form = form;
|
Form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; }
|
public EventBatchId BatchId { get; init; }
|
||||||
}
|
}
|
|
@ -12,22 +12,22 @@ public interface IBattleStatCalculator
|
||||||
/// Calculate all the flat stats of a Pokemon, disregarding stat boosts.
|
/// Calculate all the flat stats of a Pokemon, disregarding stat boosts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void CalculateFlatStats(IPokemon pokemon, StatisticSet<uint> stats);
|
void CalculateFlatStats(IPokemon pokemon, StatisticSet<uint> stats);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate a single flat stat of a Pokemon, disregarding stat boosts.
|
/// Calculate a single flat stat of a Pokemon, disregarding stat boosts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint CalculateFlatStat(IPokemon pokemon, Statistic stat);
|
uint CalculateFlatStat(IPokemon pokemon, Statistic stat);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
|
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void CalculateBoostedStats(IPokemon pokemon, StatisticSet<uint> stats);
|
void CalculateBoostedStats(IPokemon pokemon, StatisticSet<uint> stats);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
|
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint CalculateBoostedStat(IPokemon pokemon, Statistic stat);
|
uint CalculateBoostedStat(IPokemon pokemon, Statistic stat);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the accuracy for a move, taking into account any accuracy modifiers.
|
/// Calculates the accuracy for a move, taking into account any accuracy modifiers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -15,8 +15,8 @@ public record struct CaptureResult
|
||||||
public bool IsCaught { get; init; }
|
public bool IsCaught { get; init; }
|
||||||
public int Shakes { get; init; }
|
public int Shakes { get; init; }
|
||||||
public bool CriticalCapture { get; init; }
|
public bool CriticalCapture { get; init; }
|
||||||
|
|
||||||
public static CaptureResult Failed => new CaptureResult(false, 0, false);
|
public static CaptureResult Failed => new(false, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ICaptureLibrary
|
public interface ICaptureLibrary
|
||||||
|
|
|
@ -31,12 +31,12 @@ public interface IDynamicLibrary
|
||||||
/// calculators.
|
/// calculators.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IMiscLibrary MiscLibrary { get; }
|
IMiscLibrary MiscLibrary { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The capture library deals with the calculation of the capture rate of a Pokémon.
|
/// The capture library deals with the calculation of the capture rate of a Pokémon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ICaptureLibrary CaptureLibrary { get; }
|
ICaptureLibrary CaptureLibrary { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A holder of the script types that can be resolved by this library.
|
/// A holder of the script types that can be resolved by this library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -66,12 +66,13 @@ public class DynamicLibraryImpl : IDynamicLibrary
|
||||||
if (registry.CaptureLibrary is null)
|
if (registry.CaptureLibrary is null)
|
||||||
throw new InvalidOperationException("Capture library not found in plugins.");
|
throw new InvalidOperationException("Capture library not found in plugins.");
|
||||||
var scriptResolver = new ScriptResolver(registry.ScriptTypes, registry.ItemScriptTypes);
|
var scriptResolver = new ScriptResolver(registry.ScriptTypes, registry.ItemScriptTypes);
|
||||||
return new DynamicLibraryImpl(staticLibrary, registry.BattleStatCalculator,
|
return new DynamicLibraryImpl(staticLibrary, registry.BattleStatCalculator, registry.DamageCalculator,
|
||||||
registry.DamageCalculator, registry.MiscLibrary, registry.CaptureLibrary, scriptResolver);
|
registry.MiscLibrary, registry.CaptureLibrary, scriptResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DynamicLibraryImpl(IStaticLibrary staticLibrary, IBattleStatCalculator statCalculator,
|
private DynamicLibraryImpl(IStaticLibrary staticLibrary, IBattleStatCalculator statCalculator,
|
||||||
IDamageCalculator damageCalculator, IMiscLibrary miscLibrary, ICaptureLibrary captureLibrary, ScriptResolver scriptResolver)
|
IDamageCalculator damageCalculator, IMiscLibrary miscLibrary, ICaptureLibrary captureLibrary,
|
||||||
|
ScriptResolver scriptResolver)
|
||||||
{
|
{
|
||||||
StaticLibrary = staticLibrary;
|
StaticLibrary = staticLibrary;
|
||||||
StatCalculator = statCalculator;
|
StatCalculator = statCalculator;
|
||||||
|
|
|
@ -14,7 +14,7 @@ public interface IMiscLibrary
|
||||||
/// moves left, yet wants to make a move.
|
/// moves left, yet wants to make a move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ITurnChoice ReplacementChoice(IPokemon user, byte targetSide, byte targetPosition);
|
ITurnChoice ReplacementChoice(IPokemon user, byte targetSide, byte targetPosition);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current time of day for the battle.
|
/// Gets the current time of day for the battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -94,7 +94,7 @@ public interface IBattle : IScriptSource, IDeepCloneable
|
||||||
void ValidateBattleState();
|
void ValidateBattleState();
|
||||||
|
|
||||||
bool HasForcedTurn(IPokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice);
|
bool HasForcedTurn(IPokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether a choice is actually possible.
|
/// Checks whether a choice is actually possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -111,22 +111,22 @@ public interface IBattle : IScriptSource, IDeepCloneable
|
||||||
void SetWeather(StringKey? weatherName);
|
void SetWeather(StringKey? weatherName);
|
||||||
|
|
||||||
public IScriptSet Volatile { get; }
|
public IScriptSet Volatile { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current weather of the battle. If no weather is present, this returns null.
|
/// Gets the current weather of the battle. If no weather is present, this returns null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
StringKey? WeatherName { get; }
|
StringKey? WeatherName { get; }
|
||||||
|
|
||||||
void SetTerrain(StringKey? terrainName);
|
void SetTerrain(StringKey? terrainName);
|
||||||
|
|
||||||
StringKey? TerrainName { get; }
|
StringKey? TerrainName { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the turn choices of the previous turn. This is a list of lists, where each list represents the choices
|
/// Gets the turn choices of the previous turn. This is a list of lists, where each list represents the choices
|
||||||
/// for a single turn. The outer list is ordered from oldest to newest turn.
|
/// for a single turn. The outer list is ordered from oldest to newest turn.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<IReadOnlyList<ITurnChoice>> PreviousTurnChoices { get; }
|
IReadOnlyList<IReadOnlyList<ITurnChoice>> PreviousTurnChoices { get; }
|
||||||
|
|
||||||
CaptureResult AttempCapture(byte sideIndex, byte position, IItem item);
|
CaptureResult AttempCapture(byte sideIndex, byte position, IItem item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,10 +247,10 @@ public class BattleImpl : ScriptSource, IBattle
|
||||||
choice = null;
|
choice = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ITurnChoice? forcedChoice = null;
|
ITurnChoice? forcedChoice = null;
|
||||||
pokemon.RunScriptHook(
|
pokemon.RunScriptHook(script =>
|
||||||
script => script.ForceTurnSelection(battleData.SideIndex, battleData.Position, ref forcedChoice));
|
script.ForceTurnSelection(battleData.SideIndex, battleData.Position, ref forcedChoice));
|
||||||
choice = forcedChoice;
|
choice = forcedChoice;
|
||||||
return choice != null;
|
return choice != null;
|
||||||
}
|
}
|
||||||
|
@ -262,7 +262,7 @@ public class BattleImpl : ScriptSource, IBattle
|
||||||
return false;
|
return false;
|
||||||
if (HasForcedTurn(choice.User, out var forcedChoice) && choice != forcedChoice)
|
if (HasForcedTurn(choice.User, out var forcedChoice) && choice != forcedChoice)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (choice is IMoveChoice moveChoice)
|
if (choice is IMoveChoice moveChoice)
|
||||||
{
|
{
|
||||||
// TODO: Hook to change number of PP needed.
|
// TODO: Hook to change number of PP needed.
|
||||||
|
@ -365,7 +365,7 @@ public class BattleImpl : ScriptSource, IBattle
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public StringKey? WeatherName => _weatherScript.Script?.Name;
|
public StringKey? WeatherName => _weatherScript.Script?.Name;
|
||||||
|
|
||||||
private readonly ScriptContainer _terrainScript = new();
|
private readonly ScriptContainer _terrainScript = new();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -386,7 +386,6 @@ public class BattleImpl : ScriptSource, IBattle
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public StringKey? TerrainName => _terrainScript.Script?.Name;
|
public StringKey? TerrainName => _terrainScript.Script?.Name;
|
||||||
|
|
||||||
|
|
||||||
private readonly List<IReadOnlyList<ITurnChoice>> _previousTurnChoices = new();
|
private readonly List<IReadOnlyList<ITurnChoice>> _previousTurnChoices = new();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -398,7 +397,7 @@ public class BattleImpl : ScriptSource, IBattle
|
||||||
var target = GetPokemon(sideIndex, position);
|
var target = GetPokemon(sideIndex, position);
|
||||||
if (target is not { IsUsable: true })
|
if (target is not { IsUsable: true })
|
||||||
return CaptureResult.Failed;
|
return CaptureResult.Failed;
|
||||||
|
|
||||||
var attemptCapture = Library.CaptureLibrary.TryCapture(target, item, Random);
|
var attemptCapture = Library.CaptureLibrary.TryCapture(target, item, Random);
|
||||||
if (attemptCapture.IsCaught)
|
if (attemptCapture.IsCaught)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class BattleChoiceQueue : IDeepCloneable
|
||||||
{
|
{
|
||||||
_choices = choices;
|
_choices = choices;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dequeues the next turn choice to be executed. This gives back the choice and sets it to null in the queue. It
|
/// Dequeues the next turn choice to be executed. This gives back the choice and sets it to null in the queue. It
|
||||||
/// also increments the internal index.
|
/// also increments the internal index.
|
||||||
|
@ -40,18 +40,17 @@ public class BattleChoiceQueue : IDeepCloneable
|
||||||
LastRanChoice = choice;
|
LastRanChoice = choice;
|
||||||
return choice;
|
return choice;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This reads what the next choice to execute will be, without modifying state.
|
/// This reads what the next choice to execute will be, without modifying state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ITurnChoice? Peek() => _currentIndex >= _choices.Length ? null : _choices[_currentIndex];
|
public ITurnChoice? Peek() => _currentIndex >= _choices.Length ? null : _choices[_currentIndex];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if there are any more choices to execute.
|
/// Checks if there are any more choices to execute.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasNext() => _currentIndex < _choices.Length;
|
public bool HasNext() => _currentIndex < _choices.Length;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This resorts the yet to be executed choices. This can be useful for dealing with situations
|
/// This resorts the yet to be executed choices. This can be useful for dealing with situations
|
||||||
/// such as Pokémon changing forms just after the very start of a turn, when turn order has
|
/// such as Pokémon changing forms just after the very start of a turn, when turn order has
|
||||||
|
@ -72,7 +71,7 @@ public class BattleChoiceQueue : IDeepCloneable
|
||||||
choice.User.RunScriptHook(script => script.ChangeSpeed(choice, ref speed));
|
choice.User.RunScriptHook(script => script.ChangeSpeed(choice, ref speed));
|
||||||
choice.Speed = speed;
|
choice.Speed = speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only sort the choices that are left
|
// We only sort the choices that are left
|
||||||
Array.Sort(_choices, currentIndex, length - currentIndex, TurnChoiceComparer.Instance!);
|
Array.Sort(_choices, currentIndex, length - currentIndex, TurnChoiceComparer.Instance!);
|
||||||
}
|
}
|
||||||
|
@ -99,8 +98,9 @@ public class BattleChoiceQueue : IDeepCloneable
|
||||||
_choices[_currentIndex] = choice;
|
_choices[_currentIndex] = choice;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IReadOnlyList<ITurnChoice?> GetChoices() => _choices;
|
internal IReadOnlyList<ITurnChoice?> GetChoices() => _choices;
|
||||||
|
|
||||||
public ITurnChoice? FirstOrDefault(Func<ITurnChoice, bool> predicate) => _choices.WhereNotNull().FirstOrDefault(predicate);
|
public ITurnChoice? FirstOrDefault(Func<ITurnChoice, bool> predicate) =>
|
||||||
|
_choices.WhereNotNull().FirstOrDefault(predicate);
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ internal static class MoveTurnExecutor
|
||||||
{
|
{
|
||||||
var chosenMove = moveChoice.ChosenMove;
|
var chosenMove = moveChoice.ChosenMove;
|
||||||
var moveData = chosenMove.MoveData;
|
var moveData = chosenMove.MoveData;
|
||||||
|
|
||||||
var moveDataName = moveData.Name;
|
var moveDataName = moveData.Name;
|
||||||
moveChoice.RunScriptHook(x => x.ChangeMove(moveChoice, ref moveDataName));
|
moveChoice.RunScriptHook(x => x.ChangeMove(moveChoice, ref moveDataName));
|
||||||
if (moveData.Name != moveDataName)
|
if (moveData.Name != moveDataName)
|
||||||
|
@ -31,11 +31,11 @@ internal static class MoveTurnExecutor
|
||||||
moveChoice.Script.Set(script);
|
moveChoice.Script.Set(script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetType = moveData.Target;
|
var targetType = moveData.Target;
|
||||||
var targets = TargetResolver.ResolveTargets(battle, moveChoice.TargetSide, moveChoice.TargetPosition, targetType);
|
var targets =
|
||||||
|
TargetResolver.ResolveTargets(battle, moveChoice.TargetSide, moveChoice.TargetPosition, targetType);
|
||||||
moveChoice.RunScriptHook(x => x.ChangeTargets(moveChoice, ref targets));
|
moveChoice.RunScriptHook(x => x.ChangeTargets(moveChoice, ref targets));
|
||||||
|
|
||||||
byte numberOfHits = 1;
|
byte numberOfHits = 1;
|
||||||
|
@ -46,7 +46,7 @@ internal static class MoveTurnExecutor
|
||||||
}
|
}
|
||||||
|
|
||||||
var executingMove = new ExecutingMoveImpl(targets, numberOfHits, chosenMove, moveData, moveChoice);
|
var executingMove = new ExecutingMoveImpl(targets, numberOfHits, chosenMove, moveData, moveChoice);
|
||||||
|
|
||||||
var prevented = false;
|
var prevented = false;
|
||||||
executingMove.RunScriptHook(x => x.PreventMove(executingMove, ref prevented));
|
executingMove.RunScriptHook(x => x.PreventMove(executingMove, ref prevented));
|
||||||
if (prevented)
|
if (prevented)
|
||||||
|
@ -56,9 +56,9 @@ internal static class MoveTurnExecutor
|
||||||
// TODO: Modify the PP used by the move.
|
// TODO: Modify the PP used by the move.
|
||||||
if (!executingMove.ChosenMove.TryUse(ppUsed))
|
if (!executingMove.ChosenMove.TryUse(ppUsed))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
battle.EventHook.Invoke(new MoveUseEvent(executingMove));
|
battle.EventHook.Invoke(new MoveUseEvent(executingMove));
|
||||||
|
|
||||||
var failed = false;
|
var failed = false;
|
||||||
executingMove.RunScriptHook(x => x.FailMove(executingMove, ref failed));
|
executingMove.RunScriptHook(x => x.FailMove(executingMove, ref failed));
|
||||||
if (failed)
|
if (failed)
|
||||||
|
@ -66,12 +66,12 @@ internal static class MoveTurnExecutor
|
||||||
// TODO: fail handling
|
// TODO: fail handling
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stopped = false;
|
var stopped = false;
|
||||||
executingMove.RunScriptHook(x => x.StopBeforeMove(executingMove, ref stopped));
|
executingMove.RunScriptHook(x => x.StopBeforeMove(executingMove, ref stopped));
|
||||||
if (stopped)
|
if (stopped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
executingMove.RunScriptHook(x => x.OnBeforeMove(executingMove));
|
executingMove.RunScriptHook(x => x.OnBeforeMove(executingMove));
|
||||||
foreach (var target in targets.WhereNotNull())
|
foreach (var target in targets.WhereNotNull())
|
||||||
{
|
{
|
||||||
|
@ -88,7 +88,7 @@ internal static class MoveTurnExecutor
|
||||||
// TODO: fail handling
|
// TODO: fail handling
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isInvulnerable = false;
|
var isInvulnerable = false;
|
||||||
target.RunScriptHook(x => x.IsInvulnerableToMove(executingMove, target, ref isInvulnerable));
|
target.RunScriptHook(x => x.IsInvulnerableToMove(executingMove, target, ref isInvulnerable));
|
||||||
if (isInvulnerable)
|
if (isInvulnerable)
|
||||||
|
@ -96,7 +96,7 @@ internal static class MoveTurnExecutor
|
||||||
// TODO: event?
|
// TODO: event?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var numberOfHits = executingMove.NumberOfHits;
|
var numberOfHits = executingMove.NumberOfHits;
|
||||||
var targetHitStat = executingMove.GetTargetIndex(target) * numberOfHits;
|
var targetHitStat = executingMove.GetTargetIndex(target) * numberOfHits;
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ internal static class MoveTurnExecutor
|
||||||
var effectiveness = battle.Library.StaticLibrary.Types.GetEffectiveness(hitType, target.Types);
|
var effectiveness = battle.Library.StaticLibrary.Types.GetEffectiveness(hitType, target.Types);
|
||||||
executingMove.RunScriptHook(x => x.ChangeEffectiveness(executingMove, target, hitIndex, ref effectiveness));
|
executingMove.RunScriptHook(x => x.ChangeEffectiveness(executingMove, target, hitIndex, ref effectiveness));
|
||||||
hitData.Effectiveness = effectiveness;
|
hitData.Effectiveness = effectiveness;
|
||||||
|
|
||||||
var blockCritical = false;
|
var blockCritical = false;
|
||||||
executingMove.RunScriptHook(x => x.BlockCriticalHit(executingMove, target, hitIndex, ref blockCritical));
|
executingMove.RunScriptHook(x => x.BlockCriticalHit(executingMove, target, hitIndex, ref blockCritical));
|
||||||
target.RunScriptHook(x => x.BlockIncomingCriticalHit(executingMove, target, hitIndex, ref blockCritical));
|
target.RunScriptHook(x => x.BlockIncomingCriticalHit(executingMove, target, hitIndex, ref blockCritical));
|
||||||
|
@ -129,10 +129,10 @@ internal static class MoveTurnExecutor
|
||||||
var critical = battle.Library.DamageCalculator.IsCritical(battle, executingMove, target, hitIndex);
|
var critical = battle.Library.DamageCalculator.IsCritical(battle, executingMove, target, hitIndex);
|
||||||
hitData.IsCritical = critical;
|
hitData.IsCritical = critical;
|
||||||
}
|
}
|
||||||
|
|
||||||
var basePower = battle.Library.DamageCalculator.GetBasePower(executingMove, target, hitIndex, hitData);
|
var basePower = battle.Library.DamageCalculator.GetBasePower(executingMove, target, hitIndex, hitData);
|
||||||
hitData.BasePower = basePower;
|
hitData.BasePower = basePower;
|
||||||
|
|
||||||
hitData.Damage = battle.Library.DamageCalculator.GetDamage(executingMove, target, hitIndex, hitData);
|
hitData.Damage = battle.Library.DamageCalculator.GetDamage(executingMove, target, hitIndex, hitData);
|
||||||
|
|
||||||
var accuracy = useMove.Accuracy;
|
var accuracy = useMove.Accuracy;
|
||||||
|
@ -140,17 +140,17 @@ internal static class MoveTurnExecutor
|
||||||
// modifying it.
|
// modifying it.
|
||||||
if (accuracy != 255)
|
if (accuracy != 255)
|
||||||
{
|
{
|
||||||
accuracy = battle.Library.StatCalculator.CalculateModifiedAccuracy(executingMove, target,
|
accuracy = battle.Library.StatCalculator.CalculateModifiedAccuracy(executingMove, target, hitIndex,
|
||||||
hitIndex, accuracy);
|
accuracy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accuracy < 100 && battle.Random.GetInt(100) >= accuracy)
|
if (accuracy < 100 && battle.Random.GetInt(100) >= accuracy)
|
||||||
{
|
{
|
||||||
executingMove.RunScriptHook(x => x.OnMoveMiss(executingMove, target));
|
executingMove.RunScriptHook(x => x.OnMoveMiss(executingMove, target));
|
||||||
battle.EventHook.Invoke(new MoveMissEvent(executingMove));
|
battle.EventHook.Invoke(new MoveMissEvent(executingMove));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var blockIncomingHit = false;
|
var blockIncomingHit = false;
|
||||||
target.RunScriptHook(x => x.BlockIncomingHit(executingMove, target, hitIndex, ref blockIncomingHit));
|
target.RunScriptHook(x => x.BlockIncomingHit(executingMove, target, hitIndex, ref blockIncomingHit));
|
||||||
executingMove.RunScriptHook(x => x.BlockOutgoingHit(executingMove, target, hitIndex, ref blockIncomingHit));
|
executingMove.RunScriptHook(x => x.BlockOutgoingHit(executingMove, target, hitIndex, ref blockIncomingHit));
|
||||||
|
@ -187,7 +187,7 @@ internal static class MoveTurnExecutor
|
||||||
BatchId = hitEventBatch,
|
BatchId = hitEventBatch,
|
||||||
});
|
});
|
||||||
target.Damage(damage, DamageSource.MoveDamage, hitEventBatch);
|
target.Damage(damage, DamageSource.MoveDamage, hitEventBatch);
|
||||||
if (!target.IsFainted)
|
if (!target.IsFainted)
|
||||||
target.RunScriptHook(x => x.OnIncomingHit(executingMove, target, hitIndex));
|
target.RunScriptHook(x => x.OnIncomingHit(executingMove, target, hitIndex));
|
||||||
else
|
else
|
||||||
executingMove.RunScriptHook(x => x.OnOpponentFaints(executingMove, target, hitIndex));
|
executingMove.RunScriptHook(x => x.OnOpponentFaints(executingMove, target, hitIndex));
|
||||||
|
@ -198,14 +198,16 @@ internal static class MoveTurnExecutor
|
||||||
if (secondaryEffect != null)
|
if (secondaryEffect != null)
|
||||||
{
|
{
|
||||||
var preventSecondary = false;
|
var preventSecondary = false;
|
||||||
target.RunScriptHook(x => x.PreventSecondaryEffect(executingMove, target, hitIndex, ref preventSecondary));
|
target.RunScriptHook(x =>
|
||||||
|
x.PreventSecondaryEffect(executingMove, target, hitIndex, ref preventSecondary));
|
||||||
|
|
||||||
if (!preventSecondary)
|
if (!preventSecondary)
|
||||||
{
|
{
|
||||||
var chance = secondaryEffect.Chance;
|
var chance = secondaryEffect.Chance;
|
||||||
if (chance < 0 || battle.Random.EffectChance(chance, executingMove, target, hitIndex))
|
if (chance < 0 || battle.Random.EffectChance(chance, executingMove, target, hitIndex))
|
||||||
{
|
{
|
||||||
executingMove.RunScriptHook(x => x.OnSecondaryEffect(executingMove, target, hitIndex));
|
executingMove.RunScriptHook(x =>
|
||||||
|
x.OnSecondaryEffect(executingMove, target, hitIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,7 +215,7 @@ internal static class MoveTurnExecutor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numberOfHits == 0)
|
if (numberOfHits == 0)
|
||||||
{
|
{
|
||||||
target.RunScriptHook(x => x.OnMoveMiss(executingMove, target));
|
target.RunScriptHook(x => x.OnMoveMiss(executingMove, target));
|
||||||
|
@ -224,6 +226,5 @@ internal static class MoveTurnExecutor
|
||||||
{
|
{
|
||||||
executingMove.RunScriptHook(x => x.OnAfterHits(executingMove, target));
|
executingMove.RunScriptHook(x => x.OnAfterHits(executingMove, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,8 +15,8 @@ public static class TargetResolver
|
||||||
return target switch
|
return target switch
|
||||||
{
|
{
|
||||||
MoveTarget.Adjacent or MoveTarget.AdjacentAlly or MoveTarget.AdjacentAllySelf or MoveTarget.AdjacentOpponent
|
MoveTarget.Adjacent or MoveTarget.AdjacentAlly or MoveTarget.AdjacentAllySelf or MoveTarget.AdjacentOpponent
|
||||||
or MoveTarget.Any or MoveTarget.RandomOpponent
|
or MoveTarget.Any or MoveTarget.RandomOpponent or MoveTarget.SelfUse =>
|
||||||
or MoveTarget.SelfUse => [battle.GetPokemon(side, position)],
|
[battle.GetPokemon(side, position)],
|
||||||
MoveTarget.All => GetAllTargets(battle),
|
MoveTarget.All => GetAllTargets(battle),
|
||||||
MoveTarget.AllAdjacentOpponent => GetAllAdjacentAndOpponent(battle, side, position),
|
MoveTarget.AllAdjacentOpponent => GetAllAdjacentAndOpponent(battle, side, position),
|
||||||
MoveTarget.AllAdjacent => GetAllAdjacent(battle, side, position),
|
MoveTarget.AllAdjacent => GetAllAdjacent(battle, side, position),
|
||||||
|
@ -144,7 +144,8 @@ public static class TargetResolver
|
||||||
|
|
||||||
return
|
return
|
||||||
[
|
[
|
||||||
battle.GetPokemon(side, position), battle.GetPokemon(side, (byte)left), battle.GetPokemon(side, (byte)right),
|
battle.GetPokemon(side, position), battle.GetPokemon(side, (byte)left),
|
||||||
|
battle.GetPokemon(side, (byte)right),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,9 +16,11 @@ public static class TurnRunner
|
||||||
{
|
{
|
||||||
var queue = battle.ChoiceQueue;
|
var queue = battle.ChoiceQueue;
|
||||||
if (queue == null)
|
if (queue == null)
|
||||||
|
{
|
||||||
throw new ArgumentNullException(nameof(battle.ChoiceQueue),
|
throw new ArgumentNullException(nameof(battle.ChoiceQueue),
|
||||||
"The battle's choice queue must be set before running a turn.");
|
"The battle's choice queue must be set before running a turn.");
|
||||||
|
}
|
||||||
|
|
||||||
// We are now at the very beginning of a turn. We have assigned speeds and priorities to all
|
// We are now at the very beginning of a turn. We have assigned speeds and priorities to all
|
||||||
// choices, and put them in the correct order.
|
// choices, and put them in the correct order.
|
||||||
|
|
||||||
|
@ -30,7 +32,7 @@ public static class TurnRunner
|
||||||
{
|
{
|
||||||
choice.RunScriptHook(script => script.OnBeforeTurnStart(choice));
|
choice.RunScriptHook(script => script.OnBeforeTurnStart(choice));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we can properly begin executing choices.
|
// Now we can properly begin executing choices.
|
||||||
// One by one dequeue the turns, and run them. If the battle has ended we do not want to
|
// One by one dequeue the turns, and run them. If the battle has ended we do not want to
|
||||||
// continue running.
|
// continue running.
|
||||||
|
@ -41,7 +43,7 @@ public static class TurnRunner
|
||||||
continue;
|
continue;
|
||||||
ExecuteChoice(battle, next);
|
ExecuteChoice(battle, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the battle is not ended, we have arrived at the normal end of a turn. and thus want
|
// If the battle is not ended, we have arrived at the normal end of a turn. and thus want
|
||||||
// to run the end turn scripts.
|
// to run the end turn scripts.
|
||||||
|
|
||||||
|
@ -122,7 +124,6 @@ public static class TurnRunner
|
||||||
userSide.SwapPokemon(battleData.Position, fleeChoice.SwitchTo);
|
userSide.SwapPokemon(battleData.Position, fleeChoice.SwitchTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void ExecuteFleeChoice(IBattle battle, IFleeChoice fleeChoice)
|
private static void ExecuteFleeChoice(IBattle battle, IFleeChoice fleeChoice)
|
||||||
{
|
{
|
||||||
var user = fleeChoice.User;
|
var user = fleeChoice.User;
|
||||||
|
@ -131,7 +132,7 @@ public static class TurnRunner
|
||||||
return;
|
return;
|
||||||
if (!battle.CanFlee)
|
if (!battle.CanFlee)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var preventFlee = false;
|
var preventFlee = false;
|
||||||
fleeChoice.RunScriptHook(script => script.PreventSelfRunAway(fleeChoice, ref preventFlee));
|
fleeChoice.RunScriptHook(script => script.PreventSelfRunAway(fleeChoice, ref preventFlee));
|
||||||
if (preventFlee)
|
if (preventFlee)
|
||||||
|
@ -148,10 +149,10 @@ public static class TurnRunner
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!battle.Library.MiscLibrary.CanFlee(battle, fleeChoice))
|
if (!battle.Library.MiscLibrary.CanFlee(battle, fleeChoice))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var userSide = battle.Sides[battleData.SideIndex];
|
var userSide = battle.Sides[battleData.SideIndex];
|
||||||
userSide.MarkAsFled();
|
userSide.MarkAsFled();
|
||||||
battle.ValidateBattleState();
|
battle.ValidateBattleState();
|
||||||
|
@ -171,5 +172,4 @@ public static class TurnRunner
|
||||||
}
|
}
|
||||||
itemChoice.Item.RunItemScript(battle.Library.ScriptResolver, target ?? user);
|
itemChoice.Item.RunItemScript(battle.Library.ScriptResolver, target ?? user);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -10,22 +10,22 @@ public record struct BattleResult
|
||||||
ConclusiveResult = conclusiveResult;
|
ConclusiveResult = conclusiveResult;
|
||||||
WinningSide = winningSide;
|
WinningSide = winningSide;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An inconclusive battle result. This means no side has won.
|
/// An inconclusive battle result. This means no side has won.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static BattleResult Inconclusive => new(false, null);
|
public static BattleResult Inconclusive => new(false, null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A conclusive battle result. This means one side has won.
|
/// A conclusive battle result. This means one side has won.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static BattleResult Conclusive(byte winningSide) => new(true, winningSide);
|
public static BattleResult Conclusive(byte winningSide) => new(true, winningSide);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the battle has a conclusive result. If false, no side has won.
|
/// Whether the battle has a conclusive result. If false, no side has won.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ConclusiveResult { get; }
|
public bool ConclusiveResult { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The side that won the battle. If null, no side has won.
|
/// The side that won the battle. If null, no side has won.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -14,48 +14,48 @@ public interface IBattleSide : IScriptSource, IDeepCloneable
|
||||||
/// The index of the side on the battle.
|
/// The index of the side on the battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte Index { get; }
|
byte Index { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of Pokémon that can be on the side.
|
/// The number of Pokémon that can be on the side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte NumberOfPositions { get; }
|
byte NumberOfPositions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of Pokémon currently on the battlefield.
|
/// A list of Pokémon currently on the battlefield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<IPokemon?> Pokemon { get; }
|
IReadOnlyList<IPokemon?> Pokemon { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently set choices for all Pokémon on the battlefield. Cleared when the turn starts.
|
/// The currently set choices for all Pokémon on the battlefield. Cleared when the turn starts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<ITurnChoice?> SetChoices { get; }
|
IReadOnlyList<ITurnChoice?> SetChoices { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether every Pokémon on this side has its choices
|
/// Whether every Pokémon on this side has its choices
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool AllChoicesSet { get; }
|
bool AllChoicesSet { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The slots on the side that can still be filled. Once all slots are set to false, this side
|
/// The slots on the side that can still be filled. Once all slots are set to false, this side
|
||||||
/// has lost the battle.
|
/// has lost the battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<bool> FillablePositions { get; }
|
IReadOnlyList<bool> FillablePositions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A reference to the battle this side is in.
|
/// A reference to the battle this side is in.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IBattle Battle { get; }
|
IBattle Battle { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this side has fled.
|
/// Whether this side has fled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool HasFledBattle { get; }
|
bool HasFledBattle { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The volatile scripts that are attached to the side.
|
/// The volatile scripts that are attached to the side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IScriptSet VolatileScripts { get; }
|
IScriptSet VolatileScripts { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if there are slots that need to be filled with a new pokemon, that have parties
|
/// Returns true if there are slots that need to be filled with a new pokemon, that have parties
|
||||||
/// responsible for them. Returns false if all slots are filled with usable pokemon, or slots are
|
/// responsible for them. Returns false if all slots are filled with usable pokemon, or slots are
|
||||||
|
@ -67,7 +67,7 @@ public interface IBattleSide : IScriptSource, IDeepCloneable
|
||||||
/// Sets a choice for a Pokémon on this side.
|
/// Sets a choice for a Pokémon on this side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void SetChoice(byte position, ITurnChoice choice);
|
void SetChoice(byte position, ITurnChoice choice);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets all choices on this side.
|
/// Resets all choices on this side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -110,12 +110,12 @@ public interface IBattleSide : IScriptSource, IDeepCloneable
|
||||||
/// Checks whether the side has been defeated.
|
/// Checks whether the side has been defeated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsDefeated();
|
bool IsDefeated();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of times this side has attempted to flee.
|
/// The number of times this side has attempted to flee.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint FleeAttempts { get; }
|
uint FleeAttempts { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a flee attempt for this side.
|
/// Registers a flee attempt for this side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -150,7 +150,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||||
Battle = battle;
|
Battle = battle;
|
||||||
VolatileScripts = new ScriptSet();
|
VolatileScripts = new ScriptSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public byte Index { get; }
|
public byte Index { get; }
|
||||||
|
|
||||||
|
@ -158,6 +158,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||||
public byte NumberOfPositions { get; }
|
public byte NumberOfPositions { get; }
|
||||||
|
|
||||||
private readonly IPokemon?[] _pokemon;
|
private readonly IPokemon?[] _pokemon;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<IPokemon?> Pokemon => _pokemon;
|
public IReadOnlyList<IPokemon?> Pokemon => _pokemon;
|
||||||
|
|
||||||
|
@ -170,6 +171,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||||
public bool AllChoicesSet => _setChoices.All(choice => choice is not null);
|
public bool AllChoicesSet => _setChoices.All(choice => choice is not null);
|
||||||
|
|
||||||
private readonly bool[] _fillablePositions;
|
private readonly bool[] _fillablePositions;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<bool> FillablePositions => _fillablePositions;
|
public IReadOnlyList<bool> FillablePositions => _fillablePositions;
|
||||||
|
|
||||||
|
@ -223,7 +225,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||||
pokemon.RunScriptHook(script => script.OnRemove());
|
pokemon.RunScriptHook(script => script.OnRemove());
|
||||||
pokemon.SetOnBattlefield(false);
|
pokemon.SetOnBattlefield(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_pokemon[index] = null;
|
_pokemon[index] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +261,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||||
{
|
{
|
||||||
Battle.EventHook.Invoke(new SwitchEvent(Index, position, null));
|
Battle.EventHook.Invoke(new SwitchEvent(Index, position, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
return oldPokemon;
|
return oldPokemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ namespace PkmnLib.Dynamic.Models.Choices;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IFleeChoice : ITurnChoice
|
public interface IFleeChoice : ITurnChoice
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IFleeChoice"/>
|
/// <inheritdoc cref="IFleeChoice"/>
|
||||||
|
@ -22,7 +21,9 @@ public class FleeTurnChoice : TurnChoice, IFleeChoice
|
||||||
public override int ScriptCount => User.ScriptCount;
|
public override int ScriptCount => User.ScriptCount;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts) { }
|
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts) => User.CollectScripts(scripts);
|
public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts) => User.CollectScripts(scripts);
|
||||||
|
|
|
@ -12,7 +12,7 @@ public interface IItemChoice : ITurnChoice
|
||||||
/// The item that is used.
|
/// The item that is used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IItem Item { get; }
|
public IItem Item { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The side the move is targeted at.
|
/// The side the move is targeted at.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -32,9 +32,9 @@ public interface IMoveChoice : ITurnChoice
|
||||||
/// The underlying script of the move.
|
/// The underlying script of the move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ScriptContainer Script { get; set; }
|
ScriptContainer Script { get; set; }
|
||||||
|
|
||||||
Dictionary<StringKey, object?>? AdditionalData { get; }
|
Dictionary<StringKey, object?>? AdditionalData { get; }
|
||||||
|
|
||||||
IScriptSet Volatile { get; }
|
IScriptSet Volatile { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public class MoveChoice : TurnChoice, IMoveChoice
|
||||||
ChosenMove = usedMove;
|
ChosenMove = usedMove;
|
||||||
TargetSide = targetSide;
|
TargetSide = targetSide;
|
||||||
TargetPosition = targetPosition;
|
TargetPosition = targetPosition;
|
||||||
|
|
||||||
var secondaryEffect = usedMove.MoveData.SecondaryEffect;
|
var secondaryEffect = usedMove.MoveData.SecondaryEffect;
|
||||||
if (secondaryEffect != null)
|
if (secondaryEffect != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,6 @@ namespace PkmnLib.Dynamic.Models.Choices;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IPassChoice : ITurnChoice
|
public interface IPassChoice : ITurnChoice
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PassChoice : TurnChoice, IPassChoice
|
public class PassChoice : TurnChoice, IPassChoice
|
||||||
|
|
|
@ -7,7 +7,7 @@ public class TurnChoiceComparer : IComparer<ITurnChoice>
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="TurnChoiceComparer"/>
|
/// <inheritdoc cref="TurnChoiceComparer"/>
|
||||||
public static TurnChoiceComparer Instance { get; } = new();
|
public static TurnChoiceComparer Instance { get; } = new();
|
||||||
|
|
||||||
private enum CompareValues
|
private enum CompareValues
|
||||||
{
|
{
|
||||||
XEqualsY = 0,
|
XEqualsY = 0,
|
||||||
|
@ -25,7 +25,7 @@ public class TurnChoiceComparer : IComparer<ITurnChoice>
|
||||||
// This is to ensure that the order of choices is deterministic.
|
// This is to ensure that the order of choices is deterministic.
|
||||||
return (CompareValues)x.RandomValue.CompareTo(y.RandomValue);
|
return (CompareValues)x.RandomValue.CompareTo(y.RandomValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CompareValues CompareImpl(ITurnChoice? x, ITurnChoice? y)
|
private static CompareValues CompareImpl(ITurnChoice? x, ITurnChoice? y)
|
||||||
{
|
{
|
||||||
// Deal with possible null values
|
// Deal with possible null values
|
||||||
|
@ -77,10 +77,10 @@ public class TurnChoiceComparer : IComparer<ITurnChoice>
|
||||||
_ => CompareValues.XGreaterThanY,
|
_ => CompareValues.XGreaterThanY,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompareValues.XLessThanY;
|
return CompareValues.XLessThanY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Compare(ITurnChoice? x, ITurnChoice? y) => (int) CompareImpl(x, y);
|
public int Compare(ITurnChoice? x, ITurnChoice? y) => (int)CompareImpl(x, y);
|
||||||
}
|
}
|
|
@ -19,13 +19,13 @@ public enum DamageSource
|
||||||
/// The damage is done because of struggling.
|
/// The damage is done because of struggling.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Struggle = 2,
|
Struggle = 2,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The damage is done because of a form change.
|
/// The damage is done because of a form change.
|
||||||
/// This happens when the form of a Pokemon changes, and it has less max HP than it had before.
|
/// This happens when the form of a Pokemon changes, and it has less max HP than it had before.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
FormChange = 3,
|
FormChange = 3,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The damage is done because of the weather.
|
/// The damage is done because of the weather.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -129,12 +129,12 @@ public interface IExecutingMove : IScriptSource
|
||||||
/// Gets a hit based on its raw index.
|
/// Gets a hit based on its raw index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IHitData GetDataFromRawIndex(int index);
|
IHitData GetDataFromRawIndex(int index);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the targets of this move.
|
/// Gets the targets of this move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<IPokemon?> Targets { get; }
|
IReadOnlyList<IPokemon?> Targets { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The underlying move choice.
|
/// The underlying move choice.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -54,7 +54,7 @@ public interface ILearnedMove : IDeepCloneable
|
||||||
/// The maximal power points for this move.
|
/// The maximal power points for this move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte MaxPp { get; }
|
byte MaxPp { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current power points for this move.
|
/// The current power points for this move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -86,7 +86,7 @@ public interface ILearnedMove : IDeepCloneable
|
||||||
public class LearnedMoveImpl : ILearnedMove
|
public class LearnedMoveImpl : ILearnedMove
|
||||||
{
|
{
|
||||||
private byte _maxPpModification = 0;
|
private byte _maxPpModification = 0;
|
||||||
|
|
||||||
/// <inheritdoc cref="LearnedMoveImpl" />
|
/// <inheritdoc cref="LearnedMoveImpl" />
|
||||||
public LearnedMoveImpl(IMoveData moveData, MoveLearnMethod learnMethod)
|
public LearnedMoveImpl(IMoveData moveData, MoveLearnMethod learnMethod)
|
||||||
{
|
{
|
||||||
|
@ -95,8 +95,7 @@ public class LearnedMoveImpl : ILearnedMove
|
||||||
CurrentPp = MaxPp;
|
CurrentPp = MaxPp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LearnedMoveImpl(IMoveData moveData, MoveLearnMethod learnMethod, byte pp)
|
public LearnedMoveImpl(IMoveData moveData, MoveLearnMethod learnMethod, byte pp) : this(moveData, learnMethod)
|
||||||
: this(moveData, learnMethod)
|
|
||||||
{
|
{
|
||||||
CurrentPp = pp;
|
CurrentPp = pp;
|
||||||
}
|
}
|
||||||
|
@ -109,12 +108,12 @@ public class LearnedMoveImpl : ILearnedMove
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MoveLearnMethod LearnMethod { get; }
|
public MoveLearnMethod LearnMethod { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The available power points for this move.
|
/// The available power points for this move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte CurrentPp { get; private set; }
|
public byte CurrentPp { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Try to use the move. This subtracts the amount of PP from the current PP. If the amount requested is
|
/// Try to use the move. This subtracts the amount of PP from the current PP. If the amount requested is
|
||||||
/// higher than the current PP, this will return false, and the PP will not be reduced.
|
/// higher than the current PP, this will return false, and the PP will not be reduced.
|
||||||
|
@ -123,7 +122,7 @@ public class LearnedMoveImpl : ILearnedMove
|
||||||
{
|
{
|
||||||
if (CurrentPp < amount)
|
if (CurrentPp < amount)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
CurrentPp -= amount;
|
CurrentPp -= amount;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// The amount of experience of the Pokemon.
|
/// The amount of experience of the Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint Experience { get; }
|
uint Experience { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Increases the experience of the Pokemon. Returns whether any experience was gained.
|
/// Increases the experience of the Pokemon. Returns whether any experience was gained.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -71,7 +71,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// currently not used, and can be used for other implementations.
|
/// currently not used, and can be used for other implementations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte Coloring { get; }
|
byte Coloring { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the Pokemon is shiny.
|
/// Whether the Pokemon is shiny.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -98,7 +98,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// <param name="weightInKg">The new weight in kilograms</param>
|
/// <param name="weightInKg">The new weight in kilograms</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool ChangeWeightInKgBy(float weightInKg);
|
public bool ChangeWeightInKgBy(float weightInKg);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The height of the Pokémon in meters.
|
/// The height of the Pokémon in meters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -124,7 +124,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// The stats of the Pokemon including the stat boosts
|
/// The stats of the Pokemon including the stat boosts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
StatisticSet<uint> BoostedStats { get; }
|
StatisticSet<uint> BoostedStats { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum health of the Pokemon.
|
/// The maximum health of the Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -171,12 +171,12 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// are null.
|
/// are null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<ILearnedMove?> Moves { get; }
|
IReadOnlyList<ILearnedMove?> Moves { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether the Pokemon has a specific move in its current moveset.
|
/// Checks whether the Pokemon has a specific move in its current moveset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool HasMove(StringKey moveName);
|
bool HasMove(StringKey moveName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Swaps two moves of the Pokemon.
|
/// Swaps two moves of the Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -201,7 +201,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// Whether or not this Pokemon was caught this battle.
|
/// Whether or not this Pokemon was caught this battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsCaught { get; }
|
bool IsCaught { get; }
|
||||||
|
|
||||||
public void MarkAsCaught();
|
public void MarkAsCaught();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -240,7 +240,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MustUseReturnValue]
|
[MustUseReturnValue]
|
||||||
IItem? RemoveHeldItem();
|
IItem? RemoveHeldItem();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the held item from the Pokemon for the duration of the battle. Returns the previously held item.
|
/// Removes the held item from the Pokemon for the duration of the battle. Returns the previously held item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -249,7 +249,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// restored after the battle.
|
/// restored after the battle.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
IItem? RemoveHeldItemForBattle();
|
IItem? RemoveHeldItemForBattle();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restores the held item of a Pokémon if it was temporarily removed.
|
/// Restores the held item of a Pokémon if it was temporarily removed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -273,7 +273,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// Suppresses the ability of the Pokémon.
|
/// Suppresses the ability of the Pokémon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SuppressAbility();
|
public void SuppressAbility();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the currently active ability.
|
/// Returns the currently active ability.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -322,7 +322,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// heal if the Pokemon has 0 health. If the amount healed is 0, this will return false.
|
/// heal if the Pokemon has 0 health. If the amount healed is 0, this will return false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool Heal(uint heal, bool allowRevive = false);
|
bool Heal(uint heal, bool allowRevive = false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restores all PP of the Pokemon.
|
/// Restores all PP of the Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -337,10 +337,12 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// Checks whether the Pokémon has a specific non-volatile status.
|
/// Checks whether the Pokémon has a specific non-volatile status.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool HasStatus(StringKey status);
|
bool HasStatus(StringKey status);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a non-volatile status to the Pokemon.
|
/// Adds a non-volatile status to the Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void SetStatus(StringKey status);
|
void SetStatus(StringKey status);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the current non-volatile status from the Pokemon.
|
/// Removes the current non-volatile status from the Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -371,23 +373,23 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
||||||
/// Marks a Pokemon as seen in the battle.
|
/// Marks a Pokemon as seen in the battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void MarkOpponentAsSeen(IPokemon pokemon);
|
void MarkOpponentAsSeen(IPokemon pokemon);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a type from the Pokémon. Returns whether the type was removed.
|
/// Removes a type from the Pokémon. Returns whether the type was removed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool RemoveType(TypeIdentifier type);
|
bool RemoveType(TypeIdentifier type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a type to the Pokémon. Returns whether the type was added. It will not add the type if
|
/// Adds a type to the Pokémon. Returns whether the type was added. It will not add the type if
|
||||||
/// the Pokémon already has it.
|
/// the Pokémon already has it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool AddType(TypeIdentifier type);
|
bool AddType(TypeIdentifier type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Replace the types of the Pokémon with the provided types.
|
/// Replace the types of the Pokémon with the provided types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void SetTypes(IReadOnlyList<TypeIdentifier> types);
|
void SetTypes(IReadOnlyList<TypeIdentifier> types);
|
||||||
|
|
||||||
void ChangeAbility(IAbility ability);
|
void ChangeAbility(IAbility ability);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -431,17 +433,17 @@ public interface IPokemonBattleData : IDeepCloneable
|
||||||
/// Adds an opponent to the list of seen opponents.
|
/// Adds an opponent to the list of seen opponents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void MarkOpponentAsSeen(IPokemon opponent);
|
void MarkOpponentAsSeen(IPokemon opponent);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of items the Pokémon has consumed this battle.
|
/// A list of items the Pokémon has consumed this battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<IItem> ConsumedItems { get; }
|
IReadOnlyList<IItem> ConsumedItems { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marks an item as consumed.
|
/// Marks an item as consumed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void MarkItemAsConsumed(IItem itemName);
|
void MarkItemAsConsumed(IItem itemName);
|
||||||
|
|
||||||
uint SwitchInTurn { get; internal set; }
|
uint SwitchInTurn { get; internal set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,7 +571,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
{
|
{
|
||||||
BatchId = batchId,
|
BatchId = batchId,
|
||||||
});
|
});
|
||||||
|
|
||||||
var newLevel = Library.StaticLibrary.GrowthRates.CalculateLevel(Species.GrowthRate, Experience);
|
var newLevel = Library.StaticLibrary.GrowthRates.CalculateLevel(Species.GrowthRate, Experience);
|
||||||
if (newLevel > Level)
|
if (newLevel > Level)
|
||||||
{
|
{
|
||||||
|
@ -579,7 +581,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
{
|
{
|
||||||
BatchId = batchId,
|
BatchId = batchId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (newLevel >= maxLevel)
|
if (newLevel >= maxLevel)
|
||||||
{
|
{
|
||||||
Experience = Library.StaticLibrary.GrowthRates.CalculateExperience(Species.GrowthRate, maxLevel);
|
Experience = Library.StaticLibrary.GrowthRates.CalculateExperience(Species.GrowthRate, maxLevel);
|
||||||
|
@ -597,7 +599,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public byte Coloring { get; }
|
public byte Coloring { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsShiny => Coloring == 1;
|
public bool IsShiny => Coloring == 1;
|
||||||
|
|
||||||
|
@ -686,7 +688,11 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
private List<TypeIdentifier> _types = new();
|
private List<TypeIdentifier> _types = new();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<TypeIdentifier> Types { get => _types; private set => _types = value.ToList(); }
|
public IReadOnlyList<TypeIdentifier> Types
|
||||||
|
{
|
||||||
|
get => _types;
|
||||||
|
private set => _types = value.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsEgg { get; private set; }
|
public bool IsEgg { get; private set; }
|
||||||
|
@ -730,7 +736,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
HeldItem = null;
|
HeldItem = null;
|
||||||
return previous;
|
return previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IItem? _stolenHeldItem;
|
private IItem? _stolenHeldItem;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -753,7 +759,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
return false;
|
return false;
|
||||||
if (!Library.ScriptResolver.TryResolveBattleItemScript(HeldItem, out _))
|
if (!Library.ScriptResolver.TryResolveBattleItemScript(HeldItem, out _))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (BattleData != null)
|
if (BattleData != null)
|
||||||
{
|
{
|
||||||
var prevented = false;
|
var prevented = false;
|
||||||
|
@ -761,7 +767,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
if (prevented)
|
if (prevented)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: actually consume the item
|
// TODO: actually consume the item
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -798,12 +804,12 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
RecalculateBoostedStats();
|
RecalculateBoostedStats();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the ability of the Pokémon is suppressed.
|
/// Whether the ability of the Pokémon is suppressed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AbilitySuppressed { get; private set; }
|
public bool AbilitySuppressed { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SuppressAbility()
|
public void SuppressAbility()
|
||||||
{
|
{
|
||||||
|
@ -933,7 +939,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
this.RunScriptHook(script => script.ChangeIncomingDamage(this, source, ref dmg));
|
this.RunScriptHook(script => script.ChangeIncomingDamage(this, source, ref dmg));
|
||||||
damage = dmg;
|
damage = dmg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the damage is more than the current health, we cap it at the current health, to prevent
|
// If the damage is more than the current health, we cap it at the current health, to prevent
|
||||||
// underflow.
|
// underflow.
|
||||||
if (damage >= CurrentHealth)
|
if (damage >= CurrentHealth)
|
||||||
|
@ -989,7 +995,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
{
|
{
|
||||||
if (IsFainted && !allowRevive)
|
if (IsFainted && !allowRevive)
|
||||||
return false;
|
return false;
|
||||||
var maxAmount = this.BoostedStats.Hp - CurrentHealth;
|
var maxAmount = BoostedStats.Hp - CurrentHealth;
|
||||||
if (heal > maxAmount)
|
if (heal > maxAmount)
|
||||||
heal = maxAmount;
|
heal = maxAmount;
|
||||||
if (heal == 0)
|
if (heal == 0)
|
||||||
|
@ -1019,7 +1025,8 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||||
{
|
{
|
||||||
for (byte i = 0; i < Moves.Count; i++)
|
for (byte i = 0; i < Moves.Count; i++)
|
||||||
{
|
{
|
||||||
if (Moves[i] is not null) continue;
|
if (Moves[i] is not null)
|
||||||
|
continue;
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ public interface IPokemonParty : IReadOnlyList<IPokemon?>, IDeepCloneable
|
||||||
{
|
{
|
||||||
event EventHandler<(IPokemon?, int index)>? OnSwapInto;
|
event EventHandler<(IPokemon?, int index)>? OnSwapInto;
|
||||||
event EventHandler<(int index1, int index2)>? OnSwap;
|
event EventHandler<(int index1, int index2)>? OnSwap;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon.
|
/// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -28,7 +28,7 @@ public interface IPokemonParty : IReadOnlyList<IPokemon?>, IDeepCloneable
|
||||||
/// This will return false if all Pokemon are fainted, or eggs, etc.
|
/// This will return false if all Pokemon are fainted, or eggs, etc.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
bool HasUsablePokemon();
|
bool HasUsablePokemon();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Packs the party so that all Pokémon are at the front, and the empty slots are at the back.
|
/// Packs the party so that all Pokémon are at the front, and the empty slots are at the back.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -46,7 +46,6 @@ public class PokemonParty : IPokemonParty
|
||||||
_pokemon = new IPokemon[size];
|
_pokemon = new IPokemon[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<(IPokemon?, int index)>? OnSwapInto;
|
public event EventHandler<(IPokemon?, int index)>? OnSwapInto;
|
||||||
|
|
||||||
|
@ -73,10 +72,9 @@ public class PokemonParty : IPokemonParty
|
||||||
OnSwap?.Invoke(this, (index1, index2));
|
OnSwap?.Invoke(this, (index1, index2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool HasUsablePokemon() => _pokemon.Any(p => p is { IsUsable: true });
|
public bool HasUsablePokemon() => _pokemon.Any(p => p is { IsUsable: true });
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerator<IPokemon?> GetEnumerator() => ((IEnumerable<IPokemon?>)_pokemon).GetEnumerator();
|
public IEnumerator<IPokemon?> GetEnumerator() => ((IEnumerable<IPokemon?>)_pokemon).GetEnumerator();
|
||||||
|
|
||||||
|
@ -95,11 +93,11 @@ public class PokemonParty : IPokemonParty
|
||||||
// Pack the party so that all Pokémon are at the front.
|
// Pack the party so that all Pokémon are at the front.
|
||||||
for (var i = 0; i < _pokemon.Length; i++)
|
for (var i = 0; i < _pokemon.Length; i++)
|
||||||
{
|
{
|
||||||
if (_pokemon[i] != null)
|
if (_pokemon[i] != null)
|
||||||
continue;
|
continue;
|
||||||
for (var j = i + 1; j < _pokemon.Length; j++)
|
for (var j = i + 1; j < _pokemon.Length; j++)
|
||||||
{
|
{
|
||||||
if (_pokemon[j] == null)
|
if (_pokemon[j] == null)
|
||||||
continue;
|
continue;
|
||||||
Swap(i, j);
|
Swap(i, j);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -134,7 +134,7 @@ public record SerializedStats
|
||||||
public SerializedStats()
|
public SerializedStats()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public SerializedStats(ImmutableStatisticSet<byte> stats)
|
public SerializedStats(ImmutableStatisticSet<byte> stats)
|
||||||
{
|
{
|
||||||
Hp = stats.Hp;
|
Hp = stats.Hp;
|
||||||
|
@ -144,7 +144,7 @@ public record SerializedStats
|
||||||
SpecialDefense = stats.SpecialDefense;
|
SpecialDefense = stats.SpecialDefense;
|
||||||
Speed = stats.Speed;
|
Speed = stats.Speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SerializedStats(long hp, long attack, long defense, long specialAttack, long specialDefense, long speed)
|
public SerializedStats(long hp, long attack, long defense, long specialAttack, long specialDefense, long speed)
|
||||||
{
|
{
|
||||||
Hp = hp;
|
Hp = hp;
|
||||||
|
|
|
@ -12,8 +12,7 @@ public abstract class ItemScript : IDeepCloneable
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IItem Item { get; private set; }
|
protected IItem Item { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the script with the given parameters for a specific item
|
/// Initializes the script with the given parameters for a specific item
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -11,14 +11,14 @@ public abstract class PokeballScript : ItemScript
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract byte GetCatchRate(IPokemon target);
|
public abstract byte GetCatchRate(IPokemon target);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnUseWithTarget(IPokemon target)
|
public override void OnUseWithTarget(IPokemon target)
|
||||||
{
|
{
|
||||||
var battleData = target.BattleData;
|
var battleData = target.BattleData;
|
||||||
if (battleData == null)
|
if (battleData == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
battleData.Battle.AttempCapture(battleData.SideIndex, battleData.Position, Item);
|
battleData.Battle.AttempCapture(battleData.SideIndex, battleData.Position, Item);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,8 +3,7 @@ using PkmnLib.Static.Utils;
|
||||||
|
|
||||||
namespace PkmnLib.Dynamic.ScriptHandling.Registry;
|
namespace PkmnLib.Dynamic.ScriptHandling.Registry;
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class), MeansImplicitUse]
|
||||||
[MeansImplicitUse]
|
|
||||||
public class ItemScriptAttribute : Attribute
|
public class ItemScriptAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -11,11 +11,11 @@ public abstract class Plugin
|
||||||
protected Plugin()
|
protected Plugin()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Plugin(PluginConfiguration configuration)
|
protected Plugin(PluginConfiguration configuration)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the plugin. Mostly used for debugging purposes.
|
/// The name of the plugin. Mostly used for debugging purposes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -6,8 +6,7 @@ namespace PkmnLib.Dynamic.ScriptHandling.Registry;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper attribute to register scripts through reflection.
|
/// Helper attribute to register scripts through reflection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
[AttributeUsage(AttributeTargets.Class, Inherited = false), MeansImplicitUse]
|
||||||
[MeansImplicitUse]
|
|
||||||
public class ScriptAttribute : Attribute
|
public class ScriptAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class ScriptRegistry
|
||||||
// This is more performant than using Activator.CreateInstance.
|
// This is more performant than using Activator.CreateInstance.
|
||||||
_scriptTypes[(category, name)] = Expression.Lambda<Func<Script>>(Expression.New(constructor)).Compile();
|
_scriptTypes[(category, name)] = Expression.Lambda<Func<Script>>(Expression.New(constructor)).Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register an item script type with the given name.
|
/// Register an item script type with the given name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -83,32 +83,32 @@ public class ScriptRegistry
|
||||||
// This is more performant than using Activator.CreateInstance.
|
// This is more performant than using Activator.CreateInstance.
|
||||||
var parameterExpression = Expression.Parameter(typeof(IItem), "item");
|
var parameterExpression = Expression.Parameter(typeof(IItem), "item");
|
||||||
var newExpression = Expression.New(constructor, parameterExpression);
|
var newExpression = Expression.New(constructor, parameterExpression);
|
||||||
_itemScriptTypes[name] = Expression.Lambda<Func<IItem, ItemScript>>(newExpression, parameterExpression).Compile();
|
_itemScriptTypes[name] =
|
||||||
|
Expression.Lambda<Func<IItem, ItemScript>>(newExpression, parameterExpression).Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a battle stat calculator.
|
/// Register a battle stat calculator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RegisterBattleStatCalculator<T>(T battleStatCalculator)
|
public void RegisterBattleStatCalculator<T>(T battleStatCalculator) where T : IBattleStatCalculator =>
|
||||||
where T : IBattleStatCalculator => _battleStatCalculator = battleStatCalculator;
|
_battleStatCalculator = battleStatCalculator;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a damage calculator.
|
/// Register a damage calculator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RegisterDamageCalculator<T>(T damageCalculator)
|
public void RegisterDamageCalculator<T>(T damageCalculator) where T : IDamageCalculator =>
|
||||||
where T : IDamageCalculator => _damageCalculator = damageCalculator;
|
_damageCalculator = damageCalculator;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a misc library.
|
/// Register a misc library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RegisterMiscLibrary<T>(T miscLibrary) where T : IMiscLibrary
|
public void RegisterMiscLibrary<T>(T miscLibrary) where T : IMiscLibrary => _miscLibrary = miscLibrary;
|
||||||
=> _miscLibrary = miscLibrary;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a capture library.
|
/// Register a capture library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RegisterCaptureLibrary<T>(T captureLibrary) where T : ICaptureLibrary
|
public void RegisterCaptureLibrary<T>(T captureLibrary) where T : ICaptureLibrary =>
|
||||||
=> _captureLibrary = captureLibrary;
|
_captureLibrary = captureLibrary;
|
||||||
|
|
||||||
internal IReadOnlyDictionary<(ScriptCategory category, StringKey name), Func<Script>> ScriptTypes => _scriptTypes;
|
internal IReadOnlyDictionary<(ScriptCategory category, StringKey name), Func<Script>> ScriptTypes => _scriptTypes;
|
||||||
internal IReadOnlyDictionary<StringKey, Func<IItem, ItemScript>> ItemScriptTypes => _itemScriptTypes;
|
internal IReadOnlyDictionary<StringKey, Func<IItem, ItemScript>> ItemScriptTypes => _itemScriptTypes;
|
||||||
|
|
|
@ -9,14 +9,14 @@ namespace PkmnLib.Dynamic.ScriptHandling.Registry;
|
||||||
public static class ScriptUtils
|
public static class ScriptUtils
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<Type, StringKey> NameCache = new();
|
private static readonly Dictionary<Type, StringKey> NameCache = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolve name from the <see cref="ScriptAttribute"/> of the given script.
|
/// Resolve name from the <see cref="ScriptAttribute"/> of the given script.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static StringKey ResolveName(this Script script) => ResolveName(script.GetType());
|
public static StringKey ResolveName(this Script script) => ResolveName(script.GetType());
|
||||||
|
|
||||||
public static StringKey ResolveName<T>() where T : Script => ResolveName(typeof(T));
|
public static StringKey ResolveName<T>() where T : Script => ResolveName(typeof(T));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolve name from the <see cref="ScriptAttribute"/> of the given type.
|
/// Resolve name from the <see cref="ScriptAttribute"/> of the given type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace PkmnLib.Dynamic.ScriptHandling;
|
||||||
public abstract class Script : IDeepCloneable
|
public abstract class Script : IDeepCloneable
|
||||||
{
|
{
|
||||||
internal event Action<Script>? OnRemoveEvent;
|
internal event Action<Script>? OnRemoveEvent;
|
||||||
|
|
||||||
private int _suppressCount;
|
private int _suppressCount;
|
||||||
|
|
||||||
public void RemoveSelf()
|
public void RemoveSelf()
|
||||||
|
@ -76,14 +76,14 @@ public abstract class Script : IDeepCloneable
|
||||||
public virtual void OnInitialize(IReadOnlyDictionary<StringKey, object?>? parameters)
|
public virtual void OnInitialize(IReadOnlyDictionary<StringKey, object?>? parameters)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Override to customize whether the move can be selected at all.
|
/// Override to customize whether the move can be selected at all.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void PreventMoveSelection(IMoveChoice choice, ref bool prevent)
|
public virtual void PreventMoveSelection(IMoveChoice choice, ref bool prevent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
|
public virtual void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ public abstract class Script : IDeepCloneable
|
||||||
public virtual void ChangeMove(IMoveChoice choice, ref StringKey moveName)
|
public virtual void ChangeMove(IMoveChoice choice, ref StringKey moveName)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the targets of a move choice. This allows for changing the targets of a move before the move starts.
|
/// Changes the targets of a move choice. This allows for changing the targets of a move before the move starts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -267,7 +267,7 @@ public abstract class Script : IDeepCloneable
|
||||||
public virtual void BypassDefensiveStatBoosts(IExecutingMove move, IPokemon target, byte hit, ref bool bypass)
|
public virtual void BypassDefensiveStatBoosts(IExecutingMove move, IPokemon target, byte hit, ref bool bypass)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to bypass evasion stat boosts for a move hit.
|
/// This function allows a script to bypass evasion stat boosts for a move hit.
|
||||||
/// If this is true, the move will handle the evasion stat boosts as if the target has no positive stat boosts.
|
/// If this is true, the move will handle the evasion stat boosts as if the target has no positive stat boosts.
|
||||||
|
@ -527,11 +527,11 @@ public abstract class Script : IDeepCloneable
|
||||||
public virtual void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
|
public virtual void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void BlockOutgoingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
|
public virtual void BlockOutgoingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom triggers for scripts. This allows scripts to run custom events that are not part of the
|
/// Custom triggers for scripts. This allows scripts to run custom events that are not part of the
|
||||||
/// standard battle flow.
|
/// standard battle flow.
|
||||||
|
@ -555,7 +555,8 @@ public abstract class Script : IDeepCloneable
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref int modifiedAccuracy)
|
public virtual void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex,
|
||||||
|
ref int modifiedAccuracy)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ public enum ScriptCategory
|
||||||
/// <see cref="IMoveChoice"/> and <see cref="IExecutingMove"/>
|
/// <see cref="IMoveChoice"/> and <see cref="IExecutingMove"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Move = 0,
|
Move = 0,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A volatile script effect that is attached to a move choice.
|
/// A volatile script effect that is attached to a move choice.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -54,7 +54,7 @@ public enum ScriptCategory
|
||||||
/// A special script for weather, for use on battles.
|
/// A special script for weather, for use on battles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Weather = 7,
|
Weather = 7,
|
||||||
|
|
||||||
Terrain = 8,
|
Terrain = 8,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -38,11 +38,7 @@ public class ScriptContainer : IEnumerable<ScriptContainer>, IDeepCloneable
|
||||||
yield return this;
|
yield return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Assigns a new script to this container. If there was a script already, it is removed.
|
/// Assigns a new script to this container. If there was a script already, it is removed.
|
||||||
|
@ -65,7 +61,7 @@ public class ScriptContainer : IEnumerable<ScriptContainer>, IDeepCloneable
|
||||||
}
|
}
|
||||||
Script = null;
|
Script = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearWithoutRemoving()
|
public void ClearWithoutRemoving()
|
||||||
{
|
{
|
||||||
Script = null;
|
Script = null;
|
||||||
|
|
|
@ -66,5 +66,4 @@ public static class ScriptExecution
|
||||||
itemScript.OnUse();
|
itemScript.OnUse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -44,16 +44,10 @@ public class ScriptIterator : IEnumerable<ScriptContainer>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IEnumerator<ScriptContainer> GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetAsEnumerable().GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
public IEnumerator<ScriptContainer> GetEnumerator() => GetAsEnumerable().GetEnumerator();
|
||||||
{
|
|
||||||
return GetEnumerator();
|
/// <inheritdoc />
|
||||||
}
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
}
|
}
|
|
@ -38,7 +38,7 @@ public class ScriptResolver
|
||||||
{
|
{
|
||||||
script.OnInitialize(parameters);
|
script.OnInitialize(parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ public class ScriptResolver
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var effect = item.BattleEffect;
|
var effect = item.BattleEffect;
|
||||||
if (effect == null)
|
if (effect == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,7 +27,7 @@ public interface IScriptSet : IEnumerable<ScriptContainer>
|
||||||
/// Gets a script from the set using its unique name.
|
/// Gets a script from the set using its unique name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ScriptContainer? Get(StringKey scriptKey);
|
ScriptContainer? Get(StringKey scriptKey);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a script from the set using its type.
|
/// Gets a script from the set using its type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -155,8 +155,5 @@ public class ScriptSet : IScriptSet
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<StringKey> GetScriptNames() =>
|
public IEnumerable<StringKey> GetScriptNames() =>
|
||||||
_scripts
|
_scripts.Select(x => x.Script).WhereNotNull().Select(s => s.Name);
|
||||||
.Select(x => x.Script)
|
|
||||||
.WhereNotNull()
|
|
||||||
.Select(s => s.Name);
|
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ public interface IScriptSource
|
||||||
/// Gets an iterator over all scripts that are relevant for this source.
|
/// Gets an iterator over all scripts that are relevant for this source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ScriptIterator GetScripts();
|
ScriptIterator GetScripts();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of scripts that are expected to be relevant for this source. This generally is
|
/// The number of scripts that are expected to be relevant for this source. This generally is
|
||||||
/// The number of its own scripts + the number of scripts for any parents.
|
/// The number of its own scripts + the number of scripts for any parents.
|
||||||
|
|
|
@ -10,7 +10,7 @@ public static class StaticHelpers
|
||||||
/// may not be the same as the system time.
|
/// may not be the same as the system time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Func<DateTimeOffset> DateTimeProvider { get; set; } = () => DateTimeOffset.Now;
|
public static Func<DateTimeOffset> DateTimeProvider { get; set; } = () => DateTimeOffset.Now;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the current date and time.
|
/// Get the current date and time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -64,13 +64,14 @@ public class LookupGrowthRate : IGrowthRate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (LevelInt)(_experienceTable.Length);
|
return (LevelInt)_experienceTable.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public uint CalculateExperience(LevelInt level)
|
public uint CalculateExperience(LevelInt level)
|
||||||
{
|
{
|
||||||
if (level < 1) level = 1;
|
if (level < 1)
|
||||||
|
level = 1;
|
||||||
return level >= _experienceTable.Length ? _experienceTable[^1] : _experienceTable[level - 1];
|
return level >= _experienceTable.Length ? _experienceTable[^1] : _experienceTable[level - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -105,10 +105,10 @@ public interface IItem : INamedValue
|
||||||
/// A set of arbitrary flags that can be set on the item.
|
/// A set of arbitrary flags that can be set on the item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ImmutableHashSet<StringKey> Flags { get; }
|
ImmutableHashSet<StringKey> Flags { get; }
|
||||||
|
|
||||||
ISecondaryEffect? Effect { get; }
|
ISecondaryEffect? Effect { get; }
|
||||||
ISecondaryEffect? BattleEffect { get; }
|
ISecondaryEffect? BattleEffect { get; }
|
||||||
|
|
||||||
byte FlingPower { get; }
|
byte FlingPower { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -160,10 +160,6 @@ public class ItemImpl : IItem
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public byte FlingPower { get; }
|
public byte FlingPower { get; }
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool HasFlag(string key)
|
public bool HasFlag(string key) => Flags.Contains(key);
|
||||||
{
|
|
||||||
return Flags.Contains(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -7,13 +7,13 @@ namespace PkmnLib.Static.Libraries;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A basic library for data types. Stores data both by name and by index.
|
/// A basic library for data types. Stores data both by name and by index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DataLibrary<T> : IEnumerable<T>
|
public abstract class DataLibrary<T> : IEnumerable<T> where T : INamedValue
|
||||||
where T : INamedValue
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The underlying data storage.
|
/// The underlying data storage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Dictionary<StringKey, T> Data = new();
|
protected readonly Dictionary<StringKey, T> Data = new();
|
||||||
|
|
||||||
private readonly List<T> _values = [];
|
private readonly List<T> _values = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -12,24 +12,27 @@ public interface IReadOnlyGrowthRateLibrary : IEnumerable<IGrowthRate>
|
||||||
/// Tries to get a growth rate from the library. Returns false if the growth rate is not found.
|
/// Tries to get a growth rate from the library. Returns false if the growth rate is not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IGrowthRate value);
|
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IGrowthRate value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a random growth rate from the library.
|
/// Gets a random growth rate from the library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IGrowthRate GetRandom(IRandom random);
|
IGrowthRate GetRandom(IRandom random);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the amount of growth rates in the library.
|
/// Gets the amount of growth rates in the library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int Count { get; }
|
int Count { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the library is empty.
|
/// Whether the library is empty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsEmpty { get; }
|
bool IsEmpty { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the experience for a given growth key name and a certain level.
|
/// Calculates the experience for a given growth key name and a certain level.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint CalculateExperience(StringKey key, LevelInt level);
|
uint CalculateExperience(StringKey key, LevelInt level);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the level for a given growth key name and a certain experience.
|
/// Calculates the level for a given growth key name and a certain experience.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace PkmnLib.Static.Libraries;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The library for all items in the game.
|
/// The library for all items in the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IReadOnlyItemLibrary : IEnumerable<IItem>
|
public interface IReadOnlyItemLibrary : IEnumerable<IItem>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to get an item from the library. Returns false if the item is not found.
|
/// Tries to get an item from the library. Returns false if the item is not found.
|
||||||
|
|
|
@ -9,7 +9,7 @@ public record LibrarySettings
|
||||||
/// The maximum level a Pokémon can reach.
|
/// The maximum level a Pokémon can reach.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public required LevelInt MaxLevel { get; init; }
|
public required LevelInt MaxLevel { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The chance of a Pokémon being shiny, as the denominator of a fraction, where the nominator
|
/// The chance of a Pokémon being shiny, as the denominator of a fraction, where the nominator
|
||||||
/// is 1. For example, if this is 1000, then the chance of a Pokémon being shiny is 1/1000.
|
/// is 1. For example, if this is 1000, then the chance of a Pokémon being shiny is 1/1000.
|
||||||
|
|
|
@ -13,14 +13,17 @@ public interface IReadOnlyMoveLibrary : IEnumerable<IMoveData>
|
||||||
/// Tries to get a move from the library. Returns false if the move is not found.
|
/// Tries to get a move from the library. Returns false if the move is not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IMoveData value);
|
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IMoveData value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a random move from the library.
|
/// Gets a random move from the library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IMoveData GetRandom(IRandom random);
|
IMoveData GetRandom(IRandom random);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of moves in the library.
|
/// The amount of moves in the library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int Count { get; }
|
int Count { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the library is empty.
|
/// Whether the library is empty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -13,7 +13,7 @@ public interface IReadOnlySpeciesLibrary : IEnumerable<ISpecies>
|
||||||
/// Tries to get a species from the library. Returns false if the species is not found.
|
/// Tries to get a species from the library. Returns false if the species is not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool TryGet(StringKey key, [MaybeNullWhen(false)] out ISpecies value);
|
bool TryGet(StringKey key, [MaybeNullWhen(false)] out ISpecies value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tried to get a species from the library by its national dex number. Returns false if the species is not found.
|
/// Tried to get a species from the library by its national dex number. Returns false if the species is not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -45,7 +45,7 @@ public interface IReadOnlySpeciesLibrary : IEnumerable<ISpecies>
|
||||||
public class SpeciesLibrary : DataLibrary<ISpecies>, IReadOnlySpeciesLibrary
|
public class SpeciesLibrary : DataLibrary<ISpecies>, IReadOnlySpeciesLibrary
|
||||||
{
|
{
|
||||||
private Dictionary<ISpecies, ISpecies> _preEvolutionCache = new();
|
private Dictionary<ISpecies, ISpecies> _preEvolutionCache = new();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool TryGetById(int id, [MaybeNullWhen(false)] out ISpecies value)
|
public bool TryGetById(int id, [MaybeNullWhen(false)] out ISpecies value)
|
||||||
{
|
{
|
||||||
|
@ -61,7 +61,7 @@ public class SpeciesLibrary : DataLibrary<ISpecies>, IReadOnlySpeciesLibrary
|
||||||
return preEvolution;
|
return preEvolution;
|
||||||
foreach (var s in this)
|
foreach (var s in this)
|
||||||
{
|
{
|
||||||
if (s.EvolutionData.All(e => e.ToSpecies != species.Name))
|
if (s.EvolutionData.All(e => e.ToSpecies != species.Name))
|
||||||
continue;
|
continue;
|
||||||
_preEvolutionCache[species] = s;
|
_preEvolutionCache[species] = s;
|
||||||
return s;
|
return s;
|
||||||
|
|
|
@ -9,32 +9,37 @@ public interface IStaticLibrary
|
||||||
/// The miscellaneous settings for the library.
|
/// The miscellaneous settings for the library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
LibrarySettings Settings { get; }
|
LibrarySettings Settings { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All data for Pokémon species.
|
/// All data for Pokémon species.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlySpeciesLibrary Species { get; }
|
IReadOnlySpeciesLibrary Species { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All data for Pokémon moves.
|
/// All data for Pokémon moves.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyMoveLibrary Moves { get; }
|
IReadOnlyMoveLibrary Moves { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All data for Pokémon abilities.
|
/// All data for Pokémon abilities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyAbilityLibrary Abilities { get; }
|
IReadOnlyAbilityLibrary Abilities { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All data for Pokémon types and their effectiveness.
|
/// All data for Pokémon types and their effectiveness.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyTypeLibrary Types { get; }
|
IReadOnlyTypeLibrary Types { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All data for Pokémon natures.
|
/// All data for Pokémon natures.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyNatureLibrary Natures { get; }
|
IReadOnlyNatureLibrary Natures { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All data for Pokémon growth rates.
|
/// All data for Pokémon growth rates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyGrowthRateLibrary GrowthRates { get; }
|
IReadOnlyGrowthRateLibrary GrowthRates { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All data for Pokémon items.
|
/// All data for Pokémon items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -45,7 +50,9 @@ public interface IStaticLibrary
|
||||||
public class StaticLibraryImpl : IStaticLibrary
|
public class StaticLibraryImpl : IStaticLibrary
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="StaticLibraryImpl" />
|
/// <inheritdoc cref="StaticLibraryImpl" />
|
||||||
public StaticLibraryImpl(LibrarySettings settings, IReadOnlySpeciesLibrary species, IReadOnlyMoveLibrary moves, IReadOnlyAbilityLibrary abilities, IReadOnlyTypeLibrary types, IReadOnlyNatureLibrary natures, IReadOnlyGrowthRateLibrary growthRates, IReadOnlyItemLibrary items)
|
public StaticLibraryImpl(LibrarySettings settings, IReadOnlySpeciesLibrary species, IReadOnlyMoveLibrary moves,
|
||||||
|
IReadOnlyAbilityLibrary abilities, IReadOnlyTypeLibrary types, IReadOnlyNatureLibrary natures,
|
||||||
|
IReadOnlyGrowthRateLibrary growthRates, IReadOnlyItemLibrary items)
|
||||||
{
|
{
|
||||||
Settings = settings;
|
Settings = settings;
|
||||||
Species = species;
|
Species = species;
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class TypeLibrary : IReadOnlyTypeLibrary
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TypeIdentifier RegisterType(StringKey name)
|
public TypeIdentifier RegisterType(StringKey name)
|
||||||
{
|
{
|
||||||
var id = new TypeIdentifier((byte)( _types.Count + 1));
|
var id = new TypeIdentifier((byte)(_types.Count + 1));
|
||||||
_types.Add(name, id);
|
_types.Add(name, id);
|
||||||
_effectiveness.Add(Enumerable.Repeat(1.0f, _effectiveness.Count).ToList());
|
_effectiveness.Add(Enumerable.Repeat(1.0f, _effectiveness.Count).ToList());
|
||||||
foreach (var list in _effectiveness)
|
foreach (var list in _effectiveness)
|
||||||
|
|
|
@ -11,12 +11,12 @@ public interface ISecondaryEffect
|
||||||
/// The chance in percentages that the effect triggers. When less than 0, the effect is always active.
|
/// The chance in percentages that the effect triggers. When less than 0, the effect is always active.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float Chance { get; }
|
public float Chance { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the effect.
|
/// The name of the effect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StringKey Name { get; }
|
public StringKey Name { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parameters for the effect.
|
/// Parameters for the effect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -50,8 +50,7 @@ public class Nature(
|
||||||
Statistic increaseStat,
|
Statistic increaseStat,
|
||||||
Statistic decreaseStat,
|
Statistic decreaseStat,
|
||||||
float increaseModifier,
|
float increaseModifier,
|
||||||
float decreaseModifier)
|
float decreaseModifier) : INature
|
||||||
: INature
|
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public StringKey Name { get; } = name;
|
public StringKey Name { get; } = name;
|
||||||
|
@ -79,8 +78,6 @@ public class Nature(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool Equals(INature? other)
|
public bool Equals(INature? other) =>
|
||||||
{
|
other is not null && StringComparer.InvariantCultureIgnoreCase.Equals(Name, other.Name);
|
||||||
return other is not null && StringComparer.InvariantCultureIgnoreCase.Equals(Name, other.Name);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -18,7 +18,7 @@ public interface IAbility : INamedValue
|
||||||
/// The parameters for the script effect of the ability.
|
/// The parameters for the script effect of the ability.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyDictionary<StringKey, object?> Parameters { get; }
|
IReadOnlyDictionary<StringKey, object?> Parameters { get; }
|
||||||
|
|
||||||
bool HasFlag(StringKey key);
|
bool HasFlag(StringKey key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,8 @@ public interface IAbility : INamedValue
|
||||||
public class AbilityImpl : IAbility
|
public class AbilityImpl : IAbility
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="AbilityImpl" />
|
/// <inheritdoc cref="AbilityImpl" />
|
||||||
public AbilityImpl(StringKey name, StringKey? effect, IReadOnlyDictionary<StringKey, object?> parameters, ImmutableHashSet<StringKey> flags)
|
public AbilityImpl(StringKey name, StringKey? effect, IReadOnlyDictionary<StringKey, object?> parameters,
|
||||||
|
ImmutableHashSet<StringKey> flags)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Effect = effect;
|
Effect = effect;
|
||||||
|
@ -46,10 +47,7 @@ public class AbilityImpl : IAbility
|
||||||
public ImmutableHashSet<StringKey> Flags;
|
public ImmutableHashSet<StringKey> Flags;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool HasFlag(StringKey key)
|
public bool HasFlag(StringKey key) => Flags.Contains(key);
|
||||||
{
|
|
||||||
return Flags.Contains(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -95,8 +95,8 @@ public interface IForm : INamedValue
|
||||||
public class FormImpl : IForm
|
public class FormImpl : IForm
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="FormImpl" />
|
/// <inheritdoc cref="FormImpl" />
|
||||||
public FormImpl(StringKey name, float height, float weight, uint baseExperience,
|
public FormImpl(StringKey name, float height, float weight, uint baseExperience, IEnumerable<TypeIdentifier> types,
|
||||||
IEnumerable<TypeIdentifier> types, ImmutableStatisticSet<ushort> baseStats, IEnumerable<StringKey> abilities,
|
ImmutableStatisticSet<ushort> baseStats, IEnumerable<StringKey> abilities,
|
||||||
IEnumerable<StringKey> hiddenAbilities, ILearnableMoves moves, ImmutableHashSet<StringKey> flags)
|
IEnumerable<StringKey> hiddenAbilities, ILearnableMoves moves, ImmutableHashSet<StringKey> flags)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
|
@ -109,7 +109,7 @@ public class FormImpl : IForm
|
||||||
HiddenAbilities = [..hiddenAbilities];
|
HiddenAbilities = [..hiddenAbilities];
|
||||||
Moves = moves;
|
Moves = moves;
|
||||||
Flags = flags;
|
Flags = flags;
|
||||||
|
|
||||||
if (Types.Count == 0)
|
if (Types.Count == 0)
|
||||||
throw new ArgumentException("A form must have at least one type.");
|
throw new ArgumentException("A form must have at least one type.");
|
||||||
if (Abilities.Count == 0)
|
if (Abilities.Count == 0)
|
||||||
|
@ -163,20 +163,24 @@ public class FormImpl : IForm
|
||||||
for (var i = 0; i < Abilities.Count && i < 255; i++)
|
for (var i = 0; i < Abilities.Count && i < 255; i++)
|
||||||
{
|
{
|
||||||
if (Abilities[i] == ability.Name)
|
if (Abilities[i] == ability.Name)
|
||||||
|
{
|
||||||
return new AbilityIndex
|
return new AbilityIndex
|
||||||
{
|
{
|
||||||
IsHidden = false,
|
IsHidden = false,
|
||||||
Index = (byte)i,
|
Index = (byte)i,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (var i = 0; i < HiddenAbilities.Count && i < 255; i++)
|
for (var i = 0; i < HiddenAbilities.Count && i < 255; i++)
|
||||||
{
|
{
|
||||||
if (HiddenAbilities[i] == ability.Name)
|
if (HiddenAbilities[i] == ability.Name)
|
||||||
|
{
|
||||||
return new AbilityIndex
|
return new AbilityIndex
|
||||||
{
|
{
|
||||||
IsHidden = true,
|
IsHidden = true,
|
||||||
Index = (byte)i,
|
Index = (byte)i,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -191,20 +195,11 @@ public class FormImpl : IForm
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public StringKey GetRandomAbility(IRandom rand)
|
public StringKey GetRandomAbility(IRandom rand) => Abilities[rand.GetInt(Abilities.Count)];
|
||||||
{
|
|
||||||
return Abilities[rand.GetInt(Abilities.Count)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public StringKey GetRandomHiddenAbility(IRandom rand)
|
public StringKey GetRandomHiddenAbility(IRandom rand) => HiddenAbilities[rand.GetInt(HiddenAbilities.Count)];
|
||||||
{
|
|
||||||
return HiddenAbilities[rand.GetInt(HiddenAbilities.Count)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool HasFlag(string key)
|
public bool HasFlag(string key) => Flags.Contains(key);
|
||||||
{
|
|
||||||
return Flags.Contains(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -10,10 +10,12 @@ public enum Gender : byte
|
||||||
{
|
{
|
||||||
/// The Pokémon has no gender.
|
/// The Pokémon has no gender.
|
||||||
Genderless,
|
Genderless,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokémon is male.
|
/// The Pokémon is male.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Male,
|
Male,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokémon is female.
|
/// The Pokémon is female.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -14,7 +14,7 @@ public interface ILearnableMoves
|
||||||
/// <param name="move">The move the Pokémon learns.</param>
|
/// <param name="move">The move the Pokémon learns.</param>
|
||||||
/// <returns>Whether the move was added successfully.</returns>
|
/// <returns>Whether the move was added successfully.</returns>
|
||||||
void AddLevelMove(LevelInt level, StringKey move);
|
void AddLevelMove(LevelInt level, StringKey move);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a new egg move the Pokémon can learn.
|
/// Adds a new egg move the Pokémon can learn.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -38,7 +38,7 @@ public interface ILearnableMoves
|
||||||
/// Gets a list of all moves a Pokémon can learn up to a specific level.
|
/// Gets a list of all moves a Pokémon can learn up to a specific level.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<StringKey> GetLearnableMovesUpToLevel(LevelInt level);
|
IReadOnlyList<StringKey> GetLearnableMovesUpToLevel(LevelInt level);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all moves a Pokémon can learn by breeding.
|
/// Gets all moves a Pokémon can learn by breeding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -50,9 +50,8 @@ public class LearnableMovesImpl : ILearnableMoves
|
||||||
{
|
{
|
||||||
private readonly Dictionary<LevelInt, List<StringKey>> _learnedByLevel = new();
|
private readonly Dictionary<LevelInt, List<StringKey>> _learnedByLevel = new();
|
||||||
private readonly HashSet<StringKey> _distinctLevelMoves = new();
|
private readonly HashSet<StringKey> _distinctLevelMoves = new();
|
||||||
|
|
||||||
private readonly List<StringKey> _eggMoves = new();
|
|
||||||
|
|
||||||
|
private readonly List<StringKey> _eggMoves = new();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void AddLevelMove(LevelInt level, StringKey move)
|
public void AddLevelMove(LevelInt level, StringKey move)
|
||||||
|
@ -81,10 +80,7 @@ public class LearnableMovesImpl : ILearnableMoves
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<StringKey> GetDistinctLevelMoves()
|
public IReadOnlyList<StringKey> GetDistinctLevelMoves() => _distinctLevelMoves.ToList();
|
||||||
{
|
|
||||||
return _distinctLevelMoves.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<StringKey> GetLearnableMovesUpToLevel(LevelInt level)
|
public IReadOnlyList<StringKey> GetLearnableMovesUpToLevel(LevelInt level)
|
||||||
|
|
|
@ -50,7 +50,7 @@ public interface ISpecies : INamedValue
|
||||||
/// Gets a form by name.
|
/// Gets a form by name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool TryGetForm(StringKey id, [MaybeNullWhen(false)] out IForm form);
|
bool TryGetForm(StringKey id, [MaybeNullWhen(false)] out IForm form);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the form the Pokémon will have by default, if no other form is specified.
|
/// Gets the form the Pokémon will have by default, if no other form is specified.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -70,7 +70,7 @@ public interface ISpecies : INamedValue
|
||||||
/// The data regarding into which Pokémon this species can evolve, and how.
|
/// The data regarding into which Pokémon this species can evolve, and how.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<IEvolution> EvolutionData { get; }
|
IReadOnlyList<IEvolution> EvolutionData { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The egg groups the Pokémon belongs to.
|
/// The egg groups the Pokémon belongs to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -124,14 +124,13 @@ public class SpeciesImpl : ISpecies
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ImmutableHashSet<StringKey> Flags { get; }
|
public ImmutableHashSet<StringKey> Flags { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<IEvolution> EvolutionData { get; }
|
public IReadOnlyList<IEvolution> EvolutionData { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ICollection<StringKey> EggGroups { get; }
|
public ICollection<StringKey> EggGroups { get; }
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool TryGetForm(StringKey id, [MaybeNullWhen(false)] out IForm form) => Forms.TryGetValue(id, out form);
|
public bool TryGetForm(StringKey id, [MaybeNullWhen(false)] out IForm form) => Forms.TryGetValue(id, out form);
|
||||||
|
|
||||||
|
|
|
@ -9,33 +9,38 @@ public enum Statistic : byte
|
||||||
/// Health Points determine how much damage a Pokémon can receive before fainting.
|
/// Health Points determine how much damage a Pokémon can receive before fainting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Hp,
|
Hp,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attack determines how much damage a Pokémon deals when using a physical attack.
|
/// Attack determines how much damage a Pokémon deals when using a physical attack.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Attack,
|
Attack,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defense determines how much damage a Pokémon receives when it is hit by a physical attack.
|
/// Defense determines how much damage a Pokémon receives when it is hit by a physical attack.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Defense,
|
Defense,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Special Attack determines how much damage a Pokémon deals when using a special attack.
|
/// Special Attack determines how much damage a Pokémon deals when using a special attack.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SpecialAttack,
|
SpecialAttack,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Special Defense determines how much damage a Pokémon receives when it is hit by a special attack.
|
/// Special Defense determines how much damage a Pokémon receives when it is hit by a special attack.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SpecialDefense,
|
SpecialDefense,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Speed determines the order that a Pokémon can act in battle.
|
/// Speed determines the order that a Pokémon can act in battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Speed,
|
Speed,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evasion determines the likelihood that a Pokémon will dodge an attack.
|
/// Evasion determines the likelihood that a Pokémon will dodge an attack.
|
||||||
/// This is not part of base stats, but is a temporary stat boost.
|
/// This is not part of base stats, but is a temporary stat boost.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Evasion,
|
Evasion,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Accuracy determines the likelihood that a Pokémon will hit an attack.
|
/// Accuracy determines the likelihood that a Pokémon will hit an attack.
|
||||||
/// This is not part of base stats, but is a temporary stat boost.
|
/// This is not part of base stats, but is a temporary stat boost.
|
||||||
|
|
|
@ -8,8 +8,7 @@ namespace PkmnLib.Static;
|
||||||
/// A set of statistics that cannot be changed.
|
/// A set of statistics that cannot be changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The size of the integer to be used</typeparam>
|
/// <typeparam name="T">The size of the integer to be used</typeparam>
|
||||||
public record ImmutableStatisticSet<T>
|
public record ImmutableStatisticSet<T> where T : struct
|
||||||
where T : struct
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The health points stat value.
|
/// The health points stat value.
|
||||||
|
@ -51,7 +50,7 @@ public record ImmutableStatisticSet<T>
|
||||||
SpecialDefense = specialDefense;
|
SpecialDefense = specialDefense;
|
||||||
Speed = speed;
|
Speed = speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableStatisticSet(ImmutableStatisticSet<T> set)
|
public ImmutableStatisticSet(ImmutableStatisticSet<T> set)
|
||||||
{
|
{
|
||||||
Hp = set.Hp;
|
Hp = set.Hp;
|
||||||
|
@ -84,8 +83,7 @@ public record ImmutableStatisticSet<T>
|
||||||
/// A set of statistics that can be changed.
|
/// A set of statistics that can be changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepCloneable
|
public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepCloneable where T : struct
|
||||||
where T : struct
|
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="StatisticSet{T}"/>
|
/// <inheritdoc cref="StatisticSet{T}"/>
|
||||||
public StatisticSet() : base(default, default, default, default, default, default)
|
public StatisticSet() : base(default, default, default, default, default, default)
|
||||||
|
@ -97,7 +95,7 @@ public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepC
|
||||||
defense, specialAttack, specialDefense, speed)
|
defense, specialAttack, specialDefense, speed)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public StatisticSet(StatisticSet<T> set) : base(set)
|
public StatisticSet(StatisticSet<T> set) : base(set)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -175,16 +173,12 @@ public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepC
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual T GetUnknownStat(Statistic stat)
|
protected virtual T GetUnknownStat(Statistic stat) => throw new ArgumentException($"Invalid statistic {stat}");
|
||||||
{
|
|
||||||
throw new ArgumentException($"Invalid statistic {stat}");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void SetUnknownStat(Statistic stat, T value)
|
protected virtual void SetUnknownStat(Statistic stat, T value)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Invalid statistic {stat}");
|
throw new ArgumentException($"Invalid statistic {stat}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Decreases a statistic in the set by a value.
|
/// Decreases a statistic in the set by a value.
|
||||||
|
@ -231,17 +225,13 @@ public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepC
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A set of statistics that can be changed, but are clamped to a minimum and maximum value.
|
/// A set of statistics that can be changed, but are clamped to a minimum and maximum value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract record ClampedStatisticSet<T> : StatisticSet<T>
|
public abstract record ClampedStatisticSet<T> : StatisticSet<T> where T : struct, IComparable<T>
|
||||||
where T : struct, IComparable<T>
|
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
|
[SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
|
||||||
|
@ -257,7 +247,7 @@ public abstract record ClampedStatisticSet<T> : StatisticSet<T>
|
||||||
SpecialDefense = Clamp(SpecialDefense, Min, Max);
|
SpecialDefense = Clamp(SpecialDefense, Min, Max);
|
||||||
Speed = Clamp(Speed, Min, Max);
|
Speed = Clamp(Speed, Min, Max);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ClampedStatisticSet(ClampedStatisticSet<T> set) : base(set)
|
protected ClampedStatisticSet(ClampedStatisticSet<T> set) : base(set)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -322,13 +312,15 @@ public record StatBoostStatisticSet : ClampedStatisticSet<sbyte>
|
||||||
protected override sbyte Max => 6;
|
protected override sbyte Max => 6;
|
||||||
|
|
||||||
private sbyte _evasion;
|
private sbyte _evasion;
|
||||||
|
|
||||||
public sbyte Evasion
|
public sbyte Evasion
|
||||||
{
|
{
|
||||||
get => _evasion;
|
get => _evasion;
|
||||||
set => _evasion = Clamp(value, Min, Max);
|
set => _evasion = Clamp(value, Min, Max);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sbyte _accuracy;
|
private sbyte _accuracy;
|
||||||
|
|
||||||
public sbyte Accuracy
|
public sbyte Accuracy
|
||||||
{
|
{
|
||||||
get => _accuracy;
|
get => _accuracy;
|
||||||
|
@ -403,13 +395,13 @@ public record IndividualValueStatisticSet : ClampedStatisticSet<byte>
|
||||||
public IndividualValueStatisticSet() : base(0, 0, 0, 0, 0, 0)
|
public IndividualValueStatisticSet() : base(0, 0, 0, 0, 0, 0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IndividualValueStatisticSet"/>
|
/// <inheritdoc cref="IndividualValueStatisticSet"/>
|
||||||
public IndividualValueStatisticSet(byte hp, byte attack, byte defense, byte specialAttack, byte specialDefense,
|
public IndividualValueStatisticSet(byte hp, byte attack, byte defense, byte specialAttack, byte specialDefense,
|
||||||
byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndividualValueStatisticSet(IndividualValueStatisticSet ivs) : base(ivs)
|
public IndividualValueStatisticSet(IndividualValueStatisticSet ivs) : base(ivs)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -436,7 +428,7 @@ public record EffortValueStatisticSet : ClampedStatisticSet<byte>
|
||||||
byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public EffortValueStatisticSet(EffortValueStatisticSet evs) : base(evs)
|
public EffortValueStatisticSet(EffortValueStatisticSet evs) : base(evs)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,9 @@ public static class DeepCloneHandler
|
||||||
/// Recursive references will be handled correctly, and will only be cloned once, to prevent infinite loops and invalid
|
/// Recursive references will be handled correctly, and will only be cloned once, to prevent infinite loops and invalid
|
||||||
/// references.
|
/// references.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static T DeepClone<T>(this T? obj, Dictionary<(Type, int), object>? objects = null) where T : IDeepCloneable
|
public static T DeepClone<T>(this T? obj, Dictionary<(Type, int), object>? objects = null)
|
||||||
{
|
where T : IDeepCloneable => (T)DeepClone((object?)obj, objects)!;
|
||||||
return (T)DeepClone((object?)obj, objects)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object? DeepClone(this object? obj, Dictionary<(Type, int), object>? objects = null)
|
private static object? DeepClone(this object? obj, Dictionary<(Type, int), object>? objects = null)
|
||||||
{
|
{
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
|
@ -41,7 +39,7 @@ public static class DeepCloneHandler
|
||||||
// We use GetUninitializedObject to create an object without calling the constructor. This is necessary to prevent
|
// We use GetUninitializedObject to create an object without calling the constructor. This is necessary to prevent
|
||||||
// side effects from the constructor, and to not require a parameterless constructor.
|
// side effects from the constructor, and to not require a parameterless constructor.
|
||||||
var newObj = FormatterServices.GetUninitializedObject(type)!;
|
var newObj = FormatterServices.GetUninitializedObject(type)!;
|
||||||
|
|
||||||
// If the objects dictionary is null, we create a new one. We use this dictionary to keep track of objects that have
|
// If the objects dictionary is null, we create a new one. We use this dictionary to keep track of objects that have
|
||||||
// already been cloned, so we can re-use them instead of cloning them again. This is necessary to prevent infinite
|
// already been cloned, so we can re-use them instead of cloning them again. This is necessary to prevent infinite
|
||||||
// loops and invalid references.
|
// loops and invalid references.
|
||||||
|
@ -74,7 +72,7 @@ public static class DeepCloneHandler
|
||||||
// If the object is a value type or a string, we can just return it.
|
// If the object is a value type or a string, we can just return it.
|
||||||
if (type.IsValueType || type == typeof(string))
|
if (type.IsValueType || type == typeof(string))
|
||||||
return obj;
|
return obj;
|
||||||
|
|
||||||
// If the object is marked as deep cloneable, we will clone it.
|
// If the object is marked as deep cloneable, we will clone it.
|
||||||
if (type.GetInterface(nameof(IDeepCloneable)) != null || ExternalDeepCloneTypes.Contains(type))
|
if (type.GetInterface(nameof(IDeepCloneable)) != null || ExternalDeepCloneTypes.Contains(type))
|
||||||
{
|
{
|
||||||
|
@ -91,8 +89,9 @@ public static class DeepCloneHandler
|
||||||
var array = (Array)obj;
|
var array = (Array)obj;
|
||||||
var newArray = Array.CreateInstance(type.GetElementType()!, array.Length);
|
var newArray = Array.CreateInstance(type.GetElementType()!, array.Length);
|
||||||
for (var i = 0; i < array.Length; i++)
|
for (var i = 0; i < array.Length; i++)
|
||||||
newArray.SetValue(DeepCloneInternal(array.GetValue(i), type.GetElementType()!, objects),
|
{
|
||||||
i);
|
newArray.SetValue(DeepCloneInternal(array.GetValue(i), type.GetElementType()!, objects), i);
|
||||||
|
}
|
||||||
return newArray;
|
return newArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,9 +114,10 @@ public static class DeepCloneHandler
|
||||||
var dictionary = (IDictionary)obj;
|
var dictionary = (IDictionary)obj;
|
||||||
var newDictionary = (IDictionary)Activator.CreateInstance(type);
|
var newDictionary = (IDictionary)Activator.CreateInstance(type);
|
||||||
foreach (DictionaryEntry entry in dictionary)
|
foreach (DictionaryEntry entry in dictionary)
|
||||||
newDictionary.Add(
|
{
|
||||||
DeepCloneInternal(entry.Key, type.GetGenericArguments()[0], objects)!,
|
newDictionary.Add(DeepCloneInternal(entry.Key, type.GetGenericArguments()[0], objects)!,
|
||||||
DeepCloneInternal(entry.Value, type.GetGenericArguments()[1], objects));
|
DeepCloneInternal(entry.Value, type.GetGenericArguments()[1], objects));
|
||||||
|
}
|
||||||
return newDictionary;
|
return newDictionary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,8 +133,7 @@ public static class DeepCloneHandler
|
||||||
/// This method is thread safe, and will only create the expressions once for each type. It returns compiled expressions for
|
/// This method is thread safe, and will only create the expressions once for each type. It returns compiled expressions for
|
||||||
/// each field in the type, so that we can get high performance deep cloning.
|
/// each field in the type, so that we can get high performance deep cloning.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private static (Func<object, object?> getter, Action<object, object?> setter)[]
|
private static (Func<object, object?> getter, Action<object, object?> setter)[] GetDeepCloneExpressions(Type type)
|
||||||
GetDeepCloneExpressions(Type type)
|
|
||||||
{
|
{
|
||||||
// We use a lock here to prevent multiple threads from trying to create the expressions at the same time.
|
// We use a lock here to prevent multiple threads from trying to create the expressions at the same time.
|
||||||
lock (DeepCloneExpressions)
|
lock (DeepCloneExpressions)
|
||||||
|
|
|
@ -2,7 +2,8 @@ namespace PkmnLib.Static.Utils;
|
||||||
|
|
||||||
public static class DictionaryHelpers
|
public static class DictionaryHelpers
|
||||||
{
|
{
|
||||||
public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
|
public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key,
|
||||||
|
TValue defaultValue)
|
||||||
{
|
{
|
||||||
if (dictionary.TryGetValue(key, out var value))
|
if (dictionary.TryGetValue(key, out var value))
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -10,7 +10,7 @@ public static class EnumerableHelpers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> enumerable) where T : class =>
|
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> enumerable) where T : class =>
|
||||||
enumerable.Where(x => x is not null)!;
|
enumerable.Where(x => x is not null)!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the index of a value in an enumerable. Returns -1 if the value is not found.
|
/// Finds the index of a value in an enumerable. Returns -1 if the value is not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
namespace PkmnLib.Static.Utils.Errors;
|
namespace PkmnLib.Static.Utils.Errors;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -13,7 +13,7 @@ public static class NumericHelpers
|
||||||
var result = value * multiplier;
|
var result = value * multiplier;
|
||||||
return result > byte.MaxValue ? byte.MaxValue : (byte)result;
|
return result > byte.MaxValue ? byte.MaxValue : (byte)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Multiplies two values. If this overflows, returns <see cref="byte.MaxValue"/>.
|
/// Multiplies two values. If this overflows, returns <see cref="byte.MaxValue"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -22,7 +22,7 @@ public static class NumericHelpers
|
||||||
var result = value * multiplier;
|
var result = value * multiplier;
|
||||||
return result > byte.MaxValue ? byte.MaxValue : (byte)result;
|
return result > byte.MaxValue ? byte.MaxValue : (byte)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Multiplies two values. If this overflows, returns <see cref="sbyte.MaxValue"/>.
|
/// Multiplies two values. If this overflows, returns <see cref="sbyte.MaxValue"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -31,7 +31,7 @@ public static class NumericHelpers
|
||||||
var result = value * multiplier;
|
var result = value * multiplier;
|
||||||
return result > sbyte.MaxValue ? sbyte.MaxValue : (sbyte)result;
|
return result > sbyte.MaxValue ? sbyte.MaxValue : (sbyte)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Multiplies two values. If this overflows, returns <see cref="ushort.MaxValue"/>.
|
/// Multiplies two values. If this overflows, returns <see cref="ushort.MaxValue"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -40,7 +40,7 @@ public static class NumericHelpers
|
||||||
var result = value * multiplier;
|
var result = value * multiplier;
|
||||||
return result > ushort.MaxValue ? ushort.MaxValue : (ushort)result;
|
return result > ushort.MaxValue ? ushort.MaxValue : (ushort)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Multiplies two values. If this overflows, returns <see cref="short.MaxValue"/>.
|
/// Multiplies two values. If this overflows, returns <see cref="short.MaxValue"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -49,11 +49,10 @@ public static class NumericHelpers
|
||||||
var result = value * multiplier;
|
var result = value * multiplier;
|
||||||
return result > short.MaxValue ? short.MaxValue : (short)result;
|
return result > short.MaxValue ? short.MaxValue : (short)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static uint MultiplyOrMax(this uint value, uint multiplier)
|
public static uint MultiplyOrMax(this uint value, uint multiplier)
|
||||||
{
|
{
|
||||||
var result = (ulong)value * multiplier;
|
var result = (ulong)value * multiplier;
|
||||||
return result > uint.MaxValue ? uint.MaxValue : (uint)result;
|
return result > uint.MaxValue ? uint.MaxValue : (uint)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -21,19 +21,19 @@ public interface IRandom
|
||||||
/// <param name="max">The maximum value (exclusive).</param>
|
/// <param name="max">The maximum value (exclusive).</param>
|
||||||
/// <returns>A random integer that is greater than or equal to 0 and less than max.</returns>
|
/// <returns>A random integer that is greater than or equal to 0 and less than max.</returns>
|
||||||
public int GetInt(int max);
|
public int GetInt(int max);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a random integer between 0 and <see cref="int.MaxValue"/>.
|
/// Get a random integer between 0 and <see cref="int.MaxValue"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A random integer that is greater than or equal to 0 and less than <see cref="int.MaxValue"/>.</returns>
|
/// <returns>A random integer that is greater than or equal to 0 and less than <see cref="int.MaxValue"/>.</returns>
|
||||||
public int GetInt();
|
public int GetInt();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a random float that is greater than or equal to 0.0 and less than 1.0.
|
/// Get a random float that is greater than or equal to 0.0 and less than 1.0.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A random float that is greater than or equal to 0.0 and less than 1.0.</returns>
|
/// <returns>A random float that is greater than or equal to 0.0 and less than 1.0.</returns>
|
||||||
public float GetFloat();
|
public float GetFloat();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a random float that is greater than or equal to min and less than max.
|
/// Get a random float that is greater than or equal to min and less than max.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -41,7 +41,7 @@ public interface IRandom
|
||||||
/// <param name="max">The maximum value (exclusive).</param>
|
/// <param name="max">The maximum value (exclusive).</param>
|
||||||
/// <returns>A random float that is greater than or equal to min and less than max.</returns>
|
/// <returns>A random float that is greater than or equal to min and less than max.</returns>
|
||||||
public float GetFloat(float min, float max);
|
public float GetFloat(float min, float max);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a random boolean. 50% chance of being true.
|
/// Get a random boolean. 50% chance of being true.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -22,14 +22,12 @@ public readonly record struct StringKey
|
||||||
/// Converts a <see cref="StringKey"/> to a <see cref="string"/>.
|
/// Converts a <see cref="StringKey"/> to a <see cref="string"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static implicit operator string(StringKey key) => key._key;
|
public static implicit operator string(StringKey key) => key._key;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a <see cref="string"/> to a <see cref="StringKey"/>.
|
/// Converts a <see cref="string"/> to a <see cref="StringKey"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static implicit operator StringKey(string key)
|
public static implicit operator StringKey(string key) =>
|
||||||
{
|
string.IsNullOrWhiteSpace(key) ? default : new StringKey(key);
|
||||||
return string.IsNullOrWhiteSpace(key) ? default : new StringKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="string.ToString()"/>
|
/// <inheritdoc cref="string.ToString()"/>
|
||||||
public override string ToString() => _key.ToLowerInvariant();
|
public override string ToString() => _key.ToLowerInvariant();
|
||||||
|
|
|
@ -10,12 +10,9 @@ public class MoveDataTests
|
||||||
public record TestCaseData(IDynamicLibrary Library, IMoveData Move)
|
public record TestCaseData(IDynamicLibrary Library, IMoveData Move)
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString() => Move.Name + " has valid scripts";
|
||||||
{
|
|
||||||
return Move.Name + " has valid scripts";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<TestCaseData> AllMovesHaveValidScriptsData()
|
public static IEnumerable<TestCaseData> AllMovesHaveValidScriptsData()
|
||||||
{
|
{
|
||||||
var library = LibraryHelpers.LoadLibrary();
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
|
@ -27,9 +24,8 @@ public class MoveDataTests
|
||||||
yield return new TestCaseData(library, move);
|
yield return new TestCaseData(library, move);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test, MethodDataSource(nameof(AllMovesHaveValidScriptsData))]
|
||||||
[MethodDataSource(nameof(AllMovesHaveValidScriptsData))]
|
|
||||||
public async Task AllMoveEffectsHaveValidScripts(TestCaseData test)
|
public async Task AllMoveEffectsHaveValidScripts(TestCaseData test)
|
||||||
{
|
{
|
||||||
if (test.Move.SecondaryEffect == null)
|
if (test.Move.SecondaryEffect == null)
|
||||||
|
@ -43,7 +39,8 @@ public class MoveDataTests
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
throw new AggregateException($"Failed to resolve script for move {test.Move.Name} with effect {scriptName}", e);
|
throw new AggregateException($"Failed to resolve script for move {test.Move.Name} with effect {scriptName}",
|
||||||
|
e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,7 +28,7 @@ public class SpeciesDataloaderTests
|
||||||
typeLibrary.RegisterType("Dark");
|
typeLibrary.RegisterType("Dark");
|
||||||
typeLibrary.RegisterType("Steel");
|
typeLibrary.RegisterType("Steel");
|
||||||
typeLibrary.RegisterType("Fairy");
|
typeLibrary.RegisterType("Fairy");
|
||||||
|
|
||||||
var library = SpeciesDataLoader.LoadSpecies(file, typeLibrary);
|
var library = SpeciesDataLoader.LoadSpecies(file, typeLibrary);
|
||||||
await Assert.That(library).IsNotNull();
|
await Assert.That(library).IsNotNull();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ public class TypeDataloaderTests
|
||||||
await using var file = File.Open("Data/Types.csv", FileMode.Open, FileAccess.Read, FileShare.Read);
|
await using var file = File.Open("Data/Types.csv", FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
var library = TypeDataLoader.LoadTypeLibrary(file);
|
var library = TypeDataLoader.LoadTypeLibrary(file);
|
||||||
await Assert.That(library).IsNotNull();
|
await Assert.That(library).IsNotNull();
|
||||||
|
|
||||||
var fire = library.TryGetTypeIdentifier("Fire", out var fireId);
|
var fire = library.TryGetTypeIdentifier("Fire", out var fireId);
|
||||||
await Assert.That(fire).IsTrue();
|
await Assert.That(fire).IsTrue();
|
||||||
var grass = library.TryGetTypeIdentifier("Grass", out var grassId);
|
var grass = library.TryGetTypeIdentifier("Grass", out var grassId);
|
||||||
await Assert.That(grass).IsTrue();
|
await Assert.That(grass).IsTrue();
|
||||||
|
|
||||||
var fireEffectiveness = library.GetSingleEffectiveness(fireId, grassId);
|
var fireEffectiveness = library.GetSingleEffectiveness(fireId, grassId);
|
||||||
await Assert.That(fireEffectiveness).IsEqualTo(2.0f);
|
await Assert.That(fireEffectiveness).IsEqualTo(2.0f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
global using TUnit;
|
global using TUnit;
|
||||||
global using FluentAssertions;
|
global using FluentAssertions;
|
||||||
|
|
||||||
global using LevelInt = byte;
|
global using LevelInt = byte;
|
|
@ -26,8 +26,7 @@ public class IntegrationTestRunner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test, MethodDataSource(nameof(TestCases))]
|
||||||
[MethodDataSource(nameof(TestCases))]
|
|
||||||
public async Task RunIntegrationTest(IntegrationTestModel test)
|
public async Task RunIntegrationTest(IntegrationTestModel test)
|
||||||
{
|
{
|
||||||
var library = LibraryHelpers.LoadLibrary();
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
|
@ -40,11 +39,10 @@ public class IntegrationTestRunner
|
||||||
var pokemon = x.Pokemon[index];
|
var pokemon = x.Pokemon[index];
|
||||||
await Assert.That(library.StaticLibrary.Species.TryGet(pokemon.Species, out var species)).IsTrue();
|
await Assert.That(library.StaticLibrary.Species.TryGet(pokemon.Species, out var species)).IsTrue();
|
||||||
var mon = new PokemonImpl(library, species!, species!.GetDefaultForm(), new AbilityIndex
|
var mon = new PokemonImpl(library, species!, species!.GetDefaultForm(), new AbilityIndex
|
||||||
{
|
{
|
||||||
IsHidden = false,
|
IsHidden = false,
|
||||||
Index = 0,
|
Index = 0,
|
||||||
},
|
}, pokemon.Level, 0, Gender.Genderless, 0, "hardy");
|
||||||
pokemon.Level, 0, Gender.Genderless, 0, "hardy");
|
|
||||||
foreach (var move in pokemon.Moves)
|
foreach (var move in pokemon.Moves)
|
||||||
{
|
{
|
||||||
mon.LearnMove(move, MoveLearnMethod.Unknown, 255);
|
mon.LearnMove(move, MoveLearnMethod.Unknown, 255);
|
||||||
|
@ -56,7 +54,7 @@ public class IntegrationTestRunner
|
||||||
return new BattlePartyImpl(party, x.Indices.Select(y => new ResponsibleIndex(y[0], y[1])).ToArray());
|
return new BattlePartyImpl(party, x.Indices.Select(y => new ResponsibleIndex(y[0], y[1])).ToArray());
|
||||||
}).ProcessOneAtATime().GetResultsAsync();
|
}).ProcessOneAtATime().GetResultsAsync();
|
||||||
var battle = new BattleImpl(library, parties, test.BattleSetup.CanFlee, test.BattleSetup.NumberOfSides,
|
var battle = new BattleImpl(library, parties, test.BattleSetup.CanFlee, test.BattleSetup.NumberOfSides,
|
||||||
test.BattleSetup.PositionsPerSide, randomSeed: test.BattleSetup.Seed);
|
test.BattleSetup.PositionsPerSide, test.BattleSetup.Seed);
|
||||||
|
|
||||||
foreach (var action in test.Actions)
|
foreach (var action in test.Actions)
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,5 +37,5 @@ public static class LibraryHelpers
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
return dynamicLibrary;
|
return dynamicLibrary;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,11 +7,8 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||||
|
|
||||||
namespace PkmnLib.Tests.Integration.Models;
|
namespace PkmnLib.Tests.Integration.Models;
|
||||||
|
|
||||||
|
[JsonDerivedType(typeof(SetPokemonAction), "setPokemon"), JsonDerivedType(typeof(SetMoveChoiceAction), "setMoveChoice"),
|
||||||
[JsonDerivedType(typeof(SetPokemonAction), "setPokemon")]
|
JsonDerivedType(typeof(SetPassChoiceAction), "setPassChoice"), JsonDerivedType(typeof(AssertAction), "assert")]
|
||||||
[JsonDerivedType(typeof(SetMoveChoiceAction), "setMoveChoice")]
|
|
||||||
[JsonDerivedType(typeof(SetPassChoiceAction), "setPassChoice")]
|
|
||||||
[JsonDerivedType(typeof(AssertAction), "assert")]
|
|
||||||
public abstract class IntegrationTestAction
|
public abstract class IntegrationTestAction
|
||||||
{
|
{
|
||||||
public abstract Task Execute(IBattle battle);
|
public abstract Task Execute(IBattle battle);
|
||||||
|
@ -36,7 +33,6 @@ public class SetMoveChoiceAction : IntegrationTestAction
|
||||||
public string Move { get; set; } = null!;
|
public string Move { get; set; } = null!;
|
||||||
public List<byte> Target { get; set; } = null!;
|
public List<byte> Target { get; set; } = null!;
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task Execute(IBattle battle)
|
public override async Task Execute(IBattle battle)
|
||||||
{
|
{
|
||||||
|
@ -73,7 +69,7 @@ public class AssertAction : IntegrationTestAction
|
||||||
{
|
{
|
||||||
var list = battle.Path(Value).ToList();
|
var list = battle.Path(Value).ToList();
|
||||||
var value = list.Count == 1 ? list[0] : list;
|
var value = list.Count == 1 ? list[0] : list;
|
||||||
|
|
||||||
var serialized = JsonSerializer.Serialize(value);
|
var serialized = JsonSerializer.Serialize(value);
|
||||||
await Assert.That(serialized).IsEqualTo(Expected.ToJsonString());
|
await Assert.That(serialized).IsEqualTo(Expected.ToJsonString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,7 @@ public class IntegrationTestModel
|
||||||
public IntegrationTestAction[] Actions { get; set; } = null!;
|
public IntegrationTestAction[] Actions { get; set; } = null!;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString() => Name;
|
||||||
{
|
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IntegrationTestBattleSetup
|
public class IntegrationTestBattleSetup
|
||||||
|
|
|
@ -11,8 +11,7 @@ namespace PkmnLib.Tests.Static;
|
||||||
|
|
||||||
public class DeepCloneTests
|
public class DeepCloneTests
|
||||||
{
|
{
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
[SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "ValueParameterNotUsed")]
|
||||||
[SuppressMessage("ReSharper", "ValueParameterNotUsed")]
|
|
||||||
private class TestClass : IDeepCloneable
|
private class TestClass : IDeepCloneable
|
||||||
{
|
{
|
||||||
public int Value { get; set; }
|
public int Value { get; set; }
|
||||||
|
@ -70,8 +69,7 @@ public class DeepCloneTests
|
||||||
var clone = obj.DeepClone();
|
var clone = obj.DeepClone();
|
||||||
await Assert.That(clone).IsNotEqualTo(obj);
|
await Assert.That(clone).IsNotEqualTo(obj);
|
||||||
var clonePrivateField =
|
var clonePrivateField =
|
||||||
clone.GetType().GetField("_privateField", BindingFlags.NonPublic | BindingFlags.Instance)!
|
clone.GetType().GetField("_privateField", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(clone);
|
||||||
.GetValue(clone);
|
|
||||||
await Assert.That(clonePrivateField).IsEqualTo(1);
|
await Assert.That(clonePrivateField).IsEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,28 +91,24 @@ public class DeepCloneTests
|
||||||
await Assert.That(library.StaticLibrary.Species.TryGet("bulbasaur", out var bulbasaur)).IsTrue();
|
await Assert.That(library.StaticLibrary.Species.TryGet("bulbasaur", out var bulbasaur)).IsTrue();
|
||||||
await Assert.That(library.StaticLibrary.Species.TryGet("charmander", out var charmander)).IsTrue();
|
await Assert.That(library.StaticLibrary.Species.TryGet("charmander", out var charmander)).IsTrue();
|
||||||
var party1 = new PokemonParty(6);
|
var party1 = new PokemonParty(6);
|
||||||
party1.SwapInto(new PokemonImpl(library, bulbasaur!,
|
party1.SwapInto(new PokemonImpl(library, bulbasaur!, bulbasaur!.GetDefaultForm(), new AbilityIndex
|
||||||
bulbasaur!.GetDefaultForm(), new AbilityIndex
|
{
|
||||||
{
|
IsHidden = false,
|
||||||
IsHidden = false,
|
Index = 0,
|
||||||
Index = 0,
|
}, 50, 0, Gender.Male, 0, "hardy"), 0);
|
||||||
}, 50, 0,
|
|
||||||
Gender.Male, 0, "hardy"), 0);
|
|
||||||
var party2 = new PokemonParty(6);
|
var party2 = new PokemonParty(6);
|
||||||
party2.SwapInto(new PokemonImpl(library, charmander!,
|
party2.SwapInto(new PokemonImpl(library, charmander!, charmander!.GetDefaultForm(), new AbilityIndex
|
||||||
charmander!.GetDefaultForm(), new AbilityIndex
|
{
|
||||||
{
|
IsHidden = false,
|
||||||
IsHidden = false,
|
Index = 0,
|
||||||
Index = 0,
|
}, 50, 0, Gender.Male, 0, "hardy"), 0);
|
||||||
}, 50, 0,
|
|
||||||
Gender.Male, 0, "hardy"), 0);
|
|
||||||
|
|
||||||
var parties = new[]
|
var parties = new[]
|
||||||
{
|
{
|
||||||
new BattlePartyImpl(party1, [new ResponsibleIndex(0, 0)]),
|
new BattlePartyImpl(party1, [new ResponsibleIndex(0, 0)]),
|
||||||
new BattlePartyImpl(party2, [new ResponsibleIndex(1, 0)]),
|
new BattlePartyImpl(party2, [new ResponsibleIndex(1, 0)]),
|
||||||
};
|
};
|
||||||
var battle = new BattleImpl(library, parties, false, 2, 3, randomSeed: 0);
|
var battle = new BattleImpl(library, parties, false, 2, 3, 0);
|
||||||
battle.Sides[0].SwapPokemon(0, party1[0]);
|
battle.Sides[0].SwapPokemon(0, party1[0]);
|
||||||
battle.Sides[1].SwapPokemon(0, party2[0]);
|
battle.Sides[1].SwapPokemon(0, party2[0]);
|
||||||
party1[0]!.ChangeStatBoost(Statistic.Defense, 2, true);
|
party1[0]!.ChangeStatBoost(Statistic.Defense, 2, true);
|
||||||
|
|
|
@ -10,21 +10,23 @@ public class GrowthRateTests
|
||||||
Action act = () => _ = new LookupGrowthRate("Test", []);
|
Action act = () => _ = new LookupGrowthRate("Test", []);
|
||||||
act.Should().Throw<ArgumentException>().WithMessage("Experience table must have at least one entry.");
|
act.Should().Throw<ArgumentException>().WithMessage("Experience table must have at least one entry.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void NonZeroLookupGrowthRateTestShouldThrowArgumentException()
|
public void NonZeroLookupGrowthRateTestShouldThrowArgumentException()
|
||||||
{
|
{
|
||||||
Action act = () => _ = new LookupGrowthRate("Test", [1]);
|
Action act = () => _ = new LookupGrowthRate("Test", [1]);
|
||||||
act.Should().Throw<ArgumentException>().WithMessage("Experience table must start at 0.");
|
act.Should().Throw<ArgumentException>().WithMessage("Experience table must start at 0.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TooLargeLookupGrowthRateTestShouldThrowArgumentException()
|
public void TooLargeLookupGrowthRateTestShouldThrowArgumentException()
|
||||||
{
|
{
|
||||||
Action act = () => _ = new LookupGrowthRate("Test", Enumerable.Range(0, LevelInt.MaxValue + 1).Select(i => (uint)i));
|
Action act = () =>
|
||||||
act.Should().Throw<ArgumentException>().WithMessage($"Experience table may have at most {LevelInt.MaxValue} entries.");
|
_ = new LookupGrowthRate("Test", Enumerable.Range(0, LevelInt.MaxValue + 1).Select(i => (uint)i));
|
||||||
|
act.Should().Throw<ArgumentException>()
|
||||||
|
.WithMessage($"Experience table may have at most {LevelInt.MaxValue} entries.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void LookupGrowthRateTest()
|
public void LookupGrowthRateTest()
|
||||||
{
|
{
|
||||||
|
@ -35,7 +37,7 @@ public class GrowthRateTests
|
||||||
growthRate.CalculateLevel(3).Should().Be(4);
|
growthRate.CalculateLevel(3).Should().Be(4);
|
||||||
growthRate.CalculateLevel(4).Should().Be(5);
|
growthRate.CalculateLevel(4).Should().Be(5);
|
||||||
growthRate.CalculateLevel(5).Should().Be(6);
|
growthRate.CalculateLevel(5).Should().Be(6);
|
||||||
|
|
||||||
growthRate.CalculateExperience(1).Should().Be(0);
|
growthRate.CalculateExperience(1).Should().Be(0);
|
||||||
growthRate.CalculateExperience(2).Should().Be(1);
|
growthRate.CalculateExperience(2).Should().Be(1);
|
||||||
growthRate.CalculateExperience(3).Should().Be(2);
|
growthRate.CalculateExperience(3).Should().Be(2);
|
||||||
|
|
|
@ -15,8 +15,7 @@ public class StringKeyTests
|
||||||
yield return () => ("TeSt", "tesv", false);
|
yield return () => ("TeSt", "tesv", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test, MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
||||||
[MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
|
||||||
public async Task StringKeyEqualityTest(string k1, string k2, bool expected)
|
public async Task StringKeyEqualityTest(string k1, string k2, bool expected)
|
||||||
{
|
{
|
||||||
var sk1 = new StringKey(k1);
|
var sk1 = new StringKey(k1);
|
||||||
|
@ -24,8 +23,7 @@ public class StringKeyTests
|
||||||
await Assert.That(sk1 == sk2).IsEqualTo(expected);
|
await Assert.That(sk1 == sk2).IsEqualTo(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test, MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
||||||
[MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
|
||||||
public async Task HashCodeEqualityTest(string k1, string k2, bool expected)
|
public async Task HashCodeEqualityTest(string k1, string k2, bool expected)
|
||||||
{
|
{
|
||||||
var sk1 = new StringKey(k1);
|
var sk1 = new StringKey(k1);
|
||||||
|
@ -33,8 +31,7 @@ public class StringKeyTests
|
||||||
await Assert.That(sk1.GetHashCode() == sk2.GetHashCode()).IsEqualTo(expected);
|
await Assert.That(sk1.GetHashCode() == sk2.GetHashCode()).IsEqualTo(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test, MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
||||||
[MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
|
||||||
public async Task HashSetEqualityTest(string k1, string k2, bool expected)
|
public async Task HashSetEqualityTest(string k1, string k2, bool expected)
|
||||||
{
|
{
|
||||||
var sk1 = new StringKey(k1);
|
var sk1 = new StringKey(k1);
|
||||||
|
|
|
@ -27,21 +27,19 @@ public class DamageCalculatorTests
|
||||||
// Imagine a level 75 Glaceon
|
// Imagine a level 75 Glaceon
|
||||||
attacker.Setup(x => x.Level).Returns(75);
|
attacker.Setup(x => x.Level).Returns(75);
|
||||||
// with an effective Attack stat of 123
|
// with an effective Attack stat of 123
|
||||||
attacker.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(
|
attacker.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(1, 123, 1, 1, 1, 1));
|
||||||
1, 123, 1, 1, 1, 1));
|
|
||||||
// We use 10 as the Ice type
|
// We use 10 as the Ice type
|
||||||
attacker.Setup(x => x.Types).Returns([new TypeIdentifier(10)]);
|
attacker.Setup(x => x.Types).Returns([new TypeIdentifier(10)]);
|
||||||
|
|
||||||
var defender = new Mock<IPokemon>();
|
var defender = new Mock<IPokemon>();
|
||||||
// a Garchomp with an effective Defense stat of 163
|
// a Garchomp with an effective Defense stat of 163
|
||||||
defender.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(
|
defender.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(1, 1, 163, 1, 1, 1));
|
||||||
1, 1, 163, 1, 1, 1));
|
|
||||||
defender.Setup(x => x.GetScripts()).Returns(new ScriptIterator([]));
|
defender.Setup(x => x.GetScripts()).Returns(new ScriptIterator([]));
|
||||||
|
|
||||||
var useMove = new Mock<IMoveData>();
|
var useMove = new Mock<IMoveData>();
|
||||||
// Ice Fang (an Ice-type physical move with a power of 65)
|
// Ice Fang (an Ice-type physical move with a power of 65)
|
||||||
useMove.Setup(x => x.Category).Returns(MoveCategory.Physical);
|
useMove.Setup(x => x.Category).Returns(MoveCategory.Physical);
|
||||||
|
|
||||||
var damageCalculator = new Gen7DamageCalculator(false);
|
var damageCalculator = new Gen7DamageCalculator(false);
|
||||||
var executingMove = new Mock<IExecutingMove>();
|
var executingMove = new Mock<IExecutingMove>();
|
||||||
executingMove.Setup(x => x.UseMove).Returns(useMove.Object);
|
executingMove.Setup(x => x.UseMove).Returns(useMove.Object);
|
||||||
|
@ -54,7 +52,7 @@ public class DamageCalculatorTests
|
||||||
hit.Setup(x => x.Type).Returns(new TypeIdentifier(10));
|
hit.Setup(x => x.Type).Returns(new TypeIdentifier(10));
|
||||||
// has a double weakness to the move's Ice type
|
// has a double weakness to the move's Ice type
|
||||||
hit.Setup(x => x.Effectiveness).Returns(4.0f);
|
hit.Setup(x => x.Effectiveness).Returns(4.0f);
|
||||||
|
|
||||||
var damage = damageCalculator.GetDamage(executingMove.Object, defender.Object, 0, hit.Object);
|
var damage = damageCalculator.GetDamage(executingMove.Object, defender.Object, 0, hit.Object);
|
||||||
// That means Ice Fang will do between 168 and 196 HP damage, depending on luck.
|
// That means Ice Fang will do between 168 and 196 HP damage, depending on luck.
|
||||||
// Note that we are testing deterministic damage, so we expect the maximum damage.
|
// Note that we are testing deterministic damage, so we expect the maximum damage.
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class AcrobaticsTests
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(basePower).IsEqualTo((byte)20);
|
await Assert.That(basePower).IsEqualTo((byte)20);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task ChangeBasePower_UserHoldingItem_BasePowerUnchanged()
|
public async Task ChangeBasePower_UserHoldingItem_BasePowerUnchanged()
|
||||||
{
|
{
|
||||||
|
@ -59,5 +59,4 @@ public class AcrobaticsTests
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(basePower).IsEqualTo(byte.MaxValue);
|
await Assert.That(basePower).IsEqualTo(byte.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue