Implements several more moves

This commit is contained in:
2025-01-27 12:18:48 +01:00
parent 549b92048a
commit 3a75493912
26 changed files with 676 additions and 38 deletions

View File

@@ -8,7 +8,8 @@ public class BulkUp : Script
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
move.User.ChangeStatBoost(Statistic.Attack, 1, true);
move.User.ChangeStatBoost(Statistic.Defense, 1, true);
EventBatchId eventBatchId = new();
move.User.ChangeStatBoost(Statistic.Attack, 1, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.Defense, 1, true, eventBatchId);
}
}

View File

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

View File

@@ -0,0 +1,7 @@
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?
}

View File

@@ -0,0 +1,25 @@
using PkmnLib.Static;
using PkmnLib.Static.Species;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "captivate")]
public class Captivate : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var user = move.User;
if (target.Gender == Gender.Genderless || target.Gender == user.Gender)
{
move.GetHitData(target, hit).Fail();
return;
}
if (target.ActiveAbility?.Name == "oblivious")
{
move.GetHitData(target, hit).Fail();
return;
}
target.ChangeStatBoost(Statistic.SpecialAttack, -2, false);
}
}

View File

@@ -0,0 +1,13 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "celebrate")]
public class Celebrate : Script
{
/// <inheritdoc />
public override void PreventMoveSelection(IMoveChoice choice, ref bool prevent)
{
// This move is mostly useless, and it's not worth the effort to implement it.
// Prevent it from being selected.
prevent = true;
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using PkmnLib.Static;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
public abstract class ChangeUserStats : Script
{
private readonly Statistic _stat;
private sbyte _amount;
protected ChangeUserStats(Statistic stat)
{
_stat = stat;
}
/// <inheritdoc />
public override void OnInitialize(IReadOnlyDictionary<StringKey, object?>? parameters)
{
if (parameters == null)
{
throw new ArgumentNullException(nameof(parameters));
}
if (!parameters.TryGetValue("amount", out var amount) || amount == null)
{
throw new ArgumentException("Parameter 'amount' is required.");
}
_amount = (sbyte)amount;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
move.User.ChangeStatBoost(_stat, _amount, true);
}
}
[Script(ScriptCategory.Move, "change_user_attack")]
public class ChangeUserAttack : ChangeUserStats
{
public ChangeUserAttack() : base(Statistic.Attack)
{
}
}
[Script(ScriptCategory.Move, "change_user_defense")]
public class ChangeUserDefense : ChangeUserStats
{
public ChangeUserDefense() : base(Statistic.Defense)
{
}
}
[Script(ScriptCategory.Move, "change_user_special_attack")]
public class ChangeUserSpecialAttack : ChangeUserStats
{
public ChangeUserSpecialAttack() : base(Statistic.SpecialAttack)
{
}
}
[Script(ScriptCategory.Move, "change_user_special_defense")]
public class ChangeUserSpecialDefense : ChangeUserStats
{
public ChangeUserSpecialDefense() : base(Statistic.SpecialDefense)
{
}
}
[Script(ScriptCategory.Move, "change_user_speed")]
public class ChangeUserSpeed : ChangeUserStats
{
public ChangeUserSpeed() : base(Statistic.Speed)
{
}
}

View File

@@ -0,0 +1,15 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "charge")]
public class Charge : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
move.User.ChangeStatBoost(Statistic.SpecialDefense, 1, true);
move.User.Volatile.Add(new ChargeEffect());
}
}

View File

@@ -0,0 +1,13 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "chip_away")]
public class ChipAway : Script
{
/// <inheritdoc />
public override void BypassDefensiveStatBoosts(IExecutingMove move, IPokemon target, byte hit, ref bool bypass)
{
bypass = true;
}
// TODO: bypass evasion stat.
}

View File

@@ -0,0 +1,7 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "circle_throw")]
public class CircleThrow : Script
{
// TODO: Implement this. How to handle forced switch?
}

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "confuse")]
public class Confuse : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
target.Volatile.Add(new Confusion());
}
}

View File

@@ -0,0 +1,20 @@
using System.Linq;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "conversion")]
public class Conversion : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var moveType = move.User.Moves.WhereNotNull().FirstOrDefault()?.MoveData.MoveType;
if (moveType == null)
{
move.GetHitData(target, hit).Fail();
return;
}
move.User.SetTypes([moveType.Value]);
}
}

View File

@@ -0,0 +1,57 @@
using System.Linq;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "conversion_2")]
public class Conversion2 : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var previousTurnChoices = target.BattleData?.Battle.PreviousTurnChoices;
var nextExecutingChoice = target.BattleData?.Battle.ChoiceQueue?.Peek();
var lastMoveByTarget = previousTurnChoices?
// The previous turn choices include the choices of the current turn, so we need to have special handling for
// the current turn
.Select((x, index) =>
{
// All choices before the current turn are valid
if (index < previousTurnChoices.Count - 1)
return x;
// If there is no next choice, we're at the end of the list, so we can just return the whole list
if (nextExecutingChoice == null)
return x;
// Otherwise we determine where the next choice is and return everything before that
var indexOfNext = x.IndexOf(nextExecutingChoice);
if (indexOfNext == -1)
return x;
return x.Take(indexOfNext);
})
.SelectMany(x => x)
// We only want the last move choice by the target
.OfType<IMoveChoice>().FirstOrDefault(x => x.User == target);
if (lastMoveByTarget == null)
{
move.GetHitData(target, hit).Fail();
return;
}
var typeLibrary = move.User.BattleData!.Battle.Library.StaticLibrary.Types;
// Get all types against which the last move would be not very effective
var type = typeLibrary.GetAllEffectivenessFromAttacking(lastMoveByTarget.ChosenMove.MoveData.MoveType)
.Where(x => x.effectiveness < 1)
// Shuffle them randomly, but deterministically
.OrderBy(_ => move.User.BattleData.Battle.Random.GetInt())
.ThenBy(x => x.type.Value)
// And grab the first one
.Select(x => x.type)
.FirstOrDefault();
if (type == null)
{
move.GetHitData(target, hit).Fail();
return;
}
move.User.SetTypes([type]);
}
}

View File

@@ -0,0 +1,24 @@
using System.Linq;
using PkmnLib.Plugin.Gen7.Scripts.Utils;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "copycat")]
public class Copycat : Script
{
/// <inheritdoc />
public override void ChangeMove(IMoveChoice choice, ref StringKey moveName)
{
var lastMove = choice.User.BattleData?.Battle.PreviousTurnChoices
.SelectMany(x => x)
.OfType<IMoveChoice>()
.LastOrDefault();
if (lastMove == null || !lastMove.ChosenMove.MoveData.CanCopyMove())
{
choice.Fail();
return;
}
moveName = lastMove.ChosenMove.MoveData.Name;
}
}

View File

@@ -0,0 +1,25 @@
using System.Linq;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "core_enforcer")]
public class CoreEnforcer : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var battleData = target.BattleData;
if (battleData == null)
return;
var turnChoices = battleData.Battle.PreviousTurnChoices.Last();
var currentChoiceIndex = turnChoices.IndexOf(move.MoveChoice);
if (currentChoiceIndex == -1 ||
!turnChoices.Take(currentChoiceIndex).Any(choice => choice is IMoveChoice or IItemChoice))
{
move.GetHitData(target, hit).Fail();
return;
}
target.SuppressAbility();
}
}

View File

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

View File

@@ -0,0 +1,18 @@
using System;
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "reset_target_stats")]
public class ResetTargetStats : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
EventBatchId eventBatchId = new();
foreach (Statistic stat in Enum.GetValues(typeof(Statistic)))
{
target.ChangeStatBoost(stat, (sbyte)-target.StatBoost.GetStatistic(stat), true, eventBatchId);
}
}
}

View File

@@ -0,0 +1,30 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "charge_effect")]
public class ChargeEffect : Script
{
private bool _turnOfUse = true;
/// <inheritdoc />
public override void ChangeDamageModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier)
{
var library = target.BattleData?.Battle.Library;
if (library == null)
return;
if (!library.StaticLibrary.Types.TryGetTypeIdentifier("electric", out var electricType))
return;
if (move.UseMove.MoveType == electricType)
modifier *= 2;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
if (_turnOfUse)
{
_turnOfUse = false;
return;
}
RemoveSelf();
}
}

View File

@@ -0,0 +1,7 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "confusion")]
public class Confusion : Script
{
// TODO: Implement confusion
}

View File

@@ -36,7 +36,7 @@ public class Hail : Script
continue;
if (pokemon.Types.Contains(iceType))
continue;
if (_hailIgnoreAbilities.Contains(pokemon.ActiveAbility.Name))
if (pokemon.ActiveAbility != null && _hailIgnoreAbilities.Contains(pokemon.ActiveAbility.Name))
continue;
var maxHealth = pokemon.BoostedStats.Hp;