Style cleanup
This commit is contained in:
parent
c0bc905c46
commit
284ab3079c
|
@ -28,13 +28,13 @@ public static class ItemDataLoader
|
|||
}
|
||||
|
||||
public delegate IItem ItemFactoryDelegate(SerializedItem serialized, StringKey name, ItemCategory type,
|
||||
BattleItemCategory battleType, int price, ImmutableHashSet<StringKey> flags,
|
||||
ISecondaryEffect? effect, ISecondaryEffect? battleTriggerEffect, byte flingPower);
|
||||
BattleItemCategory battleType, int price, ImmutableHashSet<StringKey> flags, ISecondaryEffect? effect,
|
||||
ISecondaryEffect? battleTriggerEffect, byte flingPower);
|
||||
|
||||
[PublicAPI]
|
||||
public static ItemFactoryDelegate ItemConstructor { get; set; } = (_, name, type, battleType, price, flags, effect,
|
||||
battleTriggerEffect, flingPower) =>
|
||||
new ItemImpl(name, type, battleType, price, flags, effect, battleTriggerEffect, flingPower);
|
||||
public static ItemFactoryDelegate ItemConstructor { get; set; } =
|
||||
(_, name, type, battleType, price, flags, effect, battleTriggerEffect, flingPower) => new ItemImpl(name, type,
|
||||
battleType, price, flags, effect, battleTriggerEffect, flingPower);
|
||||
|
||||
private static IItem DeserializeItem(SerializedItem serialized)
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace PkmnLib.Dataloader;
|
|||
|
||||
internal static class JsonOptions
|
||||
{
|
||||
public static JsonSerializerOptions DefaultOptions => new JsonSerializerOptions()
|
||||
public static JsonSerializerOptions DefaultOptions => new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
AllowTrailingCommas = true,
|
||||
|
|
|
@ -15,6 +15,5 @@ public class SerializedItem
|
|||
public SerializedMoveEffect? Effect { get; set; }
|
||||
public SerializedMoveEffect? BattleEffect { get; set; }
|
||||
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
}
|
|
@ -23,8 +23,7 @@ public class SerializedMove
|
|||
public string[] Flags { get; set; } = null!;
|
||||
public SerializedMoveEffect? Effect { get; set; }
|
||||
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
}
|
||||
|
||||
public class SerializedMoveEffect
|
||||
|
|
|
@ -21,8 +21,7 @@ public class SerializedSpecies
|
|||
public Dictionary<string, SerializedForm> Formes { get; set; } = null!;
|
||||
public SerializedEvolution[] Evolutions { get; set; } = [];
|
||||
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
}
|
||||
|
||||
public class SerializedForm
|
||||
|
@ -39,8 +38,7 @@ public class SerializedForm
|
|||
public SerializedMoves Moves { get; set; } = null!;
|
||||
public string[] Flags { get; set; } = [];
|
||||
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
}
|
||||
|
||||
public class SerializedEvolution
|
||||
|
|
|
@ -26,11 +26,12 @@ public static class MoveDataLoader
|
|||
return library;
|
||||
}
|
||||
|
||||
public static Func<SerializedMove, StringKey, TypeIdentifier, MoveCategory, byte, byte, byte, MoveTarget, sbyte,
|
||||
ISecondaryEffect?, IEnumerable<StringKey>, MoveDataImpl> MoveConstructor =
|
||||
(serialized, name, moveType, category, basePower, accuracy, baseUsages, target, priority, secondaryEffect,
|
||||
flags) => new MoveDataImpl(name, moveType, category, basePower, accuracy, baseUsages, target, priority,
|
||||
secondaryEffect, flags);
|
||||
public static
|
||||
Func<SerializedMove, StringKey, TypeIdentifier, MoveCategory, byte, byte, byte, MoveTarget, sbyte,
|
||||
ISecondaryEffect?, IEnumerable<StringKey>, MoveDataImpl> MoveConstructor =
|
||||
(serialized, name, moveType, category, basePower, accuracy, baseUsages, target, priority, secondaryEffect,
|
||||
flags) => new MoveDataImpl(name, moveType, category, basePower, accuracy, baseUsages, target, priority,
|
||||
secondaryEffect, flags);
|
||||
|
||||
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.");
|
||||
var secondaryEffect = effect.ParseEffect();
|
||||
|
||||
var move = MoveConstructor(serialized, serialized.Name, typeIdentifier, categoryEnum, power, accuracy, pp, targetEnum,
|
||||
priority, secondaryEffect, flags.Select(x => (StringKey)x).ToImmutableHashSet());
|
||||
var move = MoveConstructor(serialized, serialized.Name, typeIdentifier, categoryEnum, power, accuracy, pp,
|
||||
targetEnum, priority, secondaryEffect, flags.Select(x => (StringKey)x).ToImmutableHashSet());
|
||||
return move;
|
||||
}
|
||||
|
||||
}
|
|
@ -20,8 +20,8 @@ public static class SpeciesDataLoader
|
|||
var obj = JsonSerializer.Deserialize<JsonObject>(stream, JsonOptions.DefaultOptions);
|
||||
if (obj == null)
|
||||
throw new InvalidDataException("Species data is empty.");
|
||||
return obj.Where(x => x.Key != "$schema")
|
||||
.ToDictionary(x => x.Key, x => x.Value.Deserialize<SerializedSpecies>(JsonOptions.DefaultOptions));
|
||||
return obj.Where(x => x.Key != "$schema").ToDictionary(x => x.Key,
|
||||
x => x.Value.Deserialize<SerializedSpecies>(JsonOptions.DefaultOptions));
|
||||
}
|
||||
|
||||
public static SpeciesLibrary LoadSpecies(Stream[] streams, IReadOnlyTypeLibrary typeLibrary)
|
||||
|
@ -48,34 +48,37 @@ public static class SpeciesDataLoader
|
|||
return library;
|
||||
}
|
||||
|
||||
public static Func<SerializedSpecies, ushort, StringKey, float, StringKey, byte, byte, IReadOnlyDictionary<StringKey, IForm>,
|
||||
IEnumerable<StringKey>, IReadOnlyList<IEvolution>, IEnumerable<StringKey>, SpeciesImpl> SpeciesConstructor =
|
||||
(_, id, name, genderRate, growthRate, captureRate, baseHappiness, forms, flags, evolutionData,
|
||||
eggGroups) =>
|
||||
{
|
||||
return new SpeciesImpl(id, name, genderRate, growthRate,
|
||||
captureRate, baseHappiness, forms,
|
||||
flags, evolutionData, eggGroups);
|
||||
};
|
||||
public static
|
||||
Func<SerializedSpecies, ushort, StringKey, float, StringKey, byte, byte, IReadOnlyDictionary<StringKey, IForm>,
|
||||
IEnumerable<StringKey>, IReadOnlyList<IEvolution>, IEnumerable<StringKey>, SpeciesImpl> SpeciesConstructor =
|
||||
(_, id, name, genderRate, growthRate, captureRate, baseHappiness, forms, flags, evolutionData, eggGroups) =>
|
||||
{
|
||||
return new SpeciesImpl(id, name, genderRate, growthRate, captureRate, baseHappiness, forms, flags,
|
||||
evolutionData, eggGroups);
|
||||
};
|
||||
|
||||
private static SpeciesImpl DeserializeSpecies(SerializedSpecies serialized, IReadOnlyTypeLibrary typeLibrary)
|
||||
{
|
||||
var id = serialized.Id;
|
||||
var genderRate = serialized.GenderRatio;
|
||||
if (genderRate < -1.0 || genderRate > 100.0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Gender rate for species {id} is invalid: {genderRate}. Must be between -1.0 and 100.0.");
|
||||
}
|
||||
|
||||
if (serialized.EggCycles < 0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"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,
|
||||
x => DeserializeForm(x.Key, x.Value, typeLibrary));
|
||||
var evolutions = serialized.Evolutions.Select(DeserializeEvolution).ToList();
|
||||
|
||||
var species = SpeciesConstructor(serialized, serialized.Id, serialized.Species, genderRate, serialized.GrowthRate,
|
||||
serialized.CatchRate, serialized.BaseHappiness, forms,
|
||||
var species = SpeciesConstructor(serialized, serialized.Id, serialized.Species, genderRate,
|
||||
serialized.GrowthRate, serialized.CatchRate, serialized.BaseHappiness, forms,
|
||||
serialized.Flags.Select(x => new StringKey(x)), evolutions, serialized.EggGroups.Select(x => (StringKey)x));
|
||||
return species;
|
||||
}
|
||||
|
@ -85,11 +88,15 @@ public static class SpeciesDataLoader
|
|||
if (form == null)
|
||||
throw new ArgumentException("Form data is null.", nameof(form));
|
||||
if (form.Height < 0.0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Height for form {name} is invalid: {form.Height}. Must be greater than or equal to 0.0.");
|
||||
}
|
||||
if (form.Weight < 0.0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Weight for form {name} is invalid: {form.Weight}. Must be greater than or equal to 0.0.");
|
||||
}
|
||||
var types = form.Types.Select(x =>
|
||||
typeLibrary.TryGetTypeIdentifier(new StringKey(x), out var t)
|
||||
? t
|
||||
|
@ -105,25 +112,26 @@ public static class SpeciesDataLoader
|
|||
{
|
||||
var learnableMoves = new LearnableMovesImpl();
|
||||
if (moves.LevelMoves != null)
|
||||
{
|
||||
foreach (var levelMove in moves.LevelMoves)
|
||||
{
|
||||
learnableMoves.AddLevelMove((byte)levelMove.Level, new StringKey(levelMove.Name));
|
||||
}
|
||||
}
|
||||
|
||||
if (moves.EggMoves != null)
|
||||
{
|
||||
foreach (var eggMove in moves.EggMoves)
|
||||
{
|
||||
learnableMoves.AddEggMove(new StringKey(eggMove));
|
||||
}
|
||||
}
|
||||
|
||||
return learnableMoves;
|
||||
}
|
||||
|
||||
private static ImmutableStatisticSet<ushort> DeserializeStats(SerializedStats stats)
|
||||
{
|
||||
return new ImmutableStatisticSet<ushort>(stats.Hp, stats.Attack, stats.Defense, stats.SpecialAttack,
|
||||
stats.SpecialDefense, stats.Speed);
|
||||
}
|
||||
private static ImmutableStatisticSet<ushort> DeserializeStats(SerializedStats stats) =>
|
||||
new(stats.Hp, stats.Attack, stats.Defense, stats.SpecialAttack, stats.SpecialDefense, stats.Speed);
|
||||
|
||||
private static IEvolution DeserializeEvolution(SerializedEvolution evolution)
|
||||
{
|
||||
|
@ -223,8 +231,7 @@ public static class SpeciesDataLoader
|
|||
{
|
||||
Name = evolution.Data.AsObject()["type"]?.GetValue<string>() ??
|
||||
throw new InvalidDataException("Type is null."),
|
||||
Parameters = evolution.Data.AsObject()
|
||||
.Where(x => x.Key != "type")
|
||||
Parameters = evolution.Data.AsObject().Where(x => x.Key != "type")
|
||||
.ToDictionary(x => new StringKey(x.Key), x => x.Value!.ToParameter()),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
|
|
|
@ -42,8 +42,10 @@ public static class TypeDataLoader
|
|||
{
|
||||
var effectiveness = float.Parse(values[i]);
|
||||
if (effectiveness < 0.0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Effectiveness for {type} against {types[i]} is invalid: {effectiveness}. Must be greater than or equal to 0.0.");
|
||||
}
|
||||
library.SetEffectiveness(typeId, (TypeIdentifier)i, effectiveness);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,5 @@ public class PassTurnAI : PokemonAI
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
||||
{
|
||||
return new PassChoice(pokemon);
|
||||
}
|
||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon) => new PassChoice(pokemon);
|
||||
}
|
|
@ -45,6 +45,4 @@ public class RandomAI : PokemonAI
|
|||
}
|
||||
return new PassChoice(pokemon);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -15,7 +15,6 @@ public class ExperienceGainEvent : IEventData
|
|||
public uint PreviousExperience { get; }
|
||||
public uint NewExperience { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public EventBatchId BatchId { get; init; }
|
||||
}
|
|
@ -30,7 +30,6 @@ public class HealEvent : IEventData
|
|||
/// </summary>
|
||||
public uint NewHealth { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public EventBatchId BatchId { get; init; } = new();
|
||||
}
|
|
@ -16,7 +16,7 @@ public record struct CaptureResult
|
|||
public int Shakes { 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
|
||||
|
|
|
@ -66,12 +66,13 @@ public class DynamicLibraryImpl : IDynamicLibrary
|
|||
if (registry.CaptureLibrary is null)
|
||||
throw new InvalidOperationException("Capture library not found in plugins.");
|
||||
var scriptResolver = new ScriptResolver(registry.ScriptTypes, registry.ItemScriptTypes);
|
||||
return new DynamicLibraryImpl(staticLibrary, registry.BattleStatCalculator,
|
||||
registry.DamageCalculator, registry.MiscLibrary, registry.CaptureLibrary, scriptResolver);
|
||||
return new DynamicLibraryImpl(staticLibrary, registry.BattleStatCalculator, registry.DamageCalculator,
|
||||
registry.MiscLibrary, registry.CaptureLibrary, scriptResolver);
|
||||
}
|
||||
|
||||
private DynamicLibraryImpl(IStaticLibrary staticLibrary, IBattleStatCalculator statCalculator,
|
||||
IDamageCalculator damageCalculator, IMiscLibrary miscLibrary, ICaptureLibrary captureLibrary, ScriptResolver scriptResolver)
|
||||
IDamageCalculator damageCalculator, IMiscLibrary miscLibrary, ICaptureLibrary captureLibrary,
|
||||
ScriptResolver scriptResolver)
|
||||
{
|
||||
StaticLibrary = staticLibrary;
|
||||
StatCalculator = statCalculator;
|
||||
|
|
|
@ -249,8 +249,8 @@ public class BattleImpl : ScriptSource, IBattle
|
|||
}
|
||||
|
||||
ITurnChoice? forcedChoice = null;
|
||||
pokemon.RunScriptHook(
|
||||
script => script.ForceTurnSelection(battleData.SideIndex, battleData.Position, ref forcedChoice));
|
||||
pokemon.RunScriptHook(script =>
|
||||
script.ForceTurnSelection(battleData.SideIndex, battleData.Position, ref forcedChoice));
|
||||
choice = forcedChoice;
|
||||
return choice != null;
|
||||
}
|
||||
|
@ -386,7 +386,6 @@ public class BattleImpl : ScriptSource, IBattle
|
|||
/// <inheritdoc />
|
||||
public StringKey? TerrainName => _terrainScript.Script?.Name;
|
||||
|
||||
|
||||
private readonly List<IReadOnlyList<ITurnChoice>> _previousTurnChoices = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
@ -51,7 +51,6 @@ public class BattleChoiceQueue : IDeepCloneable
|
|||
/// </summary>
|
||||
public bool HasNext() => _currentIndex < _choices.Length;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
|
@ -102,5 +101,6 @@ public class BattleChoiceQueue : IDeepCloneable
|
|||
|
||||
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);
|
||||
}
|
|
@ -31,11 +31,11 @@ internal static class MoveTurnExecutor
|
|||
moveChoice.Script.Set(script);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
byte numberOfHits = 1;
|
||||
|
@ -140,8 +140,8 @@ internal static class MoveTurnExecutor
|
|||
// modifying it.
|
||||
if (accuracy != 255)
|
||||
{
|
||||
accuracy = battle.Library.StatCalculator.CalculateModifiedAccuracy(executingMove, target,
|
||||
hitIndex, accuracy);
|
||||
accuracy = battle.Library.StatCalculator.CalculateModifiedAccuracy(executingMove, target, hitIndex,
|
||||
accuracy);
|
||||
}
|
||||
|
||||
if (accuracy < 100 && battle.Random.GetInt(100) >= accuracy)
|
||||
|
@ -198,14 +198,16 @@ internal static class MoveTurnExecutor
|
|||
if (secondaryEffect != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var chance = secondaryEffect.Chance;
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,6 +226,5 @@ internal static class MoveTurnExecutor
|
|||
{
|
||||
executingMove.RunScriptHook(x => x.OnAfterHits(executingMove, target));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -15,8 +15,8 @@ public static class TargetResolver
|
|||
return target switch
|
||||
{
|
||||
MoveTarget.Adjacent or MoveTarget.AdjacentAlly or MoveTarget.AdjacentAllySelf or MoveTarget.AdjacentOpponent
|
||||
or MoveTarget.Any or MoveTarget.RandomOpponent
|
||||
or MoveTarget.SelfUse => [battle.GetPokemon(side, position)],
|
||||
or MoveTarget.Any or MoveTarget.RandomOpponent or MoveTarget.SelfUse =>
|
||||
[battle.GetPokemon(side, position)],
|
||||
MoveTarget.All => GetAllTargets(battle),
|
||||
MoveTarget.AllAdjacentOpponent => GetAllAdjacentAndOpponent(battle, side, position),
|
||||
MoveTarget.AllAdjacent => GetAllAdjacent(battle, side, position),
|
||||
|
@ -144,7 +144,8 @@ public static class TargetResolver
|
|||
|
||||
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,8 +16,10 @@ public static class TurnRunner
|
|||
{
|
||||
var queue = battle.ChoiceQueue;
|
||||
if (queue == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(battle.ChoiceQueue),
|
||||
"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
|
||||
// choices, and put them in the correct order.
|
||||
|
@ -122,7 +124,6 @@ public static class TurnRunner
|
|||
userSide.SwapPokemon(battleData.Position, fleeChoice.SwitchTo);
|
||||
}
|
||||
|
||||
|
||||
private static void ExecuteFleeChoice(IBattle battle, IFleeChoice fleeChoice)
|
||||
{
|
||||
var user = fleeChoice.User;
|
||||
|
@ -171,5 +172,4 @@ public static class TurnRunner
|
|||
}
|
||||
itemChoice.Item.RunItemScript(battle.Library.ScriptResolver, target ?? user);
|
||||
}
|
||||
|
||||
}
|
|
@ -158,6 +158,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
|||
public byte NumberOfPositions { get; }
|
||||
|
||||
private readonly IPokemon?[] _pokemon;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<IPokemon?> Pokemon => _pokemon;
|
||||
|
||||
|
@ -170,6 +171,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
|||
public bool AllChoicesSet => _setChoices.All(choice => choice is not null);
|
||||
|
||||
private readonly bool[] _fillablePositions;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<bool> FillablePositions => _fillablePositions;
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ namespace PkmnLib.Dynamic.Models.Choices;
|
|||
/// </summary>
|
||||
public interface IFleeChoice : ITurnChoice
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFleeChoice"/>
|
||||
|
@ -22,7 +21,9 @@ public class FleeTurnChoice : TurnChoice, IFleeChoice
|
|||
public override int ScriptCount => User.ScriptCount;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts) { }
|
||||
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts) => User.CollectScripts(scripts);
|
||||
|
|
|
@ -7,7 +7,6 @@ namespace PkmnLib.Dynamic.Models.Choices;
|
|||
/// </summary>
|
||||
public interface IPassChoice : ITurnChoice
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class PassChoice : TurnChoice, IPassChoice
|
||||
|
|
|
@ -82,5 +82,5 @@ public class TurnChoiceComparer : IComparer<ITurnChoice>
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Compare(ITurnChoice? x, ITurnChoice? y) => (int) CompareImpl(x, y);
|
||||
public int Compare(ITurnChoice? x, ITurnChoice? y) => (int)CompareImpl(x, y);
|
||||
}
|
|
@ -95,8 +95,7 @@ public class LearnedMoveImpl : ILearnedMove
|
|||
CurrentPp = MaxPp;
|
||||
}
|
||||
|
||||
public LearnedMoveImpl(IMoveData moveData, MoveLearnMethod learnMethod, byte pp)
|
||||
: this(moveData, learnMethod)
|
||||
public LearnedMoveImpl(IMoveData moveData, MoveLearnMethod learnMethod, byte pp) : this(moveData, learnMethod)
|
||||
{
|
||||
CurrentPp = pp;
|
||||
}
|
||||
|
|
|
@ -337,10 +337,12 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
|||
/// Checks whether the Pokémon has a specific non-volatile status.
|
||||
/// </summary>
|
||||
bool HasStatus(StringKey status);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a non-volatile status to the Pokemon.
|
||||
/// </summary>
|
||||
void SetStatus(StringKey status);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the current non-volatile status from the Pokemon.
|
||||
/// </summary>
|
||||
|
@ -686,7 +688,11 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||
private List<TypeIdentifier> _types = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<TypeIdentifier> Types { get => _types; private set => _types = value.ToList(); }
|
||||
public IReadOnlyList<TypeIdentifier> Types
|
||||
{
|
||||
get => _types;
|
||||
private set => _types = value.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEgg { get; private set; }
|
||||
|
@ -989,7 +995,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||
{
|
||||
if (IsFainted && !allowRevive)
|
||||
return false;
|
||||
var maxAmount = this.BoostedStats.Hp - CurrentHealth;
|
||||
var maxAmount = BoostedStats.Hp - CurrentHealth;
|
||||
if (heal > maxAmount)
|
||||
heal = maxAmount;
|
||||
if (heal == 0)
|
||||
|
@ -1019,7 +1025,8 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||
{
|
||||
for (byte i = 0; i < Moves.Count; i++)
|
||||
{
|
||||
if (Moves[i] is not null) continue;
|
||||
if (Moves[i] is not null)
|
||||
continue;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ public class PokemonParty : IPokemonParty
|
|||
_pokemon = new IPokemon[size];
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<(IPokemon?, int index)>? OnSwapInto;
|
||||
|
||||
|
@ -73,7 +72,6 @@ public class PokemonParty : IPokemonParty
|
|||
OnSwap?.Invoke(this, (index1, index2));
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasUsablePokemon() => _pokemon.Any(p => p is { IsUsable: true });
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ public abstract class ItemScript : IDeepCloneable
|
|||
|
||||
protected IItem Item { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the script with the given parameters for a specific item
|
||||
/// </summary>
|
||||
|
|
|
@ -3,8 +3,7 @@ using PkmnLib.Static.Utils;
|
|||
|
||||
namespace PkmnLib.Dynamic.ScriptHandling.Registry;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
[MeansImplicitUse]
|
||||
[AttributeUsage(AttributeTargets.Class), MeansImplicitUse]
|
||||
public class ItemScriptAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -6,8 +6,7 @@ namespace PkmnLib.Dynamic.ScriptHandling.Registry;
|
|||
/// <summary>
|
||||
/// Helper attribute to register scripts through reflection.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
[MeansImplicitUse]
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false), MeansImplicitUse]
|
||||
public class ScriptAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -83,32 +83,32 @@ public class ScriptRegistry
|
|||
// This is more performant than using Activator.CreateInstance.
|
||||
var parameterExpression = Expression.Parameter(typeof(IItem), "item");
|
||||
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>
|
||||
/// Register a battle stat calculator.
|
||||
/// </summary>
|
||||
public void RegisterBattleStatCalculator<T>(T battleStatCalculator)
|
||||
where T : IBattleStatCalculator => _battleStatCalculator = battleStatCalculator;
|
||||
public void RegisterBattleStatCalculator<T>(T battleStatCalculator) where T : IBattleStatCalculator =>
|
||||
_battleStatCalculator = battleStatCalculator;
|
||||
|
||||
/// <summary>
|
||||
/// Register a damage calculator.
|
||||
/// </summary>
|
||||
public void RegisterDamageCalculator<T>(T damageCalculator)
|
||||
where T : IDamageCalculator => _damageCalculator = damageCalculator;
|
||||
public void RegisterDamageCalculator<T>(T damageCalculator) where T : IDamageCalculator =>
|
||||
_damageCalculator = damageCalculator;
|
||||
|
||||
/// <summary>
|
||||
/// Register a misc library.
|
||||
/// </summary>
|
||||
public void RegisterMiscLibrary<T>(T miscLibrary) where T : IMiscLibrary
|
||||
=> _miscLibrary = miscLibrary;
|
||||
public void RegisterMiscLibrary<T>(T miscLibrary) where T : IMiscLibrary => _miscLibrary = miscLibrary;
|
||||
|
||||
/// <summary>
|
||||
/// Register a capture library.
|
||||
/// </summary>
|
||||
public void RegisterCaptureLibrary<T>(T captureLibrary) where T : ICaptureLibrary
|
||||
=> _captureLibrary = captureLibrary;
|
||||
public void RegisterCaptureLibrary<T>(T captureLibrary) where T : ICaptureLibrary =>
|
||||
_captureLibrary = captureLibrary;
|
||||
|
||||
internal IReadOnlyDictionary<(ScriptCategory category, StringKey name), Func<Script>> ScriptTypes => _scriptTypes;
|
||||
internal IReadOnlyDictionary<StringKey, Func<IItem, ItemScript>> ItemScriptTypes => _itemScriptTypes;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -38,11 +38,7 @@ public class ScriptContainer : IEnumerable<ScriptContainer>, IDeepCloneable
|
|||
yield return this;
|
||||
}
|
||||
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a new script to this container. If there was a script already, it is removed.
|
||||
|
|
|
@ -66,5 +66,4 @@ public static class ScriptExecution
|
|||
itemScript.OnUse();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -46,14 +46,8 @@ public class ScriptIterator : IEnumerable<ScriptContainer>
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<ScriptContainer> GetEnumerator()
|
||||
{
|
||||
return GetAsEnumerable().GetEnumerator();
|
||||
}
|
||||
public IEnumerator<ScriptContainer> GetEnumerator() => GetAsEnumerable().GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
|
@ -155,8 +155,5 @@ public class ScriptSet : IScriptSet
|
|||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<StringKey> GetScriptNames() =>
|
||||
_scripts
|
||||
.Select(x => x.Script)
|
||||
.WhereNotNull()
|
||||
.Select(s => s.Name);
|
||||
_scripts.Select(x => x.Script).WhereNotNull().Select(s => s.Name);
|
||||
}
|
|
@ -64,13 +64,14 @@ public class LookupGrowthRate : IGrowthRate
|
|||
}
|
||||
}
|
||||
|
||||
return (LevelInt)(_experienceTable.Length);
|
||||
return (LevelInt)_experienceTable.Length;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint CalculateExperience(LevelInt level)
|
||||
{
|
||||
if (level < 1) level = 1;
|
||||
if (level < 1)
|
||||
level = 1;
|
||||
return level >= _experienceTable.Length ? _experienceTable[^1] : _experienceTable[level - 1];
|
||||
}
|
||||
}
|
|
@ -160,10 +160,6 @@ public class ItemImpl : IItem
|
|||
/// <inheritdoc />
|
||||
public byte FlingPower { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasFlag(string key)
|
||||
{
|
||||
return Flags.Contains(key);
|
||||
}
|
||||
public bool HasFlag(string key) => Flags.Contains(key);
|
||||
}
|
|
@ -7,13 +7,13 @@ namespace PkmnLib.Static.Libraries;
|
|||
/// <summary>
|
||||
/// A basic library for data types. Stores data both by name and by index.
|
||||
/// </summary>
|
||||
public abstract class DataLibrary<T> : IEnumerable<T>
|
||||
where T : INamedValue
|
||||
public abstract class DataLibrary<T> : IEnumerable<T> where T : INamedValue
|
||||
{
|
||||
/// <summary>
|
||||
/// The underlying data storage.
|
||||
/// </summary>
|
||||
protected readonly Dictionary<StringKey, T> Data = new();
|
||||
|
||||
private readonly List<T> _values = [];
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -12,14 +12,17 @@ public interface IReadOnlyGrowthRateLibrary : IEnumerable<IGrowthRate>
|
|||
/// Tries to get a growth rate from the library. Returns false if the growth rate is not found.
|
||||
/// </summary>
|
||||
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IGrowthRate value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a random growth rate from the library.
|
||||
/// </summary>
|
||||
IGrowthRate GetRandom(IRandom random);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of growth rates in the library.
|
||||
/// </summary>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the library is empty.
|
||||
/// </summary>
|
||||
|
|
|
@ -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.
|
||||
/// </summary>
|
||||
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IMoveData value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a random move from the library.
|
||||
/// </summary>
|
||||
IMoveData GetRandom(IRandom random);
|
||||
|
||||
/// <summary>
|
||||
/// The amount of moves in the library.
|
||||
/// </summary>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the library is empty.
|
||||
/// </summary>
|
||||
|
|
|
@ -9,22 +9,27 @@ public interface IStaticLibrary
|
|||
/// The miscellaneous settings for the library.
|
||||
/// </summary>
|
||||
LibrarySettings Settings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All data for Pokémon species.
|
||||
/// </summary>
|
||||
IReadOnlySpeciesLibrary Species { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All data for Pokémon moves.
|
||||
/// </summary>
|
||||
IReadOnlyMoveLibrary Moves { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All data for Pokémon abilities.
|
||||
/// </summary>
|
||||
IReadOnlyAbilityLibrary Abilities { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All data for Pokémon types and their effectiveness.
|
||||
/// </summary>
|
||||
IReadOnlyTypeLibrary Types { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All data for Pokémon natures.
|
||||
/// </summary>
|
||||
|
@ -45,7 +50,9 @@ public interface IStaticLibrary
|
|||
public class StaticLibraryImpl : IStaticLibrary
|
||||
{
|
||||
/// <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;
|
||||
Species = species;
|
||||
|
|
|
@ -88,7 +88,7 @@ public class TypeLibrary : IReadOnlyTypeLibrary
|
|||
/// </summary>
|
||||
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);
|
||||
_effectiveness.Add(Enumerable.Repeat(1.0f, _effectiveness.Count).ToList());
|
||||
foreach (var list in _effectiveness)
|
||||
|
|
|
@ -50,8 +50,7 @@ public class Nature(
|
|||
Statistic increaseStat,
|
||||
Statistic decreaseStat,
|
||||
float increaseModifier,
|
||||
float decreaseModifier)
|
||||
: INature
|
||||
float decreaseModifier) : INature
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public StringKey Name { get; } = name;
|
||||
|
@ -79,8 +78,6 @@ public class Nature(
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(INature? other)
|
||||
{
|
||||
return other is not null && StringComparer.InvariantCultureIgnoreCase.Equals(Name, other.Name);
|
||||
}
|
||||
public bool Equals(INature? other) =>
|
||||
other is not null && StringComparer.InvariantCultureIgnoreCase.Equals(Name, other.Name);
|
||||
}
|
|
@ -26,7 +26,8 @@ public interface IAbility : INamedValue
|
|||
public class AbilityImpl : IAbility
|
||||
{
|
||||
/// <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;
|
||||
Effect = effect;
|
||||
|
@ -46,10 +47,7 @@ public class AbilityImpl : IAbility
|
|||
public ImmutableHashSet<StringKey> Flags;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasFlag(StringKey key)
|
||||
{
|
||||
return Flags.Contains(key);
|
||||
}
|
||||
public bool HasFlag(StringKey key) => Flags.Contains(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -95,8 +95,8 @@ public interface IForm : INamedValue
|
|||
public class FormImpl : IForm
|
||||
{
|
||||
/// <inheritdoc cref="FormImpl" />
|
||||
public FormImpl(StringKey name, float height, float weight, uint baseExperience,
|
||||
IEnumerable<TypeIdentifier> types, ImmutableStatisticSet<ushort> baseStats, IEnumerable<StringKey> abilities,
|
||||
public FormImpl(StringKey name, float height, float weight, uint baseExperience, IEnumerable<TypeIdentifier> types,
|
||||
ImmutableStatisticSet<ushort> baseStats, IEnumerable<StringKey> abilities,
|
||||
IEnumerable<StringKey> hiddenAbilities, ILearnableMoves moves, ImmutableHashSet<StringKey> flags)
|
||||
{
|
||||
Name = name;
|
||||
|
@ -163,20 +163,24 @@ public class FormImpl : IForm
|
|||
for (var i = 0; i < Abilities.Count && i < 255; i++)
|
||||
{
|
||||
if (Abilities[i] == ability.Name)
|
||||
{
|
||||
return new AbilityIndex
|
||||
{
|
||||
IsHidden = false,
|
||||
Index = (byte)i,
|
||||
};
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < HiddenAbilities.Count && i < 255; i++)
|
||||
{
|
||||
if (HiddenAbilities[i] == ability.Name)
|
||||
{
|
||||
return new AbilityIndex
|
||||
{
|
||||
IsHidden = true,
|
||||
Index = (byte)i,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -191,20 +195,11 @@ public class FormImpl : IForm
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringKey GetRandomAbility(IRandom rand)
|
||||
{
|
||||
return Abilities[rand.GetInt(Abilities.Count)];
|
||||
}
|
||||
public StringKey GetRandomAbility(IRandom rand) => Abilities[rand.GetInt(Abilities.Count)];
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringKey GetRandomHiddenAbility(IRandom rand)
|
||||
{
|
||||
return HiddenAbilities[rand.GetInt(HiddenAbilities.Count)];
|
||||
}
|
||||
public StringKey GetRandomHiddenAbility(IRandom rand) => HiddenAbilities[rand.GetInt(HiddenAbilities.Count)];
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasFlag(string key)
|
||||
{
|
||||
return Flags.Contains(key);
|
||||
}
|
||||
public bool HasFlag(string key) => Flags.Contains(key);
|
||||
}
|
|
@ -10,10 +10,12 @@ public enum Gender : byte
|
|||
{
|
||||
/// The Pokémon has no gender.
|
||||
Genderless,
|
||||
|
||||
/// <summary>
|
||||
/// The Pokémon is male.
|
||||
/// </summary>
|
||||
Male,
|
||||
|
||||
/// <summary>
|
||||
/// The Pokémon is female.
|
||||
/// </summary>
|
||||
|
|
|
@ -53,7 +53,6 @@ public class LearnableMovesImpl : ILearnableMoves
|
|||
|
||||
private readonly List<StringKey> _eggMoves = new();
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddLevelMove(LevelInt level, StringKey move)
|
||||
{
|
||||
|
@ -81,10 +80,7 @@ public class LearnableMovesImpl : ILearnableMoves
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<StringKey> GetDistinctLevelMoves()
|
||||
{
|
||||
return _distinctLevelMoves.ToList();
|
||||
}
|
||||
public IReadOnlyList<StringKey> GetDistinctLevelMoves() => _distinctLevelMoves.ToList();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<StringKey> GetLearnableMovesUpToLevel(LevelInt level)
|
||||
|
|
|
@ -131,7 +131,6 @@ public class SpeciesImpl : ISpecies
|
|||
/// <inheritdoc />
|
||||
public ICollection<StringKey> EggGroups { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetForm(StringKey id, [MaybeNullWhen(false)] out IForm form) => Forms.TryGetValue(id, out form);
|
||||
|
||||
|
|
|
@ -9,22 +9,27 @@ public enum Statistic : byte
|
|||
/// Health Points determine how much damage a Pokémon can receive before fainting.
|
||||
/// </summary>
|
||||
Hp,
|
||||
|
||||
/// <summary>
|
||||
/// Attack determines how much damage a Pokémon deals when using a physical attack.
|
||||
/// </summary>
|
||||
Attack,
|
||||
|
||||
/// <summary>
|
||||
/// Defense determines how much damage a Pokémon receives when it is hit by a physical attack.
|
||||
/// </summary>
|
||||
Defense,
|
||||
|
||||
/// <summary>
|
||||
/// Special Attack determines how much damage a Pokémon deals when using a special attack.
|
||||
/// </summary>
|
||||
SpecialAttack,
|
||||
|
||||
/// <summary>
|
||||
/// Special Defense determines how much damage a Pokémon receives when it is hit by a special attack.
|
||||
/// </summary>
|
||||
SpecialDefense,
|
||||
|
||||
/// <summary>
|
||||
/// Speed determines the order that a Pokémon can act in battle.
|
||||
/// </summary>
|
||||
|
|
|
@ -8,8 +8,7 @@ namespace PkmnLib.Static;
|
|||
/// A set of statistics that cannot be changed.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The size of the integer to be used</typeparam>
|
||||
public record ImmutableStatisticSet<T>
|
||||
where T : struct
|
||||
public record ImmutableStatisticSet<T> where T : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// The health points stat value.
|
||||
|
@ -84,8 +83,7 @@ public record ImmutableStatisticSet<T>
|
|||
/// A set of statistics that can be changed.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepCloneable
|
||||
where T : struct
|
||||
public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepCloneable where T : struct
|
||||
{
|
||||
/// <inheritdoc cref="StatisticSet{T}"/>
|
||||
public StatisticSet() : base(default, default, default, default, default, default)
|
||||
|
@ -175,17 +173,13 @@ public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepC
|
|||
return true;
|
||||
}
|
||||
|
||||
protected virtual T GetUnknownStat(Statistic stat)
|
||||
{
|
||||
throw new ArgumentException($"Invalid statistic {stat}");
|
||||
}
|
||||
protected virtual T GetUnknownStat(Statistic stat) => throw new ArgumentException($"Invalid statistic {stat}");
|
||||
|
||||
protected virtual void SetUnknownStat(Statistic stat, T value)
|
||||
{
|
||||
throw new ArgumentException($"Invalid statistic {stat}");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decreases a statistic in the set by a value.
|
||||
/// </summary>
|
||||
|
@ -231,17 +225,13 @@ public record StatisticSet<T> : ImmutableStatisticSet<T>, IEnumerable<T>, IDeepC
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A set of statistics that can be changed, but are clamped to a minimum and maximum value.
|
||||
/// </summary>
|
||||
public abstract record ClampedStatisticSet<T> : StatisticSet<T>
|
||||
where T : struct, IComparable<T>
|
||||
public abstract record ClampedStatisticSet<T> : StatisticSet<T> where T : struct, IComparable<T>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
|
||||
|
@ -322,6 +312,7 @@ public record StatBoostStatisticSet : ClampedStatisticSet<sbyte>
|
|||
protected override sbyte Max => 6;
|
||||
|
||||
private sbyte _evasion;
|
||||
|
||||
public sbyte Evasion
|
||||
{
|
||||
get => _evasion;
|
||||
|
@ -329,6 +320,7 @@ public record StatBoostStatisticSet : ClampedStatisticSet<sbyte>
|
|||
}
|
||||
|
||||
private sbyte _accuracy;
|
||||
|
||||
public sbyte Accuracy
|
||||
{
|
||||
get => _accuracy;
|
||||
|
|
|
@ -25,10 +25,8 @@ public static class DeepCloneHandler
|
|||
/// Recursive references will be handled correctly, and will only be cloned once, to prevent infinite loops and invalid
|
||||
/// references.
|
||||
/// </summary>
|
||||
public static T DeepClone<T>(this T? obj, Dictionary<(Type, int), object>? objects = null) where T : IDeepCloneable
|
||||
{
|
||||
return (T)DeepClone((object?)obj, objects)!;
|
||||
}
|
||||
public static T DeepClone<T>(this T? obj, Dictionary<(Type, int), object>? objects = null)
|
||||
where T : IDeepCloneable => (T)DeepClone((object?)obj, objects)!;
|
||||
|
||||
private static object? DeepClone(this object? obj, Dictionary<(Type, int), object>? objects = null)
|
||||
{
|
||||
|
@ -91,8 +89,9 @@ public static class DeepCloneHandler
|
|||
var array = (Array)obj;
|
||||
var newArray = Array.CreateInstance(type.GetElementType()!, array.Length);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -115,9 +114,10 @@ public static class DeepCloneHandler
|
|||
var dictionary = (IDictionary)obj;
|
||||
var newDictionary = (IDictionary)Activator.CreateInstance(type);
|
||||
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));
|
||||
}
|
||||
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
|
||||
/// each field in the type, so that we can get high performance deep cloning.
|
||||
/// </remarks>
|
||||
private static (Func<object, object?> getter, Action<object, object?> setter)[]
|
||||
GetDeepCloneExpressions(Type type)
|
||||
private static (Func<object, object?> getter, Action<object, object?> setter)[] GetDeepCloneExpressions(Type type)
|
||||
{
|
||||
// We use a lock here to prevent multiple threads from trying to create the expressions at the same time.
|
||||
lock (DeepCloneExpressions)
|
||||
|
|
|
@ -2,7 +2,8 @@ namespace PkmnLib.Static.Utils;
|
|||
|
||||
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))
|
||||
return value;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
namespace PkmnLib.Static.Utils.Errors;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -55,5 +55,4 @@ public static class NumericHelpers
|
|||
var result = (ulong)value * multiplier;
|
||||
return result > uint.MaxValue ? uint.MaxValue : (uint)result;
|
||||
}
|
||||
|
||||
}
|
|
@ -26,10 +26,8 @@ public readonly record struct StringKey
|
|||
/// <summary>
|
||||
/// Converts a <see cref="string"/> to a <see cref="StringKey"/>.
|
||||
/// </summary>
|
||||
public static implicit operator StringKey(string key)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(key) ? default : new StringKey(key);
|
||||
}
|
||||
public static implicit operator StringKey(string key) =>
|
||||
string.IsNullOrWhiteSpace(key) ? default : new StringKey(key);
|
||||
|
||||
/// <inheritdoc cref="string.ToString()"/>
|
||||
public override string ToString() => _key.ToLowerInvariant();
|
||||
|
|
|
@ -10,10 +10,7 @@ public class MoveDataTests
|
|||
public record TestCaseData(IDynamicLibrary Library, IMoveData Move)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Move.Name + " has valid scripts";
|
||||
}
|
||||
public override string ToString() => Move.Name + " has valid scripts";
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> AllMovesHaveValidScriptsData()
|
||||
|
@ -28,8 +25,7 @@ public class MoveDataTests
|
|||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[MethodDataSource(nameof(AllMovesHaveValidScriptsData))]
|
||||
[Test, MethodDataSource(nameof(AllMovesHaveValidScriptsData))]
|
||||
public async Task AllMoveEffectsHaveValidScripts(TestCaseData test)
|
||||
{
|
||||
if (test.Move.SecondaryEffect == null)
|
||||
|
@ -43,7 +39,8 @@ public class MoveDataTests
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
global using TUnit;
|
||||
global using FluentAssertions;
|
||||
|
||||
global using LevelInt = byte;
|
|
@ -26,8 +26,7 @@ public class IntegrationTestRunner
|
|||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[MethodDataSource(nameof(TestCases))]
|
||||
[Test, MethodDataSource(nameof(TestCases))]
|
||||
public async Task RunIntegrationTest(IntegrationTestModel test)
|
||||
{
|
||||
var library = LibraryHelpers.LoadLibrary();
|
||||
|
@ -40,11 +39,10 @@ public class IntegrationTestRunner
|
|||
var pokemon = x.Pokemon[index];
|
||||
await Assert.That(library.StaticLibrary.Species.TryGet(pokemon.Species, out var species)).IsTrue();
|
||||
var mon = new PokemonImpl(library, species!, species!.GetDefaultForm(), new AbilityIndex
|
||||
{
|
||||
IsHidden = false,
|
||||
Index = 0,
|
||||
},
|
||||
pokemon.Level, 0, Gender.Genderless, 0, "hardy");
|
||||
{
|
||||
IsHidden = false,
|
||||
Index = 0,
|
||||
}, pokemon.Level, 0, Gender.Genderless, 0, "hardy");
|
||||
foreach (var move in pokemon.Moves)
|
||||
{
|
||||
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());
|
||||
}).ProcessOneAtATime().GetResultsAsync();
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -7,11 +7,8 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
|
|||
|
||||
namespace PkmnLib.Tests.Integration.Models;
|
||||
|
||||
|
||||
[JsonDerivedType(typeof(SetPokemonAction), "setPokemon")]
|
||||
[JsonDerivedType(typeof(SetMoveChoiceAction), "setMoveChoice")]
|
||||
[JsonDerivedType(typeof(SetPassChoiceAction), "setPassChoice")]
|
||||
[JsonDerivedType(typeof(AssertAction), "assert")]
|
||||
[JsonDerivedType(typeof(SetPokemonAction), "setPokemon"), JsonDerivedType(typeof(SetMoveChoiceAction), "setMoveChoice"),
|
||||
JsonDerivedType(typeof(SetPassChoiceAction), "setPassChoice"), JsonDerivedType(typeof(AssertAction), "assert")]
|
||||
public abstract class IntegrationTestAction
|
||||
{
|
||||
public abstract Task Execute(IBattle battle);
|
||||
|
@ -36,7 +33,6 @@ public class SetMoveChoiceAction : IntegrationTestAction
|
|||
public string Move { get; set; } = null!;
|
||||
public List<byte> Target { get; set; } = null!;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Execute(IBattle battle)
|
||||
{
|
||||
|
|
|
@ -8,10 +8,7 @@ public class IntegrationTestModel
|
|||
public IntegrationTestAction[] Actions { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
public override string ToString() => Name;
|
||||
}
|
||||
|
||||
public class IntegrationTestBattleSetup
|
||||
|
|
|
@ -11,8 +11,7 @@ namespace PkmnLib.Tests.Static;
|
|||
|
||||
public class DeepCloneTests
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
[SuppressMessage("ReSharper", "ValueParameterNotUsed")]
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "ValueParameterNotUsed")]
|
||||
private class TestClass : IDeepCloneable
|
||||
{
|
||||
public int Value { get; set; }
|
||||
|
@ -70,8 +69,7 @@ public class DeepCloneTests
|
|||
var clone = obj.DeepClone();
|
||||
await Assert.That(clone).IsNotEqualTo(obj);
|
||||
var clonePrivateField =
|
||||
clone.GetType().GetField("_privateField", BindingFlags.NonPublic | BindingFlags.Instance)!
|
||||
.GetValue(clone);
|
||||
clone.GetType().GetField("_privateField", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(clone);
|
||||
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("charmander", out var charmander)).IsTrue();
|
||||
var party1 = new PokemonParty(6);
|
||||
party1.SwapInto(new PokemonImpl(library, bulbasaur!,
|
||||
bulbasaur!.GetDefaultForm(), new AbilityIndex
|
||||
{
|
||||
IsHidden = false,
|
||||
Index = 0,
|
||||
}, 50, 0,
|
||||
Gender.Male, 0, "hardy"), 0);
|
||||
party1.SwapInto(new PokemonImpl(library, bulbasaur!, bulbasaur!.GetDefaultForm(), new AbilityIndex
|
||||
{
|
||||
IsHidden = false,
|
||||
Index = 0,
|
||||
}, 50, 0, Gender.Male, 0, "hardy"), 0);
|
||||
var party2 = new PokemonParty(6);
|
||||
party2.SwapInto(new PokemonImpl(library, charmander!,
|
||||
charmander!.GetDefaultForm(), new AbilityIndex
|
||||
{
|
||||
IsHidden = false,
|
||||
Index = 0,
|
||||
}, 50, 0,
|
||||
Gender.Male, 0, "hardy"), 0);
|
||||
party2.SwapInto(new PokemonImpl(library, charmander!, charmander!.GetDefaultForm(), new AbilityIndex
|
||||
{
|
||||
IsHidden = false,
|
||||
Index = 0,
|
||||
}, 50, 0, Gender.Male, 0, "hardy"), 0);
|
||||
|
||||
var parties = new[]
|
||||
{
|
||||
new BattlePartyImpl(party1, [new ResponsibleIndex(0, 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[1].SwapPokemon(0, party2[0]);
|
||||
party1[0]!.ChangeStatBoost(Statistic.Defense, 2, true);
|
||||
|
|
|
@ -21,8 +21,10 @@ public class GrowthRateTests
|
|||
[Test]
|
||||
public void TooLargeLookupGrowthRateTestShouldThrowArgumentException()
|
||||
{
|
||||
Action act = () => _ = 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.");
|
||||
Action act = () =>
|
||||
_ = 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]
|
||||
|
|
|
@ -15,8 +15,7 @@ public class StringKeyTests
|
|||
yield return () => ("TeSt", "tesv", false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
||||
[Test, MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
||||
public async Task StringKeyEqualityTest(string k1, string k2, bool expected)
|
||||
{
|
||||
var sk1 = new StringKey(k1);
|
||||
|
@ -24,8 +23,7 @@ public class StringKeyTests
|
|||
await Assert.That(sk1 == sk2).IsEqualTo(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
||||
[Test, MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
||||
public async Task HashCodeEqualityTest(string k1, string k2, bool expected)
|
||||
{
|
||||
var sk1 = new StringKey(k1);
|
||||
|
@ -33,8 +31,7 @@ public class StringKeyTests
|
|||
await Assert.That(sk1.GetHashCode() == sk2.GetHashCode()).IsEqualTo(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
||||
[Test, MethodDataSource(nameof(StringKeyEqualityTestCases))]
|
||||
public async Task HashSetEqualityTest(string k1, string k2, bool expected)
|
||||
{
|
||||
var sk1 = new StringKey(k1);
|
||||
|
|
|
@ -27,15 +27,13 @@ public class DamageCalculatorTests
|
|||
// Imagine a level 75 Glaceon
|
||||
attacker.Setup(x => x.Level).Returns(75);
|
||||
// with an effective Attack stat of 123
|
||||
attacker.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(
|
||||
1, 123, 1, 1, 1, 1));
|
||||
attacker.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(1, 123, 1, 1, 1, 1));
|
||||
// We use 10 as the Ice type
|
||||
attacker.Setup(x => x.Types).Returns([new TypeIdentifier(10)]);
|
||||
|
||||
var defender = new Mock<IPokemon>();
|
||||
// a Garchomp with an effective Defense stat of 163
|
||||
defender.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(
|
||||
1, 1, 163, 1, 1, 1));
|
||||
defender.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(1, 1, 163, 1, 1, 1));
|
||||
defender.Setup(x => x.GetScripts()).Returns(new ScriptIterator([]));
|
||||
|
||||
var useMove = new Mock<IMoveData>();
|
||||
|
|
|
@ -59,5 +59,4 @@ public class AcrobaticsTests
|
|||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo(byte.MaxValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -44,15 +44,18 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||
var flatStat = CalculateFlatStat(pokemon, stat);
|
||||
var boostModifier = GetStatBoostModifier(pokemon, stat);
|
||||
var boostedStat = flatStat * boostModifier;
|
||||
if (boostedStat > uint.MaxValue) boostedStat = uint.MaxValue;
|
||||
if (boostedStat > uint.MaxValue)
|
||||
boostedStat = uint.MaxValue;
|
||||
return (uint)boostedStat;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte CalculateModifiedAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, byte moveAccuracy)
|
||||
public byte CalculateModifiedAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex,
|
||||
byte moveAccuracy)
|
||||
{
|
||||
var accuracyModifier = 1.0f;
|
||||
executingMove.RunScriptHook(x => x.ChangeAccuracyModifier(executingMove, target, hitIndex, ref accuracyModifier));
|
||||
executingMove.RunScriptHook(
|
||||
x => x.ChangeAccuracyModifier(executingMove, target, hitIndex, ref accuracyModifier));
|
||||
var modifiedAccuracy = (int)(moveAccuracy * accuracyModifier);
|
||||
executingMove.RunScriptHook(x => x.ChangeAccuracy(executingMove, target, hitIndex, ref modifiedAccuracy));
|
||||
var targetEvasion = target.StatBoost.Evasion;
|
||||
|
@ -66,14 +69,14 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||
{
|
||||
> 0 => 3.0f / (3.0f + Math.Min(difference, 6)),
|
||||
< 0 => 3.0f + -Math.Max(difference, -6) / 3.0f,
|
||||
_ => 1.0f
|
||||
_ => 1.0f,
|
||||
};
|
||||
modifiedAccuracy = (int)(modifiedAccuracy * statModifier);
|
||||
modifiedAccuracy = modifiedAccuracy switch
|
||||
{
|
||||
> 255 => 255,
|
||||
< 0 => 0,
|
||||
_ => modifiedAccuracy
|
||||
_ => modifiedAccuracy,
|
||||
};
|
||||
// NOTE: the main games also consider friendship here, but we don't yet have the concept of a "player Pokémon"
|
||||
// in the battle system, so for now we're just ignoring that.
|
||||
|
@ -86,8 +89,9 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||
var iv = (ulong)pokemon.IndividualValues.Hp;
|
||||
var ev = (ulong)pokemon.EffortValues.Hp;
|
||||
var level = (ulong)pokemon.Level;
|
||||
var health = (((2 * baseValue + iv + (ev / 4)) * level) / 100) + level + 10;
|
||||
if (health > uint.MaxValue) health = uint.MaxValue;
|
||||
var health = (2 * baseValue + iv + ev / 4) * level / 100 + level + 10;
|
||||
if (health > uint.MaxValue)
|
||||
health = uint.MaxValue;
|
||||
return (uint)health;
|
||||
}
|
||||
|
||||
|
@ -97,10 +101,11 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||
var iv = (ulong)pokemon.IndividualValues.GetStatistic(statistic);
|
||||
var ev = (ulong)pokemon.EffortValues.GetStatistic(statistic);
|
||||
var level = (ulong)pokemon.Level;
|
||||
var unmodified = (((2 * baseValue + iv + (ev / 4)) * level) / 100) + 5;
|
||||
var unmodified = (2 * baseValue + iv + ev / 4) * level / 100 + 5;
|
||||
var natureModifier = pokemon.Nature.GetStatModifier(statistic);
|
||||
var modified = (unmodified * natureModifier);
|
||||
if (modified > uint.MaxValue) modified = uint.MaxValue;
|
||||
var modified = unmodified * natureModifier;
|
||||
if (modified > uint.MaxValue)
|
||||
modified = uint.MaxValue;
|
||||
return (uint)modified;
|
||||
}
|
||||
|
||||
|
@ -122,7 +127,7 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||
4 => 6.0f / 2.0f,
|
||||
5 => 7.0f / 2.0f,
|
||||
6 => 8.0f / 2.0f,
|
||||
_ => throw new System.ArgumentException("Stat boost was out of expected range of -6 to 6"),
|
||||
_ => throw new ArgumentException("Stat boost was out of expected range of -6 to 6"),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -23,11 +23,10 @@ public class Gen7CaptureLibrary : ICaptureLibrary
|
|||
byte bonusStatus = 1;
|
||||
target.RunScriptHook(x => x.ChangeCatchRateBonus(target, captureItem, ref bonusStatus));
|
||||
|
||||
var modifiedCatchRate =
|
||||
(((3.0 * maxHealth) - (2.0 * currentHealth)) * catchRate * bonusBall) / (3.0 * maxHealth);
|
||||
var modifiedCatchRate = (3.0 * maxHealth - 2.0 * currentHealth) * catchRate * bonusBall / (3.0 * maxHealth);
|
||||
modifiedCatchRate *= bonusStatus;
|
||||
|
||||
var shakeProbability = 65536 / Math.Pow((255 / modifiedCatchRate), 0.1875);
|
||||
var shakeProbability = 65536 / Math.Pow(255 / modifiedCatchRate, 0.1875);
|
||||
byte shakes = 0;
|
||||
if (modifiedCatchRate >= 255)
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
|||
if (hitData.Effectiveness == 0)
|
||||
return 0;
|
||||
|
||||
var levelModifier = (2.0f * executingMove.User.Level) / 5.0f + 2.0f;
|
||||
var levelModifier = 2.0f * executingMove.User.Level / 5.0f + 2.0f;
|
||||
var basePower = (float)hitData.BasePower;
|
||||
var statModifier = GetStatModifier(executingMove, target, hitNumber, hitData);
|
||||
var damageModifier = GetDamageModifier(executingMove, target, hitNumber);
|
||||
|
@ -62,10 +62,8 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
|||
< 1 => 1,
|
||||
_ => (uint)floatDamage,
|
||||
};
|
||||
executingMove.RunScriptHook(script =>
|
||||
script.ChangeMoveDamage(executingMove, target, hitNumber, ref damage));
|
||||
target.RunScriptHook(script =>
|
||||
script.ChangeIncomingMoveDamage(executingMove, target, hitNumber, ref damage));
|
||||
executingMove.RunScriptHook(script => script.ChangeMoveDamage(executingMove, target, hitNumber, ref damage));
|
||||
target.RunScriptHook(script => script.ChangeIncomingMoveDamage(executingMove, target, hitNumber, ref damage));
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
@ -76,8 +74,7 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
|||
if (executingMove.UseMove.Category == MoveCategory.Status)
|
||||
return 0;
|
||||
var basePower = executingMove.UseMove.BasePower;
|
||||
executingMove.RunScriptHook(script =>
|
||||
script.ChangeBasePower(executingMove, target, hitNumber, ref basePower));
|
||||
executingMove.RunScriptHook(script => script.ChangeBasePower(executingMove, target, hitNumber, ref basePower));
|
||||
return basePower;
|
||||
}
|
||||
|
||||
|
@ -100,7 +97,8 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
|||
};
|
||||
}
|
||||
|
||||
private static float GetStatModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData)
|
||||
private static float GetStatModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber,
|
||||
IHitData hitData)
|
||||
{
|
||||
var category = executingMove.UseMove.Category;
|
||||
if (category == MoveCategory.Status)
|
||||
|
|
|
@ -11,14 +11,12 @@ namespace PkmnLib.Plugin.Gen7.Libraries;
|
|||
public class Gen7MiscLibrary : IMiscLibrary
|
||||
{
|
||||
private readonly IMoveData _struggleData = new MoveDataImpl("struggle", new TypeIdentifier(0),
|
||||
MoveCategory.Physical, 50,
|
||||
255, 255, MoveTarget.Any, 0,
|
||||
MoveCategory.Physical, 50, 255, 255, MoveTarget.Any, 0,
|
||||
new SecondaryEffectImpl(-1, "struggle", new Dictionary<StringKey, object?>()), []);
|
||||
|
||||
/// <inheritdoc />
|
||||
public ITurnChoice ReplacementChoice(IPokemon user, byte targetSide, byte targetPosition) =>
|
||||
new MoveChoice(user, new LearnedMoveImpl(_struggleData, MoveLearnMethod.Unknown), targetSide,
|
||||
targetPosition);
|
||||
new MoveChoice(user, new LearnedMoveImpl(_struggleData, MoveLearnMethod.Unknown), targetSide, targetPosition);
|
||||
|
||||
/// <inheritdoc />
|
||||
public TimeOfDay GetTimeOfDay()
|
||||
|
@ -57,7 +55,7 @@ public class Gen7MiscLibrary : IMiscLibrary
|
|||
var userSide = battle.Sides[battleData.SideIndex];
|
||||
|
||||
userSide.RegisterFleeAttempt();
|
||||
var fleeChance = ((userSpeed * 32) / (opponentSpeed / 4) + (30 * userSide.FleeAttempts)) / 256;
|
||||
var fleeChance = (userSpeed * 32 / (opponentSpeed / 4) + 30 * userSide.FleeAttempts) / 256;
|
||||
var random = battle.Random.GetInt(0, 100);
|
||||
return random < fleeChance;
|
||||
}
|
||||
|
|
|
@ -30,8 +30,5 @@ public class StaticPokeball : PokeballScript
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override byte GetCatchRate(IPokemon target)
|
||||
{
|
||||
return _catchRate;
|
||||
}
|
||||
public override byte GetCatchRate(IPokemon target) => _catchRate;
|
||||
}
|
|
@ -3,7 +3,8 @@ using PkmnLib.Static;
|
|||
namespace PkmnLib.Plugin.Gen7.Scripts.MoveVolatile;
|
||||
|
||||
[Script(ScriptCategory.MoveVolatile, "electrify")]
|
||||
public class ElectrifyEffect : Script {
|
||||
public class ElectrifyEffect : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeMoveType(IExecutingMove move, IPokemon target, byte hit, ref TypeIdentifier moveType)
|
||||
{
|
||||
|
|
|
@ -45,7 +45,7 @@ public class AuroraVeil : Script
|
|||
var numberOfTurns = 5;
|
||||
var dict = new Dictionary<StringKey, object?>()
|
||||
{
|
||||
{ "duration", numberOfTurns }
|
||||
{ "duration", numberOfTurns },
|
||||
};
|
||||
move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.AuroraVeilDuration, dict));
|
||||
numberOfTurns = (int)dict.GetOrDefault("duration", numberOfTurns)!;
|
||||
|
|
|
@ -24,13 +24,12 @@ public class Autotomize : Script
|
|||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var user = move.User;
|
||||
if (user.ChangeStatBoost(Statistic.Speed, 2, true) &&
|
||||
user.ChangeWeightInKgBy(-100.0f))
|
||||
if (user.ChangeStatBoost(Statistic.Speed, 2, true) && user.ChangeWeightInKgBy(-100.0f))
|
||||
{
|
||||
var battle = user.BattleData?.Battle;
|
||||
battle?.EventHook.Invoke(new DialogEvent("pokemon_became_nimble", new Dictionary<string, object>()
|
||||
{
|
||||
{ "pokemon", user }
|
||||
{ "pokemon", user },
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,5 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|||
public class BanefulBunker : ProtectionScript
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override Script GetEffectScript()
|
||||
{
|
||||
return new BanefulBunkerEffect();
|
||||
}
|
||||
protected override Script GetEffectScript() => new BanefulBunkerEffect();
|
||||
}
|
|
@ -15,7 +15,7 @@ public class BeakBlast : Script
|
|||
choice.User.Volatile.Add(new BeakBlastEffect());
|
||||
battleData.Battle.EventHook.Invoke(new DialogEvent("beak_blast_charge", new Dictionary<string, object>()
|
||||
{
|
||||
{ "user", choice.User }
|
||||
{ "user", choice.User },
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|||
public class BeatUp : Script
|
||||
{
|
||||
private IPokemon[]? _relevantPartyMembers;
|
||||
|
||||
private static IEnumerable<IPokemon> GetRelevantPartyMembers(IPokemon user)
|
||||
{
|
||||
var battleData = user.BattleData;
|
||||
|
|
|
@ -15,7 +15,7 @@ public class Bounce : Script
|
|||
move.User.Volatile.Add(new ChargeBounceEffect(move.User));
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("bounce_charge", new Dictionary<string, object>()
|
||||
{
|
||||
{ "user", move.User }
|
||||
{ "user", move.User },
|
||||
}));
|
||||
prevent = true;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ public class ChangeMultipleUserStatBoosts : Script
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,6 @@ public class ChipAway : Script
|
|||
bypass = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void BypassEvasionStatBoosts(IExecutingMove move, IPokemon target, byte hitIndex, ref bool bypass)
|
||||
=> bypass = true;
|
||||
public override void
|
||||
BypassEvasionStatBoosts(IExecutingMove move, IPokemon target, byte hitIndex, ref bool bypass) => bypass = true;
|
||||
}
|
|
@ -27,8 +27,7 @@ public class Conversion2 : Script
|
|||
if (indexOfNext == -1)
|
||||
return x;
|
||||
return x.Take(indexOfNext);
|
||||
})
|
||||
.SelectMany(x => x)
|
||||
}).SelectMany(x => x)
|
||||
// We only want the last move choice by the target
|
||||
.OfType<IMoveChoice>().FirstOrDefault(x => x.User == target);
|
||||
if (lastMoveByTarget == null)
|
||||
|
@ -42,11 +41,9 @@ public class Conversion2 : Script
|
|||
var type = typeLibrary.GetAllEffectivenessFromAttacking(lastMoveByTarget.ChosenMove.MoveData.MoveType)
|
||||
.Where(x => x.effectiveness < 1)
|
||||
// Shuffle them randomly, but deterministically
|
||||
.OrderBy(_ => move.User.BattleData.Battle.Random.GetInt())
|
||||
.ThenBy(x => x.type.Value)
|
||||
.OrderBy(_ => move.User.BattleData.Battle.Random.GetInt()).ThenBy(x => x.type.Value)
|
||||
// And grab the first one
|
||||
.Select(x => x.type)
|
||||
.FirstOrDefault();
|
||||
.Select(x => x.type).FirstOrDefault();
|
||||
if (type == null)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
|
|
|
@ -10,9 +10,7 @@ public class Copycat : Script
|
|||
/// <inheritdoc />
|
||||
public override void ChangeMove(IMoveChoice choice, ref StringKey moveName)
|
||||
{
|
||||
var lastMove = choice.User.BattleData?.Battle.PreviousTurnChoices
|
||||
.SelectMany(x => x)
|
||||
.OfType<IMoveChoice>()
|
||||
var lastMove = choice.User.BattleData?.Battle.PreviousTurnChoices.SelectMany(x => x).OfType<IMoveChoice>()
|
||||
.LastOrDefault();
|
||||
if (lastMove == null || !lastMove.ChosenMove.MoveData.CanCopyMove())
|
||||
{
|
||||
|
|
|
@ -15,7 +15,7 @@ public class Dig : Script
|
|||
move.User.Volatile.Add(new DigEffect(move.User));
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dig_charge", new Dictionary<string, object>()
|
||||
{
|
||||
{ "user", move.User }
|
||||
{ "user", move.User },
|
||||
}));
|
||||
prevent = true;
|
||||
}
|
||||
|
|
|
@ -13,10 +13,7 @@ public class Disable : Script
|
|||
if (battleData == null)
|
||||
return;
|
||||
var choiceQueue = battleData.Battle.PreviousTurnChoices;
|
||||
var lastMove = choiceQueue
|
||||
.SelectMany(x => x)
|
||||
.OfType<IMoveChoice>()
|
||||
.LastOrDefault(x => x.User == target);
|
||||
var lastMove = choiceQueue.SelectMany(x => x).OfType<IMoveChoice>().LastOrDefault(x => x.User == target);
|
||||
if (lastMove == null)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
|
|
|
@ -15,7 +15,7 @@ public class Dive : Script
|
|||
move.User.Volatile.Add(new DigEffect(move.User));
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dive_charge", new Dictionary<string, object>()
|
||||
{
|
||||
{ "user", move.User }
|
||||
{ "user", move.User },
|
||||
}));
|
||||
prevent = true;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ public class Electrify : Script
|
|||
if (choiceQueue == null)
|
||||
return;
|
||||
|
||||
if (choiceQueue.FirstOrDefault(x => x is IMoveChoice moveChoice && moveChoice.User == target) is not IMoveChoice choice)
|
||||
if (choiceQueue.FirstOrDefault(x => x is IMoveChoice moveChoice && moveChoice.User == target) is not IMoveChoice
|
||||
choice)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
|
|
|
@ -10,14 +10,14 @@ public class ElectroBall : Script
|
|||
var targetSpeed = target.BoostedStats.Speed;
|
||||
var userSpeed = user.BoostedStats.Speed;
|
||||
|
||||
var ratio = (float) userSpeed / targetSpeed;
|
||||
var ratio = (float)userSpeed / targetSpeed;
|
||||
basePower = ratio switch
|
||||
{
|
||||
> 4 => 150,
|
||||
> 3 => 120,
|
||||
> 2 => 80,
|
||||
> 1 => 60,
|
||||
_ => 40
|
||||
_ => 40,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -14,11 +14,8 @@ public class Encore : Script
|
|||
return;
|
||||
|
||||
var currentTurn = battle.ChoiceQueue!.LastRanChoice;
|
||||
var lastMove = battle.PreviousTurnChoices
|
||||
.SelectMany(x => x)
|
||||
.OfType<IMoveChoice>()
|
||||
.TakeWhile(x => x != currentTurn)
|
||||
.LastOrDefault(x => x.User == target);
|
||||
var lastMove = battle.PreviousTurnChoices.SelectMany(x => x).OfType<IMoveChoice>()
|
||||
.TakeWhile(x => x != currentTurn).LastOrDefault(x => x.User == target);
|
||||
if (lastMove == null)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
|
|
|
@ -9,5 +9,4 @@ public class Eruption : Script
|
|||
{
|
||||
basePower = Math.Max((byte)(150 * move.User.CurrentHealth / move.User.BoostedStats.Hp), (byte)1);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,7 @@ public class Flail : Script
|
|||
< 10 => 100,
|
||||
< 17 => 80,
|
||||
< 33 => 40,
|
||||
_ => 20
|
||||
_ => 20,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -15,7 +15,6 @@ public class FlareBlitz : Script
|
|||
target.SetStatus("burned");
|
||||
}
|
||||
|
||||
|
||||
var hitData = move.GetHitData(target, hit);
|
||||
var recoilDamage = (uint)(hitData.Damage * (1 / 3));
|
||||
move.User.Damage(recoilDamage, DamageSource.Misc);
|
||||
|
|
|
@ -15,7 +15,7 @@ public class Fly : Script
|
|||
move.User.Volatile.Add(new ChargeFlyEffect(move.User));
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("fly_charge", new Dictionary<string, object>()
|
||||
{
|
||||
{ "user", move.User }
|
||||
{ "user", move.User },
|
||||
}));
|
||||
prevent = true;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ public class FocusPunch : Script
|
|||
choice.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("focus_punch_charge",
|
||||
new Dictionary<string, object>()
|
||||
{
|
||||
{ "pokemon", choice.User }
|
||||
{ "pokemon", choice.User },
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,8 @@ public class FreezeDry : Script
|
|||
|
||||
if (target.Types.Contains(waterType))
|
||||
{
|
||||
var effectivenessWithoutWater = target.Types
|
||||
.Where(x => x != waterType)
|
||||
.Select(x => typeLibrary.GetEffectiveness(x, target.Types))
|
||||
.Aggregate(1f, (a, b) => a * b);
|
||||
var effectivenessWithoutWater = target.Types.Where(x => x != waterType)
|
||||
.Select(x => typeLibrary.GetEffectiveness(x, target.Types)).Aggregate(1f, (a, b) => a * b);
|
||||
effectiveness = effectivenessWithoutWater * 2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ public class MultiHitMove : Script
|
|||
< 35 => 2,
|
||||
< 70 => 3,
|
||||
< 85 => 4,
|
||||
_ => 5
|
||||
_ => 5,
|
||||
};
|
||||
numberOfHits = (byte)newHits;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|||
public class OneHitKo : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref int modifiedAccuracy)
|
||||
public override void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex,
|
||||
ref int modifiedAccuracy)
|
||||
{
|
||||
var levelDifference = executingMove.User.Level - target.Level;
|
||||
if (levelDifference < 0)
|
||||
|
|
|
@ -25,7 +25,8 @@ public class Struggle : Script
|
|||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var damage = move.User.MaxHealth / 4;
|
||||
if (damage == 0) damage = 1;
|
||||
move.User.Damage(damage, DamageSource.Struggle, new());
|
||||
if (damage == 0)
|
||||
damage = 1;
|
||||
move.User.Damage(damage, DamageSource.Struggle, new EventBatchId());
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ public class BindEffect : Script
|
|||
private int _turns;
|
||||
private readonly float _percentOfMaxHealth;
|
||||
|
||||
|
||||
public BindEffect(IPokemon owner, int turns, float percentOfMaxHealth)
|
||||
{
|
||||
_owner = owner;
|
||||
|
|
|
@ -11,6 +11,7 @@ public class DigEffect : Script
|
|||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ public class DiveEffect : Script
|
|||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
|
||||
{
|
||||
|
|
|
@ -24,9 +24,8 @@ public class EncoreEffect : Script
|
|||
choice = TurnChoiceHelper.CreateMoveChoice(_owner, _move, opposingSideIndex, position);
|
||||
if (choice is IMoveChoice { ChosenMove.CurrentPp: <= 0 } moveChoice)
|
||||
{
|
||||
choice =
|
||||
moveChoice.User.BattleData?.Battle.Library.MiscLibrary.ReplacementChoice(_owner, opposingSideIndex,
|
||||
position);
|
||||
choice = moveChoice.User.BattleData?.Battle.Library.MiscLibrary.ReplacementChoice(_owner, opposingSideIndex,
|
||||
position);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue