287 lines
7.3 KiB
C#
287 lines
7.3 KiB
C#
using PkmnLib.Dynamic.Models.Choices;
|
|
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>
|
|
ushort 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. Null if the move is typeless.
|
|
/// </summary>
|
|
TypeIdentifier? Type { get; }
|
|
|
|
/// <summary>
|
|
/// Whether the hit has failed.
|
|
/// </summary>
|
|
bool HasFailed { get; }
|
|
|
|
/// <summary>
|
|
/// Fails the hit.
|
|
/// </summary>
|
|
void Fail();
|
|
|
|
/// <summary>
|
|
/// Sets a flag on the hit data. This is used to mark certain conditions or states
|
|
/// </summary>
|
|
void SetFlag(StringKey flag);
|
|
|
|
/// <summary>
|
|
/// Checks whether a flag is set on the hit data.
|
|
/// </summary>
|
|
bool HasFlag(StringKey flag);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public record HitData : IHitData
|
|
{
|
|
/// <inheritdoc />
|
|
public bool IsCritical { get; internal set; }
|
|
|
|
/// <inheritdoc />
|
|
public ushort 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;
|
|
|
|
private HashSet<StringKey>? _flags;
|
|
|
|
/// <inheritdoc />
|
|
public void SetFlag(StringKey flag)
|
|
{
|
|
_flags ??= [];
|
|
_flags.Add(flag);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool HasFlag(StringKey flag) => _flags != null && _flags.Contains(flag);
|
|
}
|
|
|
|
/// <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; }
|
|
|
|
/// <summary>
|
|
/// The underlying move choice.
|
|
/// </summary>
|
|
IMoveChoice MoveChoice { get; }
|
|
|
|
/// <summary>
|
|
/// Returns all the hits of this move.
|
|
/// </summary>
|
|
IReadOnlyList<IHitData> Hits { get; }
|
|
|
|
/// <summary>
|
|
/// The battle this move is being executed in.
|
|
/// </summary>
|
|
IBattle Battle { get; }
|
|
}
|
|
|
|
/// <inheritdoc cref="IExecutingMove"/>
|
|
public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
|
{
|
|
private readonly IReadOnlyList<IPokemon?> _targets;
|
|
private readonly IHitData[] _hits;
|
|
private readonly IBattle _battle;
|
|
|
|
/// <inheritdoc cref="ExecutingMoveImpl"/>
|
|
public ExecutingMoveImpl(IReadOnlyList<IPokemon?> targets, byte numberOfHits, ILearnedMove chosenMove,
|
|
IMoveData useMove, IMoveChoice moveChoice, IBattle battle)
|
|
{
|
|
_targets = targets;
|
|
NumberOfHits = numberOfHits;
|
|
ChosenMove = chosenMove;
|
|
UseMove = useMove;
|
|
MoveChoice = moveChoice;
|
|
_battle = battle;
|
|
|
|
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 => MoveChoice.User;
|
|
|
|
/// <inheritdoc />
|
|
public ILearnedMove ChosenMove { get; }
|
|
|
|
/// <inheritdoc />
|
|
public IMoveData UseMove { get; }
|
|
|
|
/// <inheritdoc />
|
|
public ScriptContainer Script => MoveChoice.Script;
|
|
|
|
/// <summary>
|
|
/// The volatile scripts that are applicable to this move.
|
|
/// </summary>
|
|
public IScriptSet Volatile => MoveChoice.Volatile;
|
|
|
|
/// <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 IMoveChoice MoveChoice { get; }
|
|
|
|
/// <inheritdoc />
|
|
public IReadOnlyList<IHitData> Hits => _hits;
|
|
|
|
/// <inheritdoc />
|
|
public IBattle Battle => _battle;
|
|
|
|
/// <inheritdoc />
|
|
public override int ScriptCount => 2 + User.ScriptCount;
|
|
|
|
/// <inheritdoc />
|
|
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts)
|
|
{
|
|
scripts.Add(Volatile);
|
|
scripts.Add(Script);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts)
|
|
{
|
|
GetOwnScripts(scripts);
|
|
User.CollectScripts(scripts);
|
|
}
|
|
} |