More move scripts

This commit is contained in:
2025-05-03 16:51:44 +02:00
parent f8c43b6ba0
commit 1973ff50fa
52 changed files with 704 additions and 78 deletions

View File

@@ -13,7 +13,7 @@ public abstract class BaseChargeMove<TVolatile> : Script where TVolatile : Requi
return;
move.User.Volatile.Add(CreateVolatile(move.User));
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("began_charging", new Dictionary<string, object>()
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("began_charging", new Dictionary<string, object>
{
{ "user", move.User },
}));

View File

@@ -13,7 +13,7 @@ public class BeakBlast : Script
if (battleData == null)
return;
choice.User.Volatile.Add(new BeakBlastEffect());
battleData.Battle.EventHook.Invoke(new DialogEvent("beak_blast_charge", new Dictionary<string, object>()
battleData.Battle.EventHook.Invoke(new DialogEvent("beak_blast_charge", new Dictionary<string, object>
{
{ "user", choice.User },
}));

View File

@@ -13,7 +13,7 @@ public class Bounce : Script
return;
move.User.Volatile.Add(new ChargeBounceEffect(move.User));
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("bounce_charge", new Dictionary<string, object>()
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("bounce_charge", new Dictionary<string, object>
{
{ "user", move.User },
}));

View File

@@ -3,5 +3,5 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "camouflage")]
public class Camouflage : Script
{
// FIXME: Implement this. How to get the terrain in a sane manner?
// FIXME: Implement this. How to get the terrain in a sane manner? See also SecretPower.cs
}

View File

@@ -1,4 +1,6 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using PkmnLib.Plugin.Gen7.Scripts.Side;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
@@ -6,16 +8,16 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "defog")]
public class Defog : Script
{
public static HashSet<StringKey> DefoggedEffects = new()
{
"mist",
"light_screen",
"reflect",
"safe_guard",
[PublicAPI] public static HashSet<StringKey> DefoggedEffects =
[
ScriptUtils.ResolveName<MistEffect>(),
ScriptUtils.ResolveName<LightScreenEffect>(),
ScriptUtils.ResolveName<ReflectEffect>(),
ScriptUtils.ResolveName<SafeguardEffect>(),
"spikes",
"toxic_spikes",
"stealth_rock",
};
];
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)

View File

@@ -13,7 +13,7 @@ public class Dig : Script
return;
move.User.Volatile.Add(new DigEffect(move.User));
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dig_charge", new Dictionary<string, object>()
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dig_charge", new Dictionary<string, object>
{
{ "user", move.User },
}));

View File

@@ -13,7 +13,7 @@ public class Dive : Script
return;
move.User.Volatile.Add(new DigEffect(move.User));
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dive_charge", new Dictionary<string, object>()
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("dive_charge", new Dictionary<string, object>
{
{ "user", move.User },
}));

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "flame_wheel")]
public class FlameWheel : Script
{
private float _burnChance;
/// <inheritdoc />
public override void OnInitialize(IReadOnlyDictionary<StringKey, object?>? parameters)
{
if (parameters is null || !parameters.TryGetValue("burn_chance", out var burnChance) ||
burnChance is not float chance)
{
throw new Exception("Flame Wheel: Missing or invalid parameters.");
}
_burnChance = chance;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
if (move.User.HasStatus("frozen"))
{
move.User.ClearStatus();
}
var burnChance = _burnChance;
if (move.Battle.Random.EffectChance(_burnChance, move, target, hit))
{
target.SetStatus("burned");
}
}
}

View File

@@ -13,7 +13,7 @@ public class Fly : Script
return;
move.User.Volatile.Add(new ChargeFlyEffect(move.User));
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("fly_charge", new Dictionary<string, object>()
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("fly_charge", new Dictionary<string, object>
{
{ "user", move.User },
}));

View File

@@ -11,7 +11,7 @@ public class FocusPunch : Script
{
choice.User.Volatile.Add(new FocusPunchEffect());
choice.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("focus_punch_charge",
new Dictionary<string, object>()
new Dictionary<string, object>
{
{ "pokemon", choice.User },
}));

View File

@@ -15,7 +15,7 @@ public class LightScreen : Script
return;
var turns = 5;
var dict = new Dictionary<StringKey, object?>()
var dict = new Dictionary<StringKey, object?>
{
{ "duration", turns },
};

View File

@@ -32,7 +32,7 @@ public class Magnitude : Script
// 5% chance for 10
_ => 10,
};
battleData.Battle.EventHook.Invoke(new DialogEvent("magnitude", new Dictionary<string, object>()
battleData.Battle.EventHook.Invoke(new DialogEvent("magnitude", new Dictionary<string, object>
{
{ "magnitude", magnitude },
{ "user", move.User },

View File

@@ -9,6 +9,11 @@ public class MeanLook : Script
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var targetEffect = target.Volatile.Add(new MeanLookEffectTarget());
if (targetEffect == null)
{
move.GetHitData(target, hit).Fail();
return;
}
var userEffect = new MeanLookEffectUser(targetEffect);
move.User.Volatile.Add(userEffect);
}

View File

@@ -13,6 +13,12 @@ public class RagePowder : Script
return;
var effect = battleData.BattleSide.VolatileScripts.Add(new RagePowderEffect(move.User));
if (effect == null)
{
move.GetHitData(target, hit).Fail();
return;
}
((RagePowderEffect)effect.Script!).User = move.User;
}
}

View File

@@ -0,0 +1,29 @@
using System.Linq;
using PkmnLib.Plugin.Gen7.Scripts.MoveVolatile;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "round")]
public class Round : Script
{
/// <inheritdoc />
public override void OnAfterMove(IExecutingMove move)
{
var choiceQueue = move.Battle.ChoiceQueue;
if (choiceQueue is null)
{
return;
}
var otherRoundMoves = choiceQueue.Where(x => x is IMoveChoice mc && mc.ChosenMove.MoveData.Name == "round")
.Cast<IMoveChoice>().ToList();
// We reverse the order here, as we're constantly pushing the choices to the front of the queue.
// By reversing the order, we ensure that the first choice is the one that is up next.
foreach (var otherRoundMove in otherRoundMoves.AsEnumerable().Reverse())
{
otherRoundMove.Volatile.Add(new RoundPowerBoost());
choiceQueue.MovePokemonChoiceNext(otherRoundMove.User);
}
}
}

View File

@@ -0,0 +1,11 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "safeguard")]
public class Safeguard : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
target.BattleData?.BattleSide.VolatileScripts.Add(new Side.SafeguardEffect());
}
}

View File

@@ -0,0 +1,7 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "secret_power")]
public class SecretPower : Script
{
// FIXME: Implement this. How to get the terrain in a sane manner? See also Camouflage.cs
}

View File

@@ -0,0 +1,14 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "self_destruct")]
public class SelfDestruct : Script
{
/// <inheritdoc />
public override void OnAfterMove(IExecutingMove move)
{
if (move.User.IsFainted)
return;
move.User.Faint(DamageSource.Misc);
}
}

View File

@@ -0,0 +1,32 @@
using System.Collections.Generic;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "shadow_force")]
public class ShadowForce : Script
{
/// <inheritdoc />
public override void PreventMove(IExecutingMove move, ref bool prevent)
{
if (move.User.Volatile.Contains<ShadowForceCharge>())
return;
move.User.Volatile.Add(new ShadowForceCharge(move.User));
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("shadow_force_charge",
new Dictionary<string, object>
{
{ "user", move.User },
}));
prevent = true;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
if (move.User.Volatile.Contains<ShadowForceCharge>())
return;
move.User.Volatile.Add(new ShadowForceCharge(move.User));
}
}

View File

@@ -0,0 +1,19 @@
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "shell_smash")]
public class ShellSmash : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
EventBatchId eventBatchId = new();
move.User.ChangeStatBoost(Statistic.Attack, 2, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.SpecialAttack, 2, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.Speed, 2, true, eventBatchId);
eventBatchId = new EventBatchId();
move.User.ChangeStatBoost(Statistic.Defense, -1, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.SpecialDefense, -1, true, eventBatchId);
}
}

View File

@@ -0,0 +1,23 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "shell_trap")]
public class ShellTrap : Script
{
/// <inheritdoc />
public override void OnBeforeMove(IExecutingMove move)
{
move.User.Volatile.Add(new ShellTrapHelper());
}
/// <inheritdoc />
public override void FailMove(IExecutingMove move, ref bool fail)
{
var shellTrapHelper = move.User.Volatile.Get<ShellTrapHelper>();
if (shellTrapHelper is not { HasHit: true })
{
fail = true;
}
}
}

View File

@@ -0,0 +1,19 @@
using PkmnLib.Plugin.Gen7.Scripts.Weather;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "shore_up")]
public class ShoreUp : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var healMod = 0.5f;
if (move.Battle.WeatherName == ScriptUtils.ResolveName<Sandstorm>())
{
healMod = 2f / 3f;
}
var heal = (uint)(target.MaxHealth * healMod);
target.Heal(heal);
}
}

View File

@@ -0,0 +1,16 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "simple_beam")]
public class SimpleBeam : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
if (!move.Battle.Library.StaticLibrary.Abilities.TryGet("simple", out var simpleAbility))
{
move.GetHitData(target, hit).Fail();
return;
}
target.ChangeAbility(simpleAbility);
}
}

View File

@@ -0,0 +1,37 @@
using System.Linq;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "sketch")]
public class Sketch : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
// Defensive programming; If the move we're using is not the same as the one picked, something changed
// our move to sketch. This should never happen, as moves are not allowed to change to sketch, but in the
// case it does, we should fail the move.
if (move.ChosenMove.MoveData != move.UseMove)
{
move.GetHitData(target, hit).Fail();
return;
}
var moveSlot = move.User.Moves.IndexOf(move.ChosenMove);
if (moveSlot == -1)
{
move.GetHitData(target, hit).Fail();
return;
}
var choiceQueue = move.Battle.PreviousTurnChoices;
var lastMove = choiceQueue.SelectMany(x => x).OfType<IMoveChoice>().LastOrDefault(x => x.User == target);
if (lastMove == null || lastMove.ChosenMove.MoveData.HasFlag("not_sketchable"))
{
move.GetHitData(target, hit).Fail();
return;
}
move.User.LearnMove(lastMove.ChosenMove.MoveData.Name, MoveLearnMethod.Sketch, (byte)moveSlot);
}
}

View File

@@ -0,0 +1,25 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "skill_swap")]
public class SkillSwap : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var targetAbility = target.ActiveAbility;
var userAbility = move.User.ActiveAbility;
if (targetAbility == null || userAbility == null)
{
move.GetHitData(target, hit).Fail();
return;
}
if (targetAbility.HasFlag("cant_be_changed") || userAbility.HasFlag("cant_be_changed"))
{
move.GetHitData(target, hit).Fail();
return;
}
move.User.ChangeAbility(targetAbility);
target.ChangeAbility(userAbility);
}
}