using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Static; using PkmnLib.Static.Moves; using PkmnLib.Static.Utils; using PkmnLib.Static.Utils.Errors; namespace PkmnLib.Dynamic.Models; /// <summary> /// Data for a single hit on a target. /// </summary> public interface IHitData { /// <summary> /// Whether the hit is critical. /// </summary> bool IsCritical { get; } /// <summary> /// The base power of the hit. /// </summary> byte BasePower { get; } /// <summary> /// The effectiveness of the hit. /// </summary> float Effectiveness { get; } /// <summary> /// The damage done by the hit. /// </summary> uint Damage { get; } /// <summary> /// The type of the hit. /// </summary> TypeIdentifier Type { get; } /// <summary> /// Whether the hit has failed. /// </summary> bool HasFailed { get; } /// <summary> /// Fails the hit. /// </summary> void Fail(); } /// <inheritdoc /> public record HitData : IHitData { /// <inheritdoc /> public bool IsCritical { get; internal set; } /// <inheritdoc /> public byte BasePower { get; internal set; } /// <inheritdoc /> public float Effectiveness { get; internal set; } /// <inheritdoc /> public uint Damage { get; internal set; } /// <inheritdoc /> public TypeIdentifier Type { get; internal set; } /// <inheritdoc /> public bool HasFailed { get; private set; } /// <inheritdoc /> public void Fail() => HasFailed = true; } /// <summary> /// An executing move is the data of the move for while it is executing. /// </summary> public interface IExecutingMove : IScriptSource { /// <summary> /// The number of targets this move has. /// </summary> int TargetCount { get; } /// <summary> /// The number of hits this move has per target. /// </summary> byte NumberOfHits { get; } /// <summary> /// The user of the move. /// </summary> IPokemon User { get; } /// <summary> /// The move the user has actually chosen to do. /// </summary> ILearnedMove ChosenMove { get; } /// <summary> /// The move that the user is actually going to do. This can be different from the chosen move, for example /// when metronome is used, in which case the chosen move will be metronome, and the movedata will be the /// move that metronome has chosen. /// </summary> IMoveData UseMove { get; } /// <summary> /// The script of the move. /// </summary> ScriptContainer Script { get; } /// <summary> /// Gets a hit data for a target, with a specific index. /// </summary> IHitData GetHitData(IPokemon target, byte hit); /// <summary> /// Checks whether a Pokémon is a target for this move. /// </summary> bool IsPokemonTarget(IPokemon target); /// <summary> /// Gets the index of the hits in this move where the hits for a specific target start. /// </summary> int GetTargetIndex(IPokemon target); /// <summary> /// Gets a hit based on its raw index. /// </summary> IHitData GetDataFromRawIndex(int index); /// <summary> /// Gets the targets of this move. /// </summary> IReadOnlyList<IPokemon?> Targets { get; } } /// <inheritdoc cref="IExecutingMove"/> public class ExecutingMoveImpl : ScriptSource, IExecutingMove { private readonly IReadOnlyList<IPokemon?> _targets; private readonly IHitData[] _hits; /// <inheritdoc cref="ExecutingMoveImpl"/> public ExecutingMoveImpl(IReadOnlyList<IPokemon?> targets, byte numberOfHits, IPokemon user, ILearnedMove chosenMove, IMoveData useMove, ScriptContainer script) { _targets = targets; NumberOfHits = numberOfHits; User = user; ChosenMove = chosenMove; UseMove = useMove; Script = script; var totalHits = targets.Count * numberOfHits; _hits = new IHitData[totalHits]; for (var i = 0; i < totalHits; i++) { _hits[i] = new HitData(); } } /// <inheritdoc /> public int TargetCount => _targets.Count; /// <inheritdoc /> public byte NumberOfHits { get; } /// <inheritdoc /> public IPokemon User { get; } /// <inheritdoc /> public ILearnedMove ChosenMove { get; } /// <inheritdoc /> public IMoveData UseMove { get; } /// <inheritdoc /> public ScriptContainer Script { get; } /// <inheritdoc /> public IHitData GetHitData(IPokemon target, byte hit) { var targetIndex = _targets.IndexOf(target); if (targetIndex == -1) { throw new ArgumentException("The target is not a target of this move."); } var index = targetIndex * NumberOfHits + hit; return _hits[index]; } /// <inheritdoc /> public bool IsPokemonTarget(IPokemon target) => _targets.Contains(target); /// <inheritdoc /> public int GetTargetIndex(IPokemon target) { var targetIndex = _targets.IndexOf(target); if (targetIndex == -1) throw new ArgumentException("The target is not a target of this move."); return targetIndex * NumberOfHits; } /// <inheritdoc /> public IHitData GetDataFromRawIndex(int index) { if (index < 0 || index >= _hits.Length) throw new OutOfRangeException("Hit", index, _hits.Length - 1); return _hits[index]; } /// <inheritdoc /> public IReadOnlyList<IPokemon?> Targets => _targets.ToList(); /// <inheritdoc /> public override int ScriptCount => 1 + User.ScriptCount; /// <inheritdoc /> public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts) { scripts.Add(Script); } /// <inheritdoc /> public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts) { scripts.Add(Script); User.CollectScripts(scripts); } }