Implement highest damage AI, further work on AI runner, random fixes
All checks were successful
Build / Build (push) Successful in 51s
All checks were successful
Build / Build (push) Successful in 51s
This commit is contained in:
parent
32aaa5150a
commit
c795f20e54
@ -12,7 +12,7 @@ internal static class Program
|
||||
|
||||
private static Task<int> Main(string[] args)
|
||||
{
|
||||
Log.Logger = new LoggerConfiguration().MinimumLevel.Debug().WriteTo.Console().CreateLogger();
|
||||
Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Console().CreateLogger();
|
||||
Log.Information("Starting AI Runner...");
|
||||
_availableAIs = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes())
|
||||
.Where(type => type.IsSubclassOf(typeof(PokemonAI)) && !type.IsAbstract).Select(Activator.CreateInstance)
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using PkmnLib.Dynamic.AI;
|
||||
using PkmnLib.Dynamic.Libraries;
|
||||
using PkmnLib.Dynamic.Models;
|
||||
@ -12,32 +13,58 @@ public static class TestCommandRunner
|
||||
{
|
||||
internal static async Task RunTestCommand(PokemonAI ai1, PokemonAI ai2, int battles)
|
||||
{
|
||||
var t1 = DateTime.UtcNow;
|
||||
var library = DynamicLibraryImpl.Create([
|
||||
new Gen7Plugin(),
|
||||
]);
|
||||
|
||||
Log.Information("Running {Battles} battles between {AI1} and {AI2}", battles, ai1.Name, ai2.Name);
|
||||
var averageTimePerTurnPerBattle = new List<double>(battles);
|
||||
var results = new List<BattleResult>(battles);
|
||||
var random = new RandomImpl();
|
||||
var results = new ConcurrentBag<BattleResult>();
|
||||
var rootRandom = new RandomImpl();
|
||||
|
||||
const int maxTasks = 10;
|
||||
var battleTasks = new Task[maxTasks];
|
||||
var randoms = new IRandom[maxTasks];
|
||||
for (var i = 0; i < maxTasks; i++)
|
||||
{
|
||||
randoms[i] = new RandomImpl(rootRandom.GetInt());
|
||||
battleTasks[i] = Task.CompletedTask; // Initialize tasks to avoid null references
|
||||
}
|
||||
// Here you would implement the logic to run the AI scripts against each other.
|
||||
// This is a placeholder for demonstration purposes.
|
||||
for (var i = 0; i < battles; i++)
|
||||
{
|
||||
Log.Information("Battle {BattleNumber}: {AI1} vs {AI2}", i + 1, ai1.Name, ai2.Name);
|
||||
var battle = GenerateBattle(library, 3, random);
|
||||
var timePerTurn = new List<double>(20);
|
||||
while (!battle.HasEnded)
|
||||
var taskIndex = i % maxTasks;
|
||||
var index = i;
|
||||
var battleTask = Task.Run(async () =>
|
||||
{
|
||||
var res = await GetAndSetChoices(battle, ai1, ai2);
|
||||
timePerTurn.Add(res.MsPerTurn);
|
||||
Log.Information("Battle {BattleNumber}: {AI1} vs {AI2}", index + 1, ai1.Name, ai2.Name);
|
||||
var random = randoms[taskIndex];
|
||||
var battle = GenerateBattle(library, 3, random);
|
||||
var timePerTurn = new List<double>(20);
|
||||
while (!battle.HasEnded)
|
||||
{
|
||||
var res = await GetAndSetChoices(battle, ai1, ai2);
|
||||
timePerTurn.Add(res.MsPerTurn);
|
||||
}
|
||||
var result = battle.Result;
|
||||
Log.Information("Battle {BattleNumber} ended with result: {Result}", index + 1, result);
|
||||
averageTimePerTurnPerBattle.Add(timePerTurn.Average());
|
||||
results.Add(result.Value);
|
||||
});
|
||||
battleTasks[taskIndex] = battleTask;
|
||||
if (i % maxTasks == maxTasks - 1 || i == battles - 1)
|
||||
{
|
||||
Log.Debug("Starting {TaskCount} tasks", maxTasks);
|
||||
await Task.WhenAll(battleTasks);
|
||||
Log.Debug("Batch of {TaskCount} tasks completed", maxTasks);
|
||||
battleTasks = new Task[maxTasks]; // Reset tasks for the next batch
|
||||
}
|
||||
var result = battle.Result;
|
||||
Log.Information("Battle {BattleNumber} ended with result: {Result}", i + 1, result);
|
||||
averageTimePerTurnPerBattle.Add(timePerTurn.Average());
|
||||
results.Add(result.Value);
|
||||
}
|
||||
Log.Information("All battles completed");
|
||||
|
||||
var t2 = DateTime.UtcNow;
|
||||
Log.Information("{Amount} battles completed in {Duration} ms", battles, (t2 - t1).TotalMilliseconds);
|
||||
var averageTimePerTurn = averageTimePerTurnPerBattle.Average();
|
||||
Log.Information("Average time per turn: {AverageTimePerTurn} ms", averageTimePerTurn);
|
||||
|
||||
@ -45,8 +72,11 @@ public static class TestCommandRunner
|
||||
var winCount2 = results.Count(x => x.WinningSide == 1);
|
||||
var drawCount = results.Count(x => x.WinningSide == null);
|
||||
|
||||
Log.Information("Results: {AI1} wins: {WinCount1}, {AI2} wins: {WinCount2}, Draws: {DrawCount}", ai1.Name,
|
||||
winCount1, ai2.Name, winCount2, drawCount);
|
||||
var winRate1 = winCount1 / (double)battles * 100;
|
||||
var winRate2 = winCount2 / (double)battles * 100;
|
||||
Log.Information("AI {AI1} win rate: {WinRate1:F3}% ({WinCount1} wins)", ai1.Name, winRate1, winCount1);
|
||||
Log.Information("AI {AI2} win rate: {WinRate2:F3}% ({WinCount2} wins)", ai2.Name, winRate2, winCount2);
|
||||
Log.Information("Draw rate: {DrawRate:F3}% ({DrawCount} draws)", drawCount / (double)battles * 100, drawCount);
|
||||
}
|
||||
|
||||
private static PokemonPartyImpl GenerateParty(IDynamicLibrary library, int length, IRandom random)
|
||||
@ -64,7 +94,7 @@ public static class TestCommandRunner
|
||||
IsHidden = false,
|
||||
Index = abilityIndex,
|
||||
}, level, 0, species.GetRandomGender(random), 0, nature.Name);
|
||||
var moves = defaultForm.Moves.GetDistinctLevelMoves().OrderBy(x => random.GetInt()).Take(4);
|
||||
var moves = defaultForm.Moves.GetDistinctLevelMoves().OrderBy(_ => random.GetInt()).Take(4);
|
||||
foreach (var move in moves)
|
||||
mon.LearnMove(move, MoveLearnMethod.LevelUp, 255);
|
||||
|
||||
|
92
PkmnLib.Dynamic/AI/HighestDamageAI.cs
Normal file
92
PkmnLib.Dynamic/AI/HighestDamageAI.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Dynamic.Models.Choices;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.AI;
|
||||
|
||||
public class HighestDamageAI : PokemonAI
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public HighestDamageAI() : base("highest_damage")
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
||||
{
|
||||
var opponentSide = pokemon.BattleData!.SideIndex == 0 ? (byte)1 : (byte)0;
|
||||
var opponent = battle.Sides[opponentSide].Pokemon.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
||||
var moves = pokemon.Moves.WhereNotNull().Where(x => battle.CanUse(new MoveChoice(pokemon, x, opponentSide, 0)))
|
||||
.ToList();
|
||||
if (opponent == null)
|
||||
{
|
||||
var move = moves.FirstOrDefault();
|
||||
if (move != null)
|
||||
{
|
||||
return new MoveChoice(pokemon, move, opponentSide, 0);
|
||||
}
|
||||
return battle.Library.MiscLibrary.ReplacementChoice(pokemon, opponentSide, 0);
|
||||
}
|
||||
|
||||
var movesWithDamage = moves.Select(move =>
|
||||
{
|
||||
var hitData = new CustomHitData
|
||||
{
|
||||
BasePower = move.MoveData.BasePower,
|
||||
Effectiveness =
|
||||
battle.Library.StaticLibrary.Types.GetEffectiveness(move.MoveData.MoveType, opponent.Types),
|
||||
Type = move.MoveData.MoveType,
|
||||
};
|
||||
return new
|
||||
{
|
||||
Move = move,
|
||||
Damage = battle.Library.DamageCalculator.GetDamage(null, move.MoveData.Category, pokemon, opponent, 1,
|
||||
0, hitData),
|
||||
};
|
||||
}).OrderByDescending(x => x.Damage).ToList();
|
||||
if (movesWithDamage.Count == 0)
|
||||
{
|
||||
return battle.Library.MiscLibrary.ReplacementChoice(pokemon, opponentSide, 0);
|
||||
}
|
||||
var bestMove = movesWithDamage.First().Move;
|
||||
return new MoveChoice(pokemon, bestMove, opponentSide, 0);
|
||||
}
|
||||
|
||||
private class CustomHitData : IHitData
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsCritical => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ushort BasePower { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public float Effectiveness { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint Damage => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public TypeIdentifier? Type { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsContact => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasFailed => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Fail()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetFlag(StringKey flag)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasFlag(StringKey flag) => false;
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ namespace PkmnLib.Dynamic.AI;
|
||||
/// <summary>
|
||||
/// The base class for implementing an AI for Pokémon.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[PublicAPI, UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public abstract class PokemonAI
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -38,6 +38,8 @@ public interface IMoveChoice : ITurnChoice
|
||||
/// </summary>
|
||||
Dictionary<StringKey, object?>? AdditionalData { get; }
|
||||
|
||||
void SetAdditionalData(StringKey key, object? value);
|
||||
|
||||
/// <summary>
|
||||
/// Volatile effects that are applied to the move choice.
|
||||
/// </summary>
|
||||
@ -84,7 +86,14 @@ public class MoveChoice : TurnChoice, IMoveChoice
|
||||
public ScriptContainer Script { get; set; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<StringKey, object?>? AdditionalData { get; }
|
||||
public Dictionary<StringKey, object?>? AdditionalData { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetAdditionalData(StringKey key, object? value)
|
||||
{
|
||||
AdditionalData ??= new Dictionary<StringKey, object?>();
|
||||
AdditionalData[key] = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IScriptSet Volatile { get; }
|
||||
|
@ -936,6 +936,8 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||
AbilityScript.Clear();
|
||||
}
|
||||
|
||||
private (IAbility, AbilityIndex)? _abilityCache;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAbility? ActiveAbility
|
||||
{
|
||||
@ -945,9 +947,12 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||
return null;
|
||||
if (OverrideAbility != null)
|
||||
return OverrideAbility;
|
||||
if (_abilityCache is not null && _abilityCache.Value.Item2 == AbilityIndex)
|
||||
return _abilityCache.Value.Item1;
|
||||
var ability = Form.GetAbility(AbilityIndex);
|
||||
if (!Library.StaticLibrary.Abilities.TryGet(ability, out var abilityObj))
|
||||
throw new KeyNotFoundException($"Ability {ability} not found.");
|
||||
_abilityCache = (abilityObj, AbilityIndex);
|
||||
return abilityObj;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
using PkmnLib.Dynamic.ScriptHandling;
|
||||
using PkmnLib.Dynamic.ScriptHandling.Registry;
|
||||
using Assembly = System.Reflection.Assembly;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Tests.Scripts;
|
||||
|
||||
public class GenericScriptTests
|
||||
{
|
||||
[Test]
|
||||
public async Task EveryScriptHasAttribute()
|
||||
{
|
||||
var scripts = typeof(Gen7Plugin).Assembly.GetTypes()
|
||||
.Where(t => t.IsSubclassOf(typeof(Script)) && !t.IsAbstract);
|
||||
foreach (var script in scripts)
|
||||
{
|
||||
var attributes = script.GetCustomAttributes(typeof(ScriptAttribute), false);
|
||||
await Assert.That(attributes).IsNotEmpty().Because(
|
||||
$"Script {script.Name} does not have the Script attribute defined.");
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ using PkmnLib.Static.Moves;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Battle;
|
||||
|
||||
[Script(ScriptCategory.Battle, "wonder_room_effect")]
|
||||
public class WonderRoomEffect : Script, IScriptChangeDefensiveStatValue, IScriptOnEndTurn
|
||||
{
|
||||
public int TurnsLeft { get; private set; } = 5;
|
||||
|
@ -12,6 +12,7 @@ public class Bounce : Script, IScriptPreventMove, IScriptOnBeforeMove, IScriptOn
|
||||
return;
|
||||
|
||||
move.User.Volatile.Add(new ChargeBounceEffect(move.User));
|
||||
move.MoveChoice.SetAdditionalData("bounce_charge", true);
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("bounce_charge", new Dictionary<string, object>
|
||||
{
|
||||
{ "user", move.User },
|
||||
|
@ -12,6 +12,7 @@ public class Dig : Script, IScriptPreventMove, IScriptOnBeforeMove
|
||||
return;
|
||||
|
||||
move.User.Volatile.Add(new DigEffect(move.User));
|
||||
move.MoveChoice.SetAdditionalData("dig_charge", true);
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dig_charge", new Dictionary<string, object>
|
||||
{
|
||||
{ "user", move.User },
|
||||
|
@ -12,6 +12,7 @@ public class Dive : Script, IScriptPreventMove, IScriptOnSecondaryEffect
|
||||
return;
|
||||
|
||||
move.User.Volatile.Add(new DiveEffect(move.User));
|
||||
move.MoveChoice.SetAdditionalData("dive_charge", true);
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dive_charge", new Dictionary<string, object>
|
||||
{
|
||||
{ "user", move.User },
|
||||
|
@ -12,6 +12,7 @@ public class Fly : Script, IScriptPreventMove, IScriptOnBeforeMove
|
||||
return;
|
||||
|
||||
move.User.Volatile.Add(new ChargeFlyEffect(move.User));
|
||||
move.MoveChoice.SetAdditionalData("fly_charge", true);
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("fly_charge", new Dictionary<string, object>
|
||||
{
|
||||
{ "user", move.User },
|
||||
|
@ -23,6 +23,11 @@ public class MeFirst : Script, IScriptChangeMove
|
||||
choice.Fail();
|
||||
return;
|
||||
}
|
||||
if (battleData.Battle.Library.MiscLibrary.IsReplacementChoice(targetMove))
|
||||
{
|
||||
choice.Fail();
|
||||
return;
|
||||
}
|
||||
var targetMoveData = targetMove.ChosenMove.MoveData;
|
||||
moveName = targetMoveData.Name;
|
||||
choice.Volatile.Add(new MeFirstPowerBoost());
|
||||
|
@ -16,6 +16,7 @@ public class PhantomForce : Script, IScriptPreventMove, IScriptOnSecondaryEffect
|
||||
return;
|
||||
|
||||
move.User.Volatile.Add(new PhantomForceCharge(move.User));
|
||||
move.MoveChoice.SetAdditionalData("phantom_force_charge", true);
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("phantom_force_charge",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
|
@ -12,6 +12,7 @@ public class ShadowForce : Script, IScriptPreventMove, IScriptOnSecondaryEffect
|
||||
return;
|
||||
|
||||
move.User.Volatile.Add(new ShadowForceCharge(move.User));
|
||||
move.MoveChoice.SetAdditionalData("shadow_force_charge", true);
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("shadow_force_charge",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
|
@ -12,6 +12,7 @@ public class SkyDrop : Script, IScriptPreventMove, IScriptOnBeforeMove
|
||||
return;
|
||||
|
||||
move.User.Volatile.Add(new ChargeSkyDropEffect(move.User));
|
||||
move.MoveChoice.SetAdditionalData("sky_drop_charge", true);
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("sky_drop_charge", new Dictionary<string, object>
|
||||
{
|
||||
{ "user", move.User },
|
||||
|
@ -33,4 +33,13 @@ public class ChargeBounceEffect : Script, IScriptForceTurnSelection, IScriptChan
|
||||
if (!move.UseMove.HasFlag("effective_against_fly"))
|
||||
damage *= 2;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAfterMoveChoice(IMoveChoice moveChoice)
|
||||
{
|
||||
if (moveChoice.AdditionalData?.ContainsKey("bounce_charge") != true)
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
}
|
@ -33,4 +33,13 @@ public class ChargeFlyEffect : Script, IScriptForceTurnSelection, IScriptChangeI
|
||||
if (!move.UseMove.HasFlag("effective_against_fly"))
|
||||
damage *= 2;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAfterMoveChoice(IMoveChoice moveChoice)
|
||||
{
|
||||
if (moveChoice.AdditionalData?.ContainsKey("fly_charge") != true)
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
}
|
@ -33,4 +33,13 @@ public class ChargeSkyDropEffect : Script, IScriptForceTurnSelection, IScriptCha
|
||||
if (!move.UseMove.HasFlag("effective_against_fly"))
|
||||
damage *= 2;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAfterMoveChoice(IMoveChoice moveChoice)
|
||||
{
|
||||
if (moveChoice.AdditionalData?.ContainsKey("sky_drop_charge") != true)
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
}
|
@ -32,4 +32,13 @@ public class DigEffect : Script, IScriptForceTurnSelection, IScriptChangeIncomin
|
||||
if (!move.UseMove.HasFlag("effective_against_underground"))
|
||||
damage *= 2;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAfterMoveChoice(IMoveChoice moveChoice)
|
||||
{
|
||||
if (moveChoice.AdditionalData?.ContainsKey("dig_charge") != true)
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
}
|
@ -32,4 +32,13 @@ public class DiveEffect : Script, IScriptForceTurnSelection, IScriptChangeIncomi
|
||||
if (!move.UseMove.HasFlag("effective_against_underwater"))
|
||||
damage *= 2;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAfterMoveChoice(IMoveChoice moveChoice)
|
||||
{
|
||||
if (moveChoice.AdditionalData?.ContainsKey("dive_charge") != true)
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "helping_hand")]
|
||||
public class HelpingHandEffect : Script, IScriptChangeBasePower, IScriptOnEndTurn
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
@ -1,5 +1,6 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "lucky_chant")]
|
||||
public class LuckyChantEffect : Script, IScriptBlockCriticalHit, IScriptOnEndTurn
|
||||
{
|
||||
private int _turnsLeft = 5;
|
||||
|
@ -24,4 +24,13 @@ public class PhantomForceCharge : Script, IScriptForceTurnSelection, IScriptBloc
|
||||
{
|
||||
block = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAfterMoveChoice(IMoveChoice moveChoice)
|
||||
{
|
||||
if (moveChoice.AdditionalData?.ContainsKey("phantom_force_charge") != true)
|
||||
{
|
||||
moveChoice.User.Volatile.Remove(ScriptUtils.ResolveName<PhantomForceCharge>());
|
||||
}
|
||||
}
|
||||
}
|
@ -24,4 +24,13 @@ public class ShadowForceCharge : Script, IScriptForceTurnSelection, IScriptBlock
|
||||
{
|
||||
block = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAfterMoveChoice(IMoveChoice moveChoice)
|
||||
{
|
||||
if (moveChoice.AdditionalData?.ContainsKey("shadow_force_charge") != true)
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ using PkmnLib.Static.Moves;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
[Script(ScriptCategory.Side, "mat_block")]
|
||||
public class MatBlockEffect : Script, IScriptOnEndTurn, IScriptBlockIncomingHit
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
@ -1,5 +1,6 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
[Script(ScriptCategory.Side, "quick_guard")]
|
||||
public class QuickGuardEffect : Script, IScriptIsInvulnerableToMove, IScriptOnEndTurn
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
@ -2,6 +2,7 @@ using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
[Script(ScriptCategory.Side, "safe_guard")]
|
||||
public class SafeguardEffect : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
@ -2,6 +2,7 @@ using PkmnLib.Dynamic.BattleFlow;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
[Script(ScriptCategory.Side, "spotlight")]
|
||||
public class SpotlightEffect : Script, IScriptChangeIncomingTargets, IScriptOnEndTurn
|
||||
{
|
||||
private readonly byte _position;
|
||||
|
@ -4,14 +4,10 @@ public static class TurnChoiceHelper
|
||||
{
|
||||
public static IMoveChoice CreateMoveChoice(IPokemon owner, StringKey moveName, byte targetSide, byte targetPosition)
|
||||
{
|
||||
var move = owner.Moves.FirstOrDefault(x => x?.MoveData.Name == moveName);
|
||||
if (move == null)
|
||||
{
|
||||
if (!owner.Library.StaticLibrary.Moves.TryGet(moveName, out var moveData))
|
||||
throw new Exception($"Move '{moveName}' not found in move library.");
|
||||
if (!owner.Library.StaticLibrary.Moves.TryGet(moveName, out var moveData))
|
||||
throw new Exception($"Move '{moveName}' not found in move library.");
|
||||
|
||||
move = new LearnedMoveImpl(moveData, MoveLearnMethod.Unknown);
|
||||
}
|
||||
var move = new LearnedMoveImpl(moveData, MoveLearnMethod.Unknown);
|
||||
return new MoveChoice(owner, move, targetSide, targetPosition);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user