More moves, allow for typeless moves

This commit is contained in:
2025-05-02 15:46:37 +02:00
parent 807acf1947
commit 068ff8d5b7
33 changed files with 525 additions and 56 deletions

View File

@@ -1,6 +1,7 @@
using PkmnLib.Dynamic.Events;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Static;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;
@@ -55,7 +56,7 @@ internal static class MoveTurnExecutor
return;
}
var executingMove = new ExecutingMoveImpl(targets, numberOfHits, chosenMove, useMove, moveChoice);
var executingMove = new ExecutingMoveImpl(targets, numberOfHits, chosenMove, useMove, moveChoice, battle);
var prevented = false;
executingMove.RunScriptHook(x => x.PreventMove(executingMove, ref prevented));
@@ -124,7 +125,7 @@ internal static class MoveTurnExecutor
executingMove.RunScriptHook(x => x.OnBeforeHit(executingMove, target, hitIndex));
var useMove = executingMove.UseMove;
var hitType = useMove.MoveType;
var hitType = (TypeIdentifier?)useMove.MoveType;
executingMove.RunScriptHook(x => x.ChangeMoveType(executingMove, target, hitIndex, ref hitType));
var hitData = (HitData)executingMove.GetDataFromRawIndex(targetHitStat + i);
@@ -134,7 +135,9 @@ internal static class MoveTurnExecutor
executingMove.RunScriptHook(x => x.ChangeTypesForMove(executingMove, target, hitIndex, types));
target.RunScriptHook(x => x.ChangeTypesForIncomingMove(executingMove, target, hitIndex, types));
var effectiveness = battle.Library.StaticLibrary.Types.GetEffectiveness(hitType, types);
var effectiveness = hitType == null
? 1
: battle.Library.StaticLibrary.Types.GetEffectiveness(hitType.Value, types);
executingMove.RunScriptHook(x => x.ChangeEffectiveness(executingMove, target, hitIndex, ref effectiveness));
target.RunScriptHook(x =>
x.ChangeIncomingEffectiveness(executingMove, target, hitIndex, ref effectiveness));

View File

@@ -1,6 +1,7 @@
using PkmnLib.Dynamic.Events;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Static;
using PkmnLib.Static.Utils;
namespace PkmnLib.Dynamic.Models;
@@ -130,6 +131,21 @@ public interface IBattleSide : IScriptSource, IDeepCloneable
/// Gets a random Pokémon on the given side.
/// </summary>
byte GetRandomPosition();
/// <summary>
/// Marks an item as consumed for a position. Can be used by moves such as Recycle to get the item back.
/// </summary>
void SetConsumedItem(byte battleDataPosition, IItem heldItem);
/// <summary>
/// Gets the last consumed item for a position. Can be used by moves such as Recycle to get the item back.
/// </summary>
IItem? GetLastConsumedItem(byte battleDataPosition);
void MarkFaint(byte position);
uint? GetLastFaintTurn(byte position);
uint? GetLastFaintTurn();
}
/// <inheritdoc cref="IBattleSide"/>
@@ -302,6 +318,42 @@ public class BattleSideImpl : ScriptSource, IBattleSide
/// <inheritdoc />
public byte GetRandomPosition() => (byte)Battle.Random.GetInt(0, NumberOfPositions);
private Dictionary<byte, IItem>? _lastConsumedItems;
/// <inheritdoc />
public void SetConsumedItem(byte battleDataPosition, IItem heldItem)
{
_lastConsumedItems ??= new Dictionary<byte, IItem>();
_lastConsumedItems[battleDataPosition] = heldItem;
}
/// <inheritdoc />
public IItem? GetLastConsumedItem(byte battleDataPosition) =>
_lastConsumedItems?.GetValueOrDefault(battleDataPosition);
private Dictionary<byte, uint>? _lastFaintTurn;
/// <inheritdoc />
public void MarkFaint(byte position)
{
_lastFaintTurn ??= new Dictionary<byte, uint>();
_lastFaintTurn[position] = Battle.CurrentTurnNumber;
}
/// <inheritdoc />
public uint? GetLastFaintTurn(byte position)
{
if (_lastFaintTurn is null)
return null;
if (_lastFaintTurn.TryGetValue(position, out var turn))
return turn;
return null;
}
/// <inheritdoc />
public uint? GetLastFaintTurn() =>
_lastFaintTurn?.Values.Max() ?? null;
/// <inheritdoc />
public override int ScriptCount => 1 + Battle.ScriptCount;

View File

@@ -33,9 +33,9 @@ public interface IHitData
uint Damage { get; }
/// <summary>
/// The type of the hit.
/// The type of the hit. Null if the move is typeless.
/// </summary>
TypeIdentifier Type { get; }
TypeIdentifier? Type { get; }
/// <summary>
/// Whether the hit has failed.
@@ -64,7 +64,7 @@ public record HitData : IHitData
public uint Damage { get; internal set; }
/// <inheritdoc />
public TypeIdentifier Type { get; internal set; }
public TypeIdentifier? Type { get; internal set; }
/// <inheritdoc />
public bool HasFailed { get; private set; }
@@ -141,6 +141,8 @@ public interface IExecutingMove : IScriptSource
IMoveChoice MoveChoice { get; }
IReadOnlyList<IHitData> Hits { get; }
IBattle Battle { get; }
}
/// <inheritdoc cref="IExecutingMove"/>
@@ -148,16 +150,18 @@ 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)
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];
@@ -230,6 +234,9 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
/// <inheritdoc />
public IReadOnlyList<IHitData> Hits => _hits;
/// <inheritdoc />
public IBattle Battle => _battle;
/// <inheritdoc />
public override int ScriptCount => 2 + User.ScriptCount;

View File

@@ -346,7 +346,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
/// <summary>
/// Adds a non-volatile status to the Pokemon.
/// </summary>
void SetStatus(StringKey status);
bool SetStatus(StringKey status);
/// <summary>
/// Removes the current non-volatile status from the Pokemon.
@@ -447,7 +447,7 @@ public interface IPokemonBattleData : IDeepCloneable
/// <summary>
/// Marks an item as consumed.
/// </summary>
void MarkItemAsConsumed(IItem itemName);
void MarkItemAsConsumed(IItem item);
uint SwitchInTurn { get; internal set; }
@@ -780,6 +780,7 @@ public class PokemonImpl : ScriptSource, IPokemon
this.RunScriptHook(script => script.PreventHeldItemConsume(this, HeldItem, ref prevented));
if (prevented)
return false;
BattleData.MarkItemAsConsumed(HeldItem);
}
// TODO: actually consume the item
@@ -1007,6 +1008,7 @@ public class PokemonImpl : ScriptSource, IPokemon
{
BattleData.Battle.Sides[BattleData.SideIndex].MarkPositionAsUnfillable(BattleData.Position);
}
BattleData.BattleSide.MarkFaint(BattleData.Position);
// Validate the battle state to see if the battle is over.
BattleData.Battle.ValidateBattleState();
@@ -1078,11 +1080,12 @@ public class PokemonImpl : ScriptSource, IPokemon
public bool HasStatus(StringKey status) => StatusScript.Script?.Name == status;
/// <inheritdoc />
public void SetStatus(StringKey status)
public bool SetStatus(StringKey status)
{
if (!Library.ScriptResolver.TryResolve(ScriptCategory.Status, status, null, out var statusScript))
throw new KeyNotFoundException($"Status script {status} not found");
StatusScript.Set(statusScript);
return true;
}
/// <inheritdoc />
@@ -1255,9 +1258,10 @@ public class PokemonBattleDataImpl : IPokemonBattleData
public IReadOnlyList<IItem> ConsumedItems => _consumedItems;
/// <inheritdoc />
public void MarkItemAsConsumed(IItem itemName)
public void MarkItemAsConsumed(IItem item)
{
_consumedItems.Add(itemName);
_consumedItems.Add(item);
BattleSide.SetConsumedItem(Position, item);
}
/// <inheritdoc />