This commit is contained in:
@@ -23,6 +23,7 @@ public static class MoveFlags
|
||||
public static readonly StringKey Mental = "mental";
|
||||
public static readonly StringKey Mirror = "mirror";
|
||||
public static readonly StringKey MultiHit = "multi_hit";
|
||||
public static readonly StringKey NoRedirection = "no_redirection";
|
||||
public static readonly StringKey NonSkyBattle = "non_sky_battle";
|
||||
public static readonly StringKey NotSketchable = "not_sketchable";
|
||||
public static readonly StringKey Powder = "powder";
|
||||
|
||||
@@ -2320,7 +2320,7 @@
|
||||
"snatch"
|
||||
],
|
||||
"effect": {
|
||||
"name": "change_user_defense",
|
||||
"name": "defense_curl",
|
||||
"parameters": {
|
||||
"amount": 1
|
||||
}
|
||||
@@ -2547,7 +2547,9 @@
|
||||
"priority": 0,
|
||||
"target": "Any",
|
||||
"category": "special",
|
||||
"flags": [],
|
||||
"flags": [
|
||||
"no_redirection"
|
||||
],
|
||||
"effect": {
|
||||
"name": "doom_desire"
|
||||
}
|
||||
@@ -4361,7 +4363,9 @@
|
||||
"priority": 0,
|
||||
"target": "Any",
|
||||
"category": "special",
|
||||
"flags": [],
|
||||
"flags": [
|
||||
"no_redirection"
|
||||
],
|
||||
"effect": {
|
||||
"name": "future_sight"
|
||||
}
|
||||
@@ -5526,8 +5530,7 @@
|
||||
"category": "physical",
|
||||
"flags": [
|
||||
"mirror",
|
||||
"ignore_substitute",
|
||||
"protect"
|
||||
"ignore_substitute"
|
||||
],
|
||||
"effect": {
|
||||
"name": "hyperspace_fury"
|
||||
|
||||
@@ -19,6 +19,6 @@ public class Regenerator : Script, IScriptOnSwitchOut
|
||||
{
|
||||
BatchId = batchId,
|
||||
});
|
||||
oldPokemon.Heal(oldPokemon.MaxHealth / 3, batchId: batchId);
|
||||
oldPokemon.Heal(oldPokemon.MaxHealth / 3, batchId: batchId, forceHeal: true);
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,15 @@ public class FairyLockEffect : Script, IScriptOnEndTurn, IScriptPreventSelfRunAw
|
||||
/// <inheritdoc />
|
||||
public void PreventSelfRunAway(IFleeChoice choice, ref bool prevent)
|
||||
{
|
||||
prevent = true;
|
||||
if (choice.User.Types.All(x => x.Name != "ghost"))
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent)
|
||||
{
|
||||
prevent = true;
|
||||
if (choice.User.Types.All(x => x.Name != "ghost"))
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -3,34 +3,73 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Battle;
|
||||
[Script(ScriptCategory.Battle, "future_sight")]
|
||||
public class FutureSightEffect : Script, IScriptOnEndTurn
|
||||
{
|
||||
private int _turnsLeft = 3;
|
||||
private readonly IMoveChoice _moveChoice;
|
||||
private class PendingStrike
|
||||
{
|
||||
public IMoveChoice MoveChoice { get; }
|
||||
public int Turns { get; set; }
|
||||
|
||||
public PendingStrike(IMoveChoice moveChoice)
|
||||
{
|
||||
MoveChoice = moveChoice;
|
||||
Turns = 3;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<PendingStrike> _strikes = new();
|
||||
|
||||
public FutureSightEffect(IMoveChoice moveChoice)
|
||||
{
|
||||
_moveChoice = moveChoice;
|
||||
AddStrike(moveChoice);
|
||||
}
|
||||
|
||||
public void AddStrike(IMoveChoice moveChoice)
|
||||
{
|
||||
_strikes.Add(new PendingStrike(moveChoice));
|
||||
}
|
||||
|
||||
public bool HasStrikeAt(byte side, byte position)
|
||||
{
|
||||
return _strikes.Exists(x => x.MoveChoice.TargetSide == side && x.MoveChoice.TargetPosition == position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
_turnsLeft -= 1;
|
||||
if (_turnsLeft <= 0)
|
||||
var toRemove = new List<PendingStrike>();
|
||||
foreach (var strike in _strikes)
|
||||
{
|
||||
var target = battle.GetPokemon(_moveChoice.TargetSide, _moveChoice.TargetPosition);
|
||||
if (target is not { IsUsable: true })
|
||||
strike.Turns--;
|
||||
if (strike.Turns == 0)
|
||||
{
|
||||
battle.EventHook.Invoke(new DialogEvent("move_failed"));
|
||||
return;
|
||||
ExecuteStrike(battle, strike.MoveChoice);
|
||||
toRemove.Add(strike);
|
||||
}
|
||||
var damageCalculator = battle.Library.DamageCalculator;
|
||||
var executingMove = new ExecutingMoveImpl([target], 1, _moveChoice.ChosenMove,
|
||||
_moveChoice.ChosenMove.MoveData, _moveChoice, battle);
|
||||
var hitData = executingMove.GetHitData(target, 0);
|
||||
var damage = damageCalculator.GetDamage(executingMove, executingMove.UseMove.Category, executingMove.User,
|
||||
target, executingMove.TargetCount, 1, hitData);
|
||||
|
||||
target.Damage(damage, DamageSource.Misc);
|
||||
}
|
||||
foreach (var strike in toRemove)
|
||||
{
|
||||
_strikes.Remove(strike);
|
||||
}
|
||||
if (_strikes.Count == 0)
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExecuteStrike(IBattle battle, IMoveChoice moveChoice)
|
||||
{
|
||||
var target = battle.GetPokemon(moveChoice.TargetSide, moveChoice.TargetPosition);
|
||||
if (target is not { IsUsable: true })
|
||||
{
|
||||
battle.EventHook.Invoke(new DialogEvent("move_failed"));
|
||||
return;
|
||||
}
|
||||
var damageCalculator = battle.Library.DamageCalculator;
|
||||
var executingMove = new ExecutingMoveImpl([target], 1, moveChoice.ChosenMove, moveChoice.ChosenMove.MoveData,
|
||||
moveChoice, battle);
|
||||
var hitData = executingMove.GetHitData(target, 0);
|
||||
var damage = damageCalculator.GetDamage(executingMove, executingMove.UseMove.Category, executingMove.User,
|
||||
target, executingMove.TargetCount, 1, hitData);
|
||||
|
||||
target.Damage(damage, DamageSource.Misc);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Battle;
|
||||
|
||||
[Script(ScriptCategory.Move, "ion_deluge")]
|
||||
public class IonDelugeEffect : Script, IScriptChangeMoveType
|
||||
public class IonDelugeEffect : Script, IScriptChangeMoveType, IScriptOnEndTurn
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void ChangeMoveType(IExecutingMove move, IPokemon target, byte hit, ref TypeIdentifier? moveType)
|
||||
@@ -12,4 +12,9 @@ public class IonDelugeEffect : Script, IScriptChangeMoveType
|
||||
moveType = electricType;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Battle;
|
||||
[Script(ScriptCategory.Battle, "magic_room")]
|
||||
public class MagicRoomEffect : Script, IScriptOnBeforeAnyHookInvoked, IScriptOnEndTurn, IScriptPreventHeldItemConsume
|
||||
{
|
||||
private int _turnsLeft = 5;
|
||||
private int _turnsLeft = 4;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PreventHeldItemConsume(IPokemon pokemon, IItem heldItem, ref bool prevented)
|
||||
|
||||
@@ -10,7 +10,7 @@ public class MudSportEffect : Script, IScriptChangeBasePower, IScriptOnEndTurn
|
||||
{
|
||||
if (move.UseMove.MoveType.Name == "electric")
|
||||
{
|
||||
basePower = basePower.MultiplyOrMax(2f / 3f);
|
||||
basePower = basePower.MultiplyOrMax(1352 / 4096f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ public abstract class BaseChargeMove<TVolatile> : Script, IScriptPreventMove, IS
|
||||
if (move.User.Volatile.Contains<TVolatile>())
|
||||
return;
|
||||
|
||||
var args = new CustomTriggers.BypassChargeMoveArgs(move, false);
|
||||
move.RunScriptHook<IScriptCustomTrigger>(script => script.CustomTrigger(CustomTriggers.BypassChargeMove, args));
|
||||
if (args.Bypass)
|
||||
return;
|
||||
|
||||
move.User.Volatile.Add(CreateVolatile(move.User));
|
||||
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("began_charging", new Dictionary<string, object>
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ public abstract class ChangeUserStats : Script, IScriptOnInitialize, IScriptOnSe
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
public virtual void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
move.User.ChangeStatBoost(_stat, _amount, true, false);
|
||||
}
|
||||
|
||||
13
Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DefenseCurl.cs
Normal file
13
Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DefenseCurl.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "defense_curl")]
|
||||
public class DefenseCurl : ChangeUserDefense
|
||||
{
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
base.OnSecondaryEffect(move, target, hit);
|
||||
move.User.Volatile.StackOrAdd(ScriptUtils.ResolveName<DefenseCurlEffect>(), () => new DefenseCurlEffect());
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,31 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Status;
|
||||
using PkmnLib.Static.Moves;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "facade")]
|
||||
public class Facade : Script, IScriptChangeBasePower
|
||||
public class Facade : Script, IScriptChangeBasePower, IScriptChangeMoveDamage
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
|
||||
{
|
||||
var status = move.User.StatusScript.Script?.Name;
|
||||
if (status == "paralyzed" || status == "burned" || status == "poisoned")
|
||||
if (status == ScriptUtils.ResolveName<Paralyzed>() || status == ScriptUtils.ResolveName<Burned>() ||
|
||||
status == ScriptUtils.ResolveName<Poisoned>() || status == ScriptUtils.ResolveName<BadlyPoisoned>())
|
||||
{
|
||||
basePower = basePower.MultiplyOrMax(2);
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
|
||||
{
|
||||
var status = move.User.StatusScript.Script?.Name;
|
||||
if (status != ScriptUtils.ResolveName<Burned>())
|
||||
return;
|
||||
// Ignore Burn's effect of halving the damage done by physical moves.
|
||||
if (move.UseMove.Category == MoveCategory.Physical)
|
||||
{
|
||||
damage = (uint)(damage * 2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ public class FalseSwipe : Script, IScriptChangeMoveDamage
|
||||
/// <inheritdoc />
|
||||
public void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
|
||||
{
|
||||
if (target.CurrentHealth - damage < 1)
|
||||
if ((int)target.CurrentHealth - damage < 1)
|
||||
{
|
||||
damage = target.CurrentHealth - 1;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
@@ -8,9 +9,26 @@ public class Feint : Script, IScriptOnBeforeHit
|
||||
/// <inheritdoc />
|
||||
public void OnBeforeHit(IExecutingMove move, IPokemon target, byte hitIndex)
|
||||
{
|
||||
if (target.Volatile.Contains<ProtectionEffectScript>())
|
||||
var toRemove = new List<ScriptContainer>();
|
||||
foreach (var script in target.Volatile)
|
||||
{
|
||||
target.Volatile.Remove<ProtectionEffectScript>();
|
||||
if (script.Script is ProtectionEffectScript)
|
||||
{
|
||||
toRemove.Add(script);
|
||||
}
|
||||
}
|
||||
foreach (var script in toRemove)
|
||||
{
|
||||
script.Clear();
|
||||
}
|
||||
|
||||
var battleData = target.BattleData;
|
||||
if (battleData is not null)
|
||||
{
|
||||
if (battleData.BattleSide.VolatileScripts.Contains<CraftyShieldEffect>())
|
||||
battleData.BattleSide.VolatileScripts.Remove<CraftyShieldEffect>();
|
||||
if (battleData.BattleSide.VolatileScripts.Contains<MatBlockEffect>())
|
||||
battleData.BattleSide.VolatileScripts.Remove<MatBlockEffect>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ public class FellStinger : Script, IScriptOnAfterHits
|
||||
{
|
||||
if (target.IsFainted)
|
||||
{
|
||||
move.User.ChangeStatBoost(Statistic.Attack, 2, true, false);
|
||||
move.User.ChangeStatBoost(Statistic.Attack, 3, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,15 @@ using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "fire_spin")]
|
||||
public class FireSpin : MultiHitMove, IScriptOnSecondaryEffect
|
||||
public class FireSpin : Script, IScriptOnSecondaryEffect
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.Volatile.StackOrAdd("fire_spin", () => new FireSpinEffect(target));
|
||||
target.Volatile.StackOrAdd("fire_spin", () =>
|
||||
{
|
||||
var turns = target.BattleData!.Battle.Random.GetInt(4, 6);
|
||||
return new FireSpinEffect(target, turns, move.User);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ public class Flail : Script, IScriptChangeBasePower
|
||||
/// <inheritdoc />
|
||||
public void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
|
||||
{
|
||||
var remainingHealth = move.User.CurrentHealth / move.User.BoostedStats.Hp;
|
||||
var remainingHealth = move.User.CurrentHealth / (float)move.User.BoostedStats.Hp;
|
||||
var fraction = remainingHealth * 48;
|
||||
basePower = fraction switch
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ public class FlameBurst : Script, IScriptOnSecondaryEffect
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var adjacentFoes = GetAdjacentFoes(move.User).WhereNotNull();
|
||||
var adjacentFoes = GetAdjacentFoes(target).WhereNotNull();
|
||||
EventBatchId batchId = new();
|
||||
foreach (var adjacentFoe in adjacentFoes)
|
||||
{
|
||||
|
||||
@@ -11,18 +11,18 @@ public class FlareBlitz : Script, IScriptOnSecondaryEffect
|
||||
return;
|
||||
|
||||
var hitData = move.GetHitData(target, hit);
|
||||
var recoilDamage = hitData.Damage * (1 / 3);
|
||||
|
||||
var triggerArgs = new CustomTriggers.ModifyRecoilArgs(move, target, hit, hitData.Damage, recoilDamage);
|
||||
move.RunScriptHook<IScriptCustomTrigger>(x => x.CustomTrigger(CustomTriggers.ModifyRecoil, triggerArgs));
|
||||
if (triggerArgs.Prevent)
|
||||
return;
|
||||
var recoilDamage = hitData.Damage * (1 / 3f);
|
||||
|
||||
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
|
||||
{
|
||||
target.SetStatus("burned", move.User);
|
||||
}
|
||||
|
||||
move.User.Damage(recoilDamage, DamageSource.Misc);
|
||||
var triggerArgs = new CustomTriggers.ModifyRecoilArgs(move, target, hit, hitData.Damage, (uint)recoilDamage);
|
||||
move.RunScriptHook<IScriptCustomTrigger>(x => x.CustomTrigger(CustomTriggers.ModifyRecoil, triggerArgs));
|
||||
if (triggerArgs.Prevent)
|
||||
return;
|
||||
|
||||
move.User.Damage((uint)recoilDamage, DamageSource.Misc);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public class Fling : Script, IScriptOnSecondaryEffect, IScriptChangeBasePower
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
if (!item.TryGetAdditionalData<byte>("fling_power", out var flingPower))
|
||||
if (!item.TryGetAdditionalData<byte>("flingPower", out var flingPower))
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "flower_shield")]
|
||||
@@ -14,6 +16,7 @@ public class FlowerShield : Script, IScriptOnSecondaryEffect
|
||||
|
||||
var batchId = new EventBatchId();
|
||||
var sides = battleData.Battle.Sides;
|
||||
var anyFound = false;
|
||||
foreach (var side in sides)
|
||||
{
|
||||
foreach (var pokemon in side.Pokemon)
|
||||
@@ -22,8 +25,13 @@ public class FlowerShield : Script, IScriptOnSecondaryEffect
|
||||
continue;
|
||||
if (!pokemon.Types.Contains(grassType))
|
||||
continue;
|
||||
if (pokemon.Volatile.Any(x => x.Script?.IsSemiInvulnerableTurn == true))
|
||||
continue;
|
||||
pokemon.ChangeStatBoost(Statistic.Defense, 1, pokemon == move.User, false, batchId);
|
||||
anyFound = true;
|
||||
}
|
||||
}
|
||||
if (!anyFound)
|
||||
move.GetHitData(target, hit).Fail();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,11 @@ public class FocusEnergy : Script, IScriptOnSecondaryEffect
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.Volatile.Add(new Pokemon.IncreasedCriticalStage());
|
||||
if (target.Volatile.Contains<Pokemon.FocusEnergyEffect>())
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
target.Volatile.Add(new Pokemon.FocusEnergyEffect());
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,13 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "follow_me")]
|
||||
public class FollowMe : Script, IScriptChangeTargets
|
||||
public class FollowMe : Script, IScriptOnSecondaryEffect
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void ChangeTargets(IMoveChoice moveChoice, ref IReadOnlyList<IPokemon?> targets)
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
if (targets.Count != 1)
|
||||
return;
|
||||
|
||||
var target = targets[0];
|
||||
if (target == null)
|
||||
return;
|
||||
if (target.BattleData?.SideIndex != moveChoice.User.BattleData?.SideIndex)
|
||||
return;
|
||||
|
||||
targets = [moveChoice.User];
|
||||
move.User.BattleData!.BattleSide.VolatileScripts.StackOrAdd(ScriptUtils.ResolveName<FollowMeEffect>(),
|
||||
() => new FollowMeEffect(move.User));
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,15 @@ public class Foresight : Script, IScriptOnSecondaryEffect
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
if (target.StatBoost.Evasion > 0)
|
||||
target.ChangeStatBoost(Statistic.Evasion, (sbyte)-target.StatBoost.Evasion, false, false);
|
||||
|
||||
if (target.Volatile.Contains<ForesightEffect>())
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
var typeLibrary = battleData.Battle.Library.StaticLibrary.Types;
|
||||
target.Volatile.Add(new ForesightEffect(typeLibrary));
|
||||
target.ChangeStatBoost(Statistic.Evasion, (sbyte)-target.StatBoost.Evasion, false, false);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "forests_curse")]
|
||||
@@ -10,8 +12,18 @@ public class ForestsCurse : Script, IScriptOnSecondaryEffect
|
||||
if (battleData == null)
|
||||
return;
|
||||
var typeLibrary = battleData.Battle.Library.StaticLibrary.Types;
|
||||
if (!typeLibrary.TryGetTypeIdentifier("grass", out var grassType))
|
||||
if (!typeLibrary.TryGetTypeIdentifier("grass", out var grassType) || target.Types.Contains(grassType))
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
if (target.Volatile.TryGet<HasHadTypeAddedEffect>(out var effect))
|
||||
{
|
||||
target.RemoveType(effect.TypeIdentifier);
|
||||
target.Volatile.Remove<HasHadTypeAddedEffect>();
|
||||
}
|
||||
|
||||
target.AddType(grassType);
|
||||
target.Volatile.Add(new HasHadTypeAddedEffect(grassType));
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,11 @@ public class FreezeDry : Script, IScriptChangeEffectiveness, IScriptOnSecondaryE
|
||||
if (!typeLibrary.TryGetTypeIdentifier("water", out var waterType))
|
||||
return;
|
||||
|
||||
if (target.Types.Contains(waterType))
|
||||
var hitDataType = move.GetHitData(target, hit).Type;
|
||||
if (target.Types.Contains(waterType) && hitDataType.HasValue)
|
||||
{
|
||||
var effectivenessWithoutWater = target.Types.Where(x => x != waterType)
|
||||
.Select(x => typeLibrary.GetEffectiveness(x, target.Types)).Aggregate(1f, (a, b) => a * b);
|
||||
.Select(x => typeLibrary.GetSingleEffectiveness(hitDataType.Value, x)).Aggregate(1f, (a, b) => a * b);
|
||||
effectiveness = effectivenessWithoutWater * 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class FuryCutter : Script, IScriptChangeBasePower
|
||||
return;
|
||||
}
|
||||
|
||||
if (userEffect.TurnCount < 5)
|
||||
if (userEffect.TurnCount < 2)
|
||||
userEffect.TurnCount++;
|
||||
basePower = basePower.MultiplyOrMax((byte)Math.Pow(2, userEffect.TurnCount));
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ public class FusionBolt : Script, IScriptChangeDamageModifier
|
||||
|
||||
// Grab the choices for the current turn, that have been executed before this move.
|
||||
var choice = battleData.Battle.PreviousTurnChoices.Last().TakeWhile(x => !Equals(x, move.MoveChoice))
|
||||
// Of these, find the move choice that used Fusion Flare.
|
||||
.OfType<MoveChoice>().FirstOrDefault(x => x.ChosenMove.MoveData.Name == "fusion_flare");
|
||||
// Of these, find the last successfully executed move. Failed moves don't break the combination.
|
||||
.OfType<MoveChoice>().LastOrDefault(x => !x.HasFailed);
|
||||
|
||||
// If Fusion Flare was used, Fusion Bolt's power is doubled.
|
||||
if (choice != null)
|
||||
// If Fusion Flare was used immediately preceding, Fusion Bolt's power is doubled.
|
||||
if (choice is not null && choice.ChosenMove.MoveData.Name == "fusion_flare")
|
||||
{
|
||||
modifier *= 2;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ public class FusionFlare : Script, IScriptChangeDamageModifier
|
||||
|
||||
// Grab the choices for the current turn, that have been executed before this move.
|
||||
var choice = battleData.Battle.PreviousTurnChoices.Last().TakeWhile(x => !Equals(x, move.MoveChoice))
|
||||
// Of these, find the move choice that used Fusion Bolt.
|
||||
.OfType<MoveChoice>().FirstOrDefault(x => x.ChosenMove.MoveData.Name == "fusion_bolt");
|
||||
// Of these, find the last successfully executed move. Failed moves don't break the combination.
|
||||
.OfType<MoveChoice>().LastOrDefault(x => !x.HasFailed);
|
||||
|
||||
// If Fusion Bolt was used, Fusion Flare's power is doubled.
|
||||
if (choice != null)
|
||||
// If Fusion Bolt was used immediately preceding, Fusion Flare's power is doubled.
|
||||
if (choice is not null && choice.ChosenMove.MoveData.Name == "fusion_bolt")
|
||||
{
|
||||
modifier *= 2;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Battle;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
@@ -11,8 +12,28 @@ public class FutureSight : Script, IScriptStopBeforeMove
|
||||
var battleData = move.User.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
battleData.Battle.Volatile.Add(new FutureSightEffect(move.MoveChoice));
|
||||
var battle = battleData.Battle;
|
||||
var moveChoice = move.MoveChoice;
|
||||
|
||||
prevent = true;
|
||||
|
||||
if (IsTargetAlreadySetToBeHit(battle, moveChoice.TargetSide, moveChoice.TargetPosition))
|
||||
{
|
||||
moveChoice.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
if (battle.Volatile.TryGet<FutureSightEffect>(out var effect))
|
||||
effect.AddStrike(moveChoice);
|
||||
else
|
||||
battle.Volatile.Add(new FutureSightEffect(moveChoice));
|
||||
}
|
||||
|
||||
private static bool IsTargetAlreadySetToBeHit(IBattle battle, byte side, byte position)
|
||||
{
|
||||
if (battle.Volatile.TryGet<FutureSightEffect>(out var futureSight) && futureSight.HasStrikeAt(side, position))
|
||||
return true;
|
||||
var doomDesire = battle.Sides[side].VolatileScripts.Get<DoomDesireEffect>();
|
||||
return doomDesire != null && doomDesire.HasTarget(position);
|
||||
}
|
||||
}
|
||||
@@ -23,17 +23,15 @@ public class Gravity : Script, IScriptOnSecondaryEffect
|
||||
var flyEffect = ScriptUtils.ResolveName<ChargeFlyEffect>();
|
||||
var skyDropEffect = ScriptUtils.ResolveName<ChargeSkyDropEffect>();
|
||||
var telekinesisEffect = ScriptUtils.ResolveName<TelekinesisEffect>();
|
||||
var magnetRiseEffect = ScriptUtils.ResolveName<MagnetRiseEffect>();
|
||||
|
||||
foreach (var pokemon in battleData.Battle.Sides.SelectMany(x => x.Pokemon).WhereNotNull())
|
||||
{
|
||||
if (pokemon.Volatile.Contains(chargeBounceEffect))
|
||||
pokemon.Volatile.Remove(chargeBounceEffect);
|
||||
if (pokemon.Volatile.Contains(flyEffect))
|
||||
pokemon.Volatile.Remove(flyEffect);
|
||||
if (pokemon.Volatile.Contains(skyDropEffect))
|
||||
pokemon.Volatile.Remove(skyDropEffect);
|
||||
if (pokemon.Volatile.Contains(telekinesisEffect))
|
||||
pokemon.Volatile.Remove(telekinesisEffect);
|
||||
pokemon.Volatile.Remove(chargeBounceEffect);
|
||||
pokemon.Volatile.Remove(flyEffect);
|
||||
pokemon.Volatile.Remove(skyDropEffect);
|
||||
pokemon.Volatile.Remove(telekinesisEffect);
|
||||
pokemon.Volatile.Remove(magnetRiseEffect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ public class GuardianOfAlola : Script, IScriptChangeMoveDamage
|
||||
/// <inheritdoc />
|
||||
public void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
|
||||
{
|
||||
var maxHp = target.BoostedStats.Hp;
|
||||
damage = (uint)(maxHp * (3f / 4f));
|
||||
damage = (uint)Math.Max(target.CurrentHealth * (3f / 4f), 1);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,11 @@ public class GyroBall : Script, IScriptChangeBasePower
|
||||
/// <inheritdoc />
|
||||
public void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
|
||||
{
|
||||
basePower = Math.Min((byte)150, (byte)(25 * target.BoostedStats.Speed / move.User.BoostedStats.Speed + 1));
|
||||
if (move.User.BoostedStats.Speed == 0)
|
||||
{
|
||||
basePower = 1;
|
||||
return;
|
||||
}
|
||||
basePower = (ushort)Math.Min(150, 25 * target.BoostedStats.Speed / move.User.BoostedStats.Speed + 1);
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,9 @@ public class HealBell : Script, IScriptOnSecondaryEffect
|
||||
|
||||
foreach (var pokemon in party.Party.WhereNotNull())
|
||||
{
|
||||
if (pokemon.BattleData?.IsOnBattlefield == true && pokemon.ActiveAbility?.Name == "soundproof")
|
||||
continue;
|
||||
pokemon.ClearStatus();
|
||||
var confusion = ScriptUtils.ResolveName<Confusion>();
|
||||
if (pokemon.Volatile.Contains(confusion))
|
||||
pokemon.Volatile.Remove(confusion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,14 @@ public class HealingWish : Script, IScriptOnSecondaryEffect
|
||||
if (battleData == null)
|
||||
return;
|
||||
|
||||
var responsibleIndex = new ResponsibleIndex(battleData.SideIndex, battleData.Position);
|
||||
var party = battleData.Battle.Parties.FirstOrDefault(x => x.IsResponsibleForIndex(responsibleIndex));
|
||||
if (party == null || !party.HasUsablePokemonNotInField())
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var side = battleData.Battle.Sides[battleData.SideIndex];
|
||||
side.VolatileScripts.Add(new HealingWishEffect(battleData.Position));
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ public class HeartSwap : Script, IScriptOnSecondaryEffect
|
||||
var targetStat = targetStats.GetStatistic(stat);
|
||||
if (userStat == targetStat)
|
||||
continue;
|
||||
move.User.ChangeStatBoost(stat, (sbyte)(userStat - targetStat), true, true, eventBatchId);
|
||||
target.ChangeStatBoost(stat, (sbyte)(targetStat - userStat), false, true, eventBatchId);
|
||||
move.User.ChangeStatBoost(stat, (sbyte)(targetStat - userStat), true, true, eventBatchId);
|
||||
target.ChangeStatBoost(stat, (sbyte)(userStat - targetStat), false, true, eventBatchId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,10 @@ public class HeatCrash : Script, IScriptChangeBasePower
|
||||
var weightMultiplier = move.User.WeightInKg / target.WeightInKg;
|
||||
basePower = weightMultiplier switch
|
||||
{
|
||||
> 5 => 120,
|
||||
> 4 => 100,
|
||||
> 3 => 80,
|
||||
> 2 => 60,
|
||||
>= 5 => 120,
|
||||
>= 4 => 100,
|
||||
>= 3 => 80,
|
||||
>= 2 => 60,
|
||||
_ => 40,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ public class Hex : Script, IScriptChangeBasePower
|
||||
/// <inheritdoc />
|
||||
public void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
|
||||
{
|
||||
if (!target.StatusScript.IsEmpty)
|
||||
if (!target.StatusScript.IsEmpty || target.ActiveAbility?.Name == "comatose")
|
||||
{
|
||||
basePower = basePower.MultiplyOrMax(2);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "hidden_power")]
|
||||
public class HiddenPower : Script, IScriptChangeMoveType, IScriptChangeBasePower
|
||||
public class HiddenPower : Script, IScriptChangeMoveType
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void ChangeMoveType(IExecutingMove move, IPokemon target, byte hit, ref TypeIdentifier? moveType)
|
||||
@@ -14,16 +14,6 @@ public class HiddenPower : Script, IScriptChangeMoveType, IScriptChangeBasePower
|
||||
moveType = t;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
|
||||
{
|
||||
var ivs = move.User.IndividualValues;
|
||||
|
||||
var power = GetHiddenPowerValue(ivs, 0x00000002) * 40 / 63 + 30;
|
||||
// cast to byte with overflow check
|
||||
basePower = (byte)Math.Min(power, byte.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to calculate the hidden power value from the IVs.
|
||||
/// This is used to determine the type and power of the move.
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "high_jump_kick")]
|
||||
public class HighJumpKick : Script, IScriptOnMoveMiss
|
||||
public class HighJumpKick : Script, IScriptOnAfterHits
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void OnMoveMiss(IExecutingMove move, IPokemon target)
|
||||
public void OnAfterHits(IExecutingMove move, IPokemon target)
|
||||
{
|
||||
var damage = move.Hits.Where(h => h.HasExecuted).Sum(h => h.Damage);
|
||||
if (damage == 0)
|
||||
{
|
||||
DoRecoil(move);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DoRecoil(IExecutingMove move)
|
||||
{
|
||||
var damage = move.GetHitData(target, 0).Damage;
|
||||
var recoil = damage / 2;
|
||||
// This recoil damage will not exceed half the user's max HP
|
||||
var maxHp = move.User.BoostedStats.Hp;
|
||||
recoil = Math.Min(recoil, maxHp / 2);
|
||||
var recoil = maxHp / 2;
|
||||
if (recoil == 0)
|
||||
return;
|
||||
move.User.Damage(recoil, DamageSource.Misc);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "hyperspace_fury")]
|
||||
public class HyperspaceFury : Script, IScriptOnSecondaryEffect
|
||||
public class HyperspaceFury : Script, IScriptFailMove, IScriptOnSecondaryEffect
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
@@ -13,5 +14,21 @@ public class HyperspaceFury : Script, IScriptOnSecondaryEffect
|
||||
{
|
||||
target.Volatile.Remove(protectionScript.Name);
|
||||
}
|
||||
target.BattleData?.BattleSide.VolatileScripts.Remove(ScriptUtils.ResolveName<CraftyShieldEffect>());
|
||||
target.BattleData?.BattleSide.VolatileScripts.Remove(ScriptUtils.ResolveName<MatBlockEffect>());
|
||||
target.BattleData?.BattleSide.VolatileScripts.Remove(ScriptUtils.ResolveName<QuickGuardEffect>());
|
||||
target.BattleData?.BattleSide.VolatileScripts.Remove(ScriptUtils.ResolveName<WideGuardEffect>());
|
||||
move.User.ChangeStatBoost(Statistic.Defense, -1, true, false);
|
||||
}
|
||||
|
||||
private static StringKey HoopaName = "hoopa";
|
||||
private static StringKey UnboundName = "unbound";
|
||||
|
||||
public void FailMove(IExecutingMove move, ref bool fail)
|
||||
{
|
||||
if (move.User.Species.Name != HoopaName || move.User.Form.Name != UnboundName)
|
||||
{
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,5 @@ public class IceBall : Script, IScriptOnSecondaryEffect, IScriptChangeBasePower
|
||||
userEffect = new IceBallEffect(move.User, move.UseMove.Name);
|
||||
move.User.Volatile.Add(userEffect);
|
||||
}
|
||||
else
|
||||
{
|
||||
userEffect.TurnCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,23 @@ public class IceFang : Script, IScriptOnSecondaryEffect
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var battleData = move.User.BattleData;
|
||||
var battleData = target.BattleData;
|
||||
if (battleData == null)
|
||||
return;
|
||||
|
||||
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
|
||||
var random = battleData.Battle.Random;
|
||||
if (random.EffectChance(10, move, target, hit))
|
||||
{
|
||||
target.SetStatus("frozen", move.User);
|
||||
}
|
||||
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
|
||||
|
||||
// It also has an independent 10% chance of causing the target to flinch, if the user attacks before the target.
|
||||
var choiceQueue = battleData.Battle.ChoiceQueue;
|
||||
if (choiceQueue?.FirstOrDefault(x => x.User == target) != null)
|
||||
{
|
||||
target.Volatile.Add(new FlinchEffect());
|
||||
if (random.EffectChance(10, move, target, hit))
|
||||
{
|
||||
target.Volatile.Add(new FlinchEffect());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,6 @@ public class Imprison : Script, IScriptOnSecondaryEffect
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.Volatile.Add(new ImprisonEffect(move.User));
|
||||
target.BattleData?.Battle.Volatile.Add(new ImprisonEffect(move.User));
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,18 @@ public class Incinerate : Script, IScriptOnSecondaryEffect
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
if (target.HeldItem is not { Category: ItemCategory.Berry } || !target.TryStealHeldItem(out _))
|
||||
var isValidItem = target.HeldItem is { Category: ItemCategory.Berry } ||
|
||||
(target.HeldItem?.Name.ToString().EndsWith("_gem") == true && target.HeldItem is
|
||||
{ Category: ItemCategory.MiscItem });
|
||||
|
||||
if (!isValidItem || !target.TryStealHeldItem(out _))
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
move.Battle.EventHook.Invoke(new DialogEvent("item_incinerated", new Dictionary<string, object>
|
||||
{
|
||||
{ "pokemon", target },
|
||||
{ "item", target.HeldItem },
|
||||
{ "item", target.HeldItem! },
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,16 @@ public class Infestation : Script, IScriptOnSecondaryEffect
|
||||
{
|
||||
turns = 5;
|
||||
}
|
||||
target.Volatile.Add(new InfestationEffect(target, turns));
|
||||
|
||||
var args = new CustomTriggers.ModifyBindArgs(move)
|
||||
{
|
||||
Duration = turns,
|
||||
DamagePercent = 1 / 8f,
|
||||
};
|
||||
move.User.RunScriptHook<IScriptCustomTrigger>(x => x.CustomTrigger(CustomTriggers.ModifyBind, args));
|
||||
var bindTurns = args.Duration;
|
||||
var bindDamage = args.DamagePercent;
|
||||
|
||||
target.Volatile.Add(new InfestationEffect(target, bindTurns, bindDamage));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using PkmnLib.Dynamic.BattleFlow;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
@@ -16,7 +17,8 @@ public class Instruct : Script, IScriptOnSecondaryEffect
|
||||
|
||||
var lastMoveChoiceByTarget = target.BattleData?.LastMoveChoice;
|
||||
|
||||
if (lastMoveChoiceByTarget == null || !battleData.Battle.CanUse(lastMoveChoiceByTarget))
|
||||
if (lastMoveChoiceByTarget == null || !battleData.Battle.CanUse(lastMoveChoiceByTarget) ||
|
||||
!lastMoveChoiceByTarget.ChosenMove.MoveData.CanCopyMove())
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Battle;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "judgement")]
|
||||
@@ -6,6 +8,12 @@ public class Judgement : Script, IScriptChangeMoveType
|
||||
/// <inheritdoc />
|
||||
public void ChangeMoveType(IExecutingMove move, IPokemon target, byte hit, ref TypeIdentifier? moveType)
|
||||
{
|
||||
//If Magic Room is in effect, Judgment's type will always be Normal regardless of the Plate held
|
||||
if (move.Battle.Volatile.Contains<MagicRoomEffect>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var heldItem = move.User.HeldItem;
|
||||
if (heldItem == null)
|
||||
return;
|
||||
|
||||
@@ -3,10 +3,16 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
[Script(ScriptCategory.Move, "kings_shield")]
|
||||
public class KingsShield : ProtectionScript
|
||||
{
|
||||
protected override Script GetEffectScript() => new Pokemon.KingsShieldEffect();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
base.OnSecondaryEffect(move, target, hit);
|
||||
|
||||
// Only change Aegislash form if the move has not failed
|
||||
if (move.GetHitData(target, hit).HasFailed)
|
||||
return;
|
||||
// Default form is shield form
|
||||
if (move.User.Species.Name == "aegislash" && move.User.Form.Name != "default")
|
||||
{
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "knock_off")]
|
||||
public class KnockOff : Script, IScriptOnSecondaryEffect
|
||||
public class KnockOff : Script, IScriptOnSecondaryEffect, IScriptChangeBasePower
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
if (!target.TryStealHeldItem(out _))
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
}
|
||||
if (move.User.IsFainted)
|
||||
return;
|
||||
target.TryStealHeldItem(out _);
|
||||
}
|
||||
|
||||
public void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
|
||||
{
|
||||
if (target.HeldItem == null || target.HeldItem.Category == ItemCategory.FormChanger)
|
||||
return;
|
||||
basePower = basePower.MultiplyOrMax(1.5f);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public class LastResort : Script, IScriptPreventMoveSelection
|
||||
}
|
||||
|
||||
// Grab all move choices
|
||||
var movesForUserSinceEnteringField = battleData.Battle.PreviousTurnChoices
|
||||
var movesForUserSinceEnteringField = battleData.Battle.PreviousTurnChoices.Skip((int)battleData.SwitchInTurn)
|
||||
// Reading backwards
|
||||
.Reverse().SelectMany(x => x.Reverse())
|
||||
// We only care about move choices since the user entered the field
|
||||
@@ -29,7 +29,7 @@ public class LastResort : Script, IScriptPreventMoveSelection
|
||||
.Where(x => x.User == choice.User)
|
||||
// Grab the chosen move, and remove duplicates
|
||||
.Select(x => x.ChosenMove).Distinct().ToList();
|
||||
if (!userMoves.All(x => movesForUserSinceEnteringField.Contains(x)))
|
||||
if (!userMoves.All(movesForUserSinceEnteringField.Contains))
|
||||
{
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ public class LockOn : Script, IScriptOnSecondaryEffect
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.Volatile.Add(new LockOnEffect(target));
|
||||
target.Volatile.Add(new LockOnEffect(move.User));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
@@ -8,6 +9,6 @@ public class LuckyChant : Script, IScriptOnSecondaryEffect
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.Volatile.Add(new LuckyChantEffect());
|
||||
target.BattleData?.BattleSide.VolatileScripts.Add(new LuckyChantEffect());
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,9 @@ public class LunarDance : Script, IScriptOnSecondaryEffect
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var battleData = move.User.BattleData;
|
||||
battleData?.BattleSide.VolatileScripts.Add(new LunarDanceEffect(battleData.Position));
|
||||
if (battleData is null)
|
||||
return;
|
||||
battleData.BattleSide.VolatileScripts.Add(new LunarDanceEffect(battleData.Position));
|
||||
move.User.Faint(DamageSource.Misc);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,13 @@ public class MagicRoom : Script, IScriptOnSecondaryEffect
|
||||
return;
|
||||
|
||||
var battle = battleData.Battle;
|
||||
battle.Volatile.Add(new MagicRoomEffect());
|
||||
if (battle.Volatile.Contains<MagicRoomEffect>())
|
||||
{
|
||||
battle.Volatile.Remove<MagicRoomEffect>();
|
||||
}
|
||||
else
|
||||
{
|
||||
battle.Volatile.Add(new MagicRoomEffect());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,17 @@ using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "magma_storm")]
|
||||
public class MagmaStorm : MultiHitMove, IScriptOnSecondaryEffect
|
||||
public class MagmaStorm : Script, IScriptOnSecondaryEffect
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
target.Volatile.StackOrAdd("magma_storm", () => new MagmaStormEffect(target));
|
||||
var args = new CustomTriggers.ModifyBindArgs(move);
|
||||
move.User.RunScriptHook<IScriptCustomTrigger>(x => x.CustomTrigger(CustomTriggers.ModifyBind, args));
|
||||
|
||||
var bindTurns = args.Duration;
|
||||
var bindDamage = args.DamagePercent;
|
||||
|
||||
target.Volatile.StackOrAdd("magma_storm", () => new MagmaStormEffect(target, bindTurns, bindDamage));
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ public class MagnetRise : Script, IScriptOnSecondaryEffect
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
if (move.User.Volatile.Contains(ScriptUtils.ResolveName<IngrainEffect>()) ||
|
||||
move.User.ActiveAbility?.Name == "levitate")
|
||||
move.User.ActiveAbility?.Name == "levitate" || move.User.Volatile.Contains<MagnetRiseEffect>())
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.MoveVolatile;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Utils;
|
||||
using PkmnLib.Static.Moves;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
@@ -18,12 +20,16 @@ public class MeFirst : Script, IScriptChangeMove
|
||||
choice.Fail();
|
||||
return;
|
||||
}
|
||||
if (battleData.Battle.ChoiceQueue?.FirstOrDefault(x => x.User == target) is not IMoveChoice targetMove)
|
||||
if (battleData.Battle.ChoiceQueue?.FirstOrDefault(x => x.User == target) is not IMoveChoice
|
||||
{
|
||||
ChosenMove.MoveData.Category: MoveCategory.Physical or MoveCategory.Special,
|
||||
} targetMove)
|
||||
{
|
||||
choice.Fail();
|
||||
return;
|
||||
}
|
||||
if (battleData.Battle.Library.MiscLibrary.IsReplacementChoice(targetMove))
|
||||
if (!targetMove.ChosenMove.MoveData.CanCopyMove() || targetMove.ChosenMove.MoveData.Name == "metal_burst" ||
|
||||
battleData.Battle.Library.MiscLibrary.IsReplacementChoice(targetMove))
|
||||
{
|
||||
choice.Fail();
|
||||
return;
|
||||
|
||||
@@ -5,9 +5,13 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
[Script(ScriptCategory.Move, "mean_look")]
|
||||
public class MeanLook : Script, IScriptOnSecondaryEffect
|
||||
{
|
||||
private static StringKey GhostTypeName => new("ghost");
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
if (target.Types.Any(x => x.Name == GhostTypeName))
|
||||
return;
|
||||
var targetEffect = target.Volatile.Add(new MeanLookEffectTarget());
|
||||
if (targetEffect == null)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using PkmnLib.Static.Moves;
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "metal_burst")]
|
||||
public class MetalBurst : Script, IScriptOnBeforeTurnStart, IScriptChangeMoveDamage
|
||||
public class MetalBurst : Script, IScriptOnBeforeTurnStart, IScriptChangeTargets, IScriptChangeMoveDamage
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void OnBeforeTurnStart(ITurnChoice choice)
|
||||
@@ -11,19 +11,31 @@ public class MetalBurst : Script, IScriptOnBeforeTurnStart, IScriptChangeMoveDam
|
||||
choice.User.Volatile.Add(new MetalBurstHelper());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeTargets(IMoveChoice moveChoice, ref IReadOnlyList<IPokemon?> targets)
|
||||
{
|
||||
var helper = moveChoice.User.Volatile.Get<MetalBurstHelper>();
|
||||
var lastAttacker = helper?.LastAttacker;
|
||||
if (lastAttacker == null)
|
||||
{
|
||||
moveChoice.Fail();
|
||||
return;
|
||||
}
|
||||
targets = [lastAttacker];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
|
||||
{
|
||||
var helper = target.Volatile.Get<MetalBurstHelper>();
|
||||
var helper = move.User.Volatile.Get<MetalBurstHelper>();
|
||||
|
||||
if (helper?.LastAttacker == null || helper.LastAttacker != move.User)
|
||||
if (helper?.LastAttacker == null || helper.LastAttacker != target)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
damage = helper.LastDamage.MultiplyOrMax(1.5f);
|
||||
target.Volatile.Remove<MetalBurstHelper>();
|
||||
}
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "metal_burst_helper")]
|
||||
|
||||
@@ -18,13 +18,13 @@ public class Metronome : Script, IScriptChangeMove
|
||||
var retryCount = 0;
|
||||
while (true)
|
||||
{
|
||||
// Breaker for infinite loop. The odds that we'd accidentally roll a non-copyable move 100 times in a row
|
||||
// is extremely unlikely, unless the loaded move library only contains non-copyable moves.
|
||||
// Breaker for infinite loop. The odds that we'd accidentally roll an unselectable move 100 times in a row
|
||||
// is extremely unlikely, unless the loaded move library only contains unselectable moves.
|
||||
// This is a failsafe.
|
||||
if (retryCount > 100)
|
||||
throw new Exception("Metronome failed to find a valid move after 100 attempts.");
|
||||
var randomMove = moveLibrary.GetRandom(battleData.Battle.Random);
|
||||
if (!randomMove.CanCopyMove())
|
||||
if (!randomMove.CanBeSelectedByMetronome())
|
||||
{
|
||||
retryCount++;
|
||||
continue;
|
||||
|
||||
@@ -8,6 +8,12 @@ public class MiracleEye : Script, IScriptOnSecondaryEffect
|
||||
/// <inheritdoc />
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
if (target.Volatile.Contains<MiracleEyeEffect>())
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.StatBoost.Evasion > 0)
|
||||
{
|
||||
target.ChangeStatBoost(Statistic.Evasion, (sbyte)-target.StatBoost.Evasion, false, false);
|
||||
|
||||
@@ -3,7 +3,7 @@ using PkmnLib.Static.Moves;
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "mirror_coat")]
|
||||
public class MirrorCoat : Script, IScriptOnBeforeTurnStart, IScriptChangeMoveDamage
|
||||
public class MirrorCoat : Script, IScriptOnBeforeTurnStart, IScriptChangeTargets, IScriptChangeMoveDamage
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void OnBeforeTurnStart(ITurnChoice choice)
|
||||
@@ -11,19 +11,31 @@ public class MirrorCoat : Script, IScriptOnBeforeTurnStart, IScriptChangeMoveDam
|
||||
choice.User.Volatile.Add(new MirrorCoatHelper());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeTargets(IMoveChoice moveChoice, ref IReadOnlyList<IPokemon?> targets)
|
||||
{
|
||||
var helper = moveChoice.User.Volatile.Get<MirrorCoatHelper>();
|
||||
var lastAttacker = helper?.LastAttacker;
|
||||
if (lastAttacker == null)
|
||||
{
|
||||
moveChoice.Fail();
|
||||
return;
|
||||
}
|
||||
targets = [lastAttacker];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
|
||||
{
|
||||
var helper = target.Volatile.Get<MirrorCoatHelper>();
|
||||
var helper = move.User.Volatile.Get<MirrorCoatHelper>();
|
||||
|
||||
if (helper?.LastAttacker == null || helper.LastAttacker != move.User)
|
||||
if (helper?.LastAttacker == null || helper.LastAttacker != target)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
damage = helper.LastDamage.MultiplyOrMax(2f);
|
||||
target.Volatile.Remove<MirrorCoatHelper>();
|
||||
damage = Math.Max(helper.LastDamage.MultiplyOrMax(2f), 1);
|
||||
}
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "mirror_coat_helper")]
|
||||
|
||||
@@ -16,10 +16,14 @@ public class MirrorMove : Script, IScriptChangeMove
|
||||
if (battle.ChoiceQueue == null)
|
||||
return;
|
||||
var currentTurn = battle.ChoiceQueue.LastRanChoice;
|
||||
var target = battle.GetPokemon(choice.TargetSide, choice.TargetPosition);
|
||||
if (target is null)
|
||||
{
|
||||
choice.Fail();
|
||||
return;
|
||||
}
|
||||
var lastMove = battle.PreviousTurnChoices.SelectMany(x => x).OfType<IMoveChoice>()
|
||||
.TakeWhile(x => !Equals(x, currentTurn)).LastOrDefault(x => x.TargetPosition == choice.TargetPosition &&
|
||||
x.TargetSide == choice.TargetSide &&
|
||||
x.User.BattleData?.IsOnBattlefield == true);
|
||||
.TakeWhile(x => !Equals(x, currentTurn)).LastOrDefault(x => x.User == target);
|
||||
if (lastMove == null || !lastMove.ChosenMove.MoveData.CanCopyMove())
|
||||
{
|
||||
choice.Fail();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Battle;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "multi_attack")]
|
||||
@@ -6,6 +8,11 @@ public class MultiAttack : Script, IScriptChangeMoveType
|
||||
/// <inheritdoc />
|
||||
public void ChangeMoveType(IExecutingMove move, IPokemon target, byte hit, ref TypeIdentifier? moveType)
|
||||
{
|
||||
if (move.User.ActiveAbility?.Name == "klutz")
|
||||
return;
|
||||
if (move.Battle.Volatile.Contains<MagicRoomEffect>())
|
||||
return;
|
||||
|
||||
var item = move.User.HeldItem?.Name.ToString();
|
||||
var typeLibrary = move.User.Library.StaticLibrary.Types;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
[Script(ScriptCategory.Move, "trick_or_treat")]
|
||||
@@ -7,8 +9,18 @@ public class TrickOrTreat : Script, IScriptOnSecondaryEffect
|
||||
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
var library = move.Battle.Library.StaticLibrary.Types;
|
||||
if (!library.TryGetTypeIdentifier("ghost", out var ghostType))
|
||||
if (!library.TryGetTypeIdentifier("ghost", out var ghostType) || target.Types.Contains(ghostType))
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
if (target.Volatile.TryGet<HasHadTypeAddedEffect>(out var effect))
|
||||
{
|
||||
target.RemoveType(effect.TypeIdentifier);
|
||||
target.Volatile.Remove<HasHadTypeAddedEffect>();
|
||||
}
|
||||
|
||||
target.AddType(ghostType);
|
||||
target.Volatile.Add(new HasHadTypeAddedEffect(ghostType));
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@ public class ChargeBounceEffect : Script, IScriptForceTurnSelection, IScriptChan
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsSemiInvulnerableTurn => true;
|
||||
|
||||
public ChargeBounceEffect(IPokemon owner)
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
@@ -8,6 +8,9 @@ public class ChargeFlyEffect : Script, IScriptForceTurnSelection, IScriptChangeI
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsSemiInvulnerableTurn => true;
|
||||
|
||||
public ChargeFlyEffect(IPokemon owner)
|
||||
{
|
||||
_owner = owner;
|
||||
@@ -30,7 +33,7 @@ public class ChargeFlyEffect : Script, IScriptForceTurnSelection, IScriptChangeI
|
||||
/// <inheritdoc />
|
||||
public void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
|
||||
{
|
||||
if (!move.UseMove.HasFlag(MoveFlags.EffectiveAgainstFly))
|
||||
if (move.UseMove.HasFlag(MoveFlags.EffectiveAgainstFly))
|
||||
damage *= 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ public class ChargeSkyDropEffect : Script, IScriptForceTurnSelection, IScriptCha
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsSemiInvulnerableTurn => true;
|
||||
|
||||
public ChargeSkyDropEffect(IPokemon owner)
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
@@ -73,6 +73,9 @@ public class Confusion : Script, IScriptStopBeforeMove
|
||||
/// <inheritdoc />
|
||||
public uint Damage { get; } = 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasExecuted => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public TypeIdentifier? Type => null;
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "defense_curl")]
|
||||
public class DefenseCurlEffect : Script, IScriptChangeBasePower
|
||||
{
|
||||
private static StringKey RolloutName = "rollout";
|
||||
private static StringKey IceBallName = "ice_ball";
|
||||
|
||||
public void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
|
||||
{
|
||||
if (move.UseMove.Name == RolloutName || move.UseMove.Name == IceBallName)
|
||||
basePower = basePower.MultiplyOrMax(2);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@ public class DigEffect : Script, IScriptForceTurnSelection, IScriptChangeIncomin
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsSemiInvulnerableTurn => true;
|
||||
|
||||
public DigEffect(IPokemon owner)
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
@@ -8,6 +8,9 @@ public class DiveEffect : Script, IScriptForceTurnSelection, IScriptChangeIncomi
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsSemiInvulnerableTurn => true;
|
||||
|
||||
public DiveEffect(IPokemon owner)
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
@@ -5,27 +5,42 @@ public class FireSpinEffect : Script, IScriptOnEndTurn, IScriptPreventSelfRunAwa
|
||||
IAIInfoScriptExpectedEndOfTurnDamage
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
private int _turns;
|
||||
private readonly float _modifier;
|
||||
|
||||
public FireSpinEffect(IPokemon owner)
|
||||
public FireSpinEffect(IPokemon owner, int turns, IPokemon user)
|
||||
{
|
||||
_owner = owner;
|
||||
_turns = turns;
|
||||
_modifier = user.HasHeldItem("binding_band") ? 6f : 8f;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
_owner.Damage(_owner.BoostedStats.Hp / 8, DamageSource.Misc);
|
||||
_owner.Damage((uint)(_owner.BoostedStats.Hp / _modifier), DamageSource.Misc);
|
||||
_turns--;
|
||||
if (_turns == 0)
|
||||
RemoveSelf();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PreventSelfRunAway(IFleeChoice choice, ref bool prevent) => prevent = true;
|
||||
public void PreventSelfRunAway(IFleeChoice choice, ref bool prevent)
|
||||
{
|
||||
if (choice.User.Types.All(x => x.Name != "ghost"))
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent) => prevent = true;
|
||||
public void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent)
|
||||
{
|
||||
if (choice.User.Types.All(x => x.Name != "ghost"))
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ExpectedEndOfTurnDamage(IPokemon pokemon, ref int damage)
|
||||
{
|
||||
damage += (int)(pokemon.MaxHealth / 8f);
|
||||
damage += (int)(pokemon.MaxHealth / _modifier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "increased_critical_stage")]
|
||||
public class FocusEnergyEffect : Script, IScriptChangeCriticalStage
|
||||
{
|
||||
public void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage)
|
||||
{
|
||||
if (stage == byte.MaxValue)
|
||||
{
|
||||
move.GetHitData(target, hit).Fail();
|
||||
return;
|
||||
}
|
||||
stage = (byte)Math.Min(stage + 2, byte.MaxValue);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "focus_punch")]
|
||||
@@ -8,6 +10,8 @@ public class FocusPunchEffect : Script, IScriptOnIncomingHit
|
||||
/// <inheritdoc />
|
||||
public void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit)
|
||||
{
|
||||
if (move.UseMove.SecondaryEffect?.Name == "one_hit_ko")
|
||||
return;
|
||||
WasHit = true;
|
||||
target.BattleData?.Battle.EventHook.Invoke(new DialogEvent("focus_punch_lost_focus",
|
||||
new Dictionary<string, object>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
/// <summary>
|
||||
/// Simple marker script to indicate forests curse or trick-or-treat has been used,
|
||||
/// </summary>
|
||||
[Script(ScriptCategory.Pokemon, "has_had_type_added_effect")]
|
||||
public class HasHadTypeAddedEffect : Script
|
||||
{
|
||||
public TypeIdentifier TypeIdentifier { get; }
|
||||
|
||||
public HasHadTypeAddedEffect(TypeIdentifier typeIdentifier)
|
||||
{
|
||||
TypeIdentifier = typeIdentifier;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,19 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "helping_hand")]
|
||||
public class HelpingHandEffect : Script, IScriptChangeBasePower, IScriptOnEndTurn
|
||||
public class HelpingHandEffect : Script, IScriptChangeBasePower, IScriptOnEndTurn, IScriptStack
|
||||
{
|
||||
private int _stacks = 1;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower) =>
|
||||
basePower = basePower.MultiplyOrMax(1.5f);
|
||||
basePower = basePower.MultiplyOrMax((float)Math.Pow(1.5f, _stacks));
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle) => RemoveSelf();
|
||||
|
||||
public void Stack()
|
||||
{
|
||||
_stacks++;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ public class ImprisonEffect : Script, IScriptPreventMoveSelection
|
||||
/// <inheritdoc />
|
||||
public void PreventMoveSelection(IMoveChoice choice, ref bool prevent)
|
||||
{
|
||||
if (choice.User.BattleData?.BattleSide == _user.BattleData?.BattleSide)
|
||||
return;
|
||||
|
||||
if (_user.Moves.WhereNotNull().Any(x => x.MoveData.Name == choice.ChosenMove.MoveData.Name))
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ public class InfestationEffect : Script, IScriptOnEndTurn, IScriptPreventSelfSwi
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
private int _turns;
|
||||
private readonly float _bindDamage;
|
||||
|
||||
public InfestationEffect(IPokemon owner, int turns)
|
||||
public InfestationEffect(IPokemon owner, int turns, float bindDamage)
|
||||
{
|
||||
_owner = owner;
|
||||
_turns = turns;
|
||||
_bindDamage = bindDamage;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -22,8 +24,8 @@ public class InfestationEffect : Script, IScriptOnEndTurn, IScriptPreventSelfSwi
|
||||
/// <inheritdoc />
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
var damage = _owner.BoostedStats.Hp / 8;
|
||||
_owner.Damage(damage, DamageSource.Misc);
|
||||
var damage = _owner.BoostedStats.Hp * _bindDamage;
|
||||
_owner.Damage((uint)damage, DamageSource.Misc);
|
||||
|
||||
_turns--;
|
||||
if (_turns <= 0)
|
||||
@@ -35,6 +37,6 @@ public class InfestationEffect : Script, IScriptOnEndTurn, IScriptPreventSelfSwi
|
||||
/// <inheritdoc />
|
||||
public void ExpectedEndOfTurnDamage(IPokemon pokemon, ref int damage)
|
||||
{
|
||||
damage += (int)(_owner.MaxHealth / 8f);
|
||||
damage += (int)(_owner.MaxHealth * _bindDamage);
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,18 @@ public class IngrainEffect : Script, IScriptFailIncomingMove, IScriptOnEndTurn,
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent) => prevent = true;
|
||||
public void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent)
|
||||
{
|
||||
if (choice.User.Types.All(x => x.Name != "ghost"))
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PreventSelfRunAway(IFleeChoice choice, ref bool prevent) => prevent = true;
|
||||
public void PreventSelfRunAway(IFleeChoice choice, ref bool prevent)
|
||||
{
|
||||
if (choice.User.Types.All(x => x.Name != "ghost"))
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
|
||||
@@ -3,16 +3,19 @@ using PkmnLib.Static.Moves;
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "kings_shield")]
|
||||
public class KingsShield : ProtectionEffectScript
|
||||
public class KingsShieldEffect : ProtectionEffectScript
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
|
||||
{
|
||||
// Kings Shield doesnt protect from status moves
|
||||
if (executingMove.UseMove.Category == MoveCategory.Status)
|
||||
return;
|
||||
|
||||
base.BlockIncomingHit(executingMove, target, hitIndex, ref block);
|
||||
if (executingMove.UseMove.Category != MoveCategory.Status &&
|
||||
executingMove.GetHitData(target, hitIndex).IsContact)
|
||||
if (block && executingMove.GetHitData(target, hitIndex).IsContact)
|
||||
{
|
||||
executingMove.User.ChangeStatBoost(Statistic.Accuracy, -2, false, false);
|
||||
executingMove.User.ChangeStatBoost(Statistic.Attack, -2, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,21 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "laser_focus")]
|
||||
public class LaserFocusEffect : Script, IScriptChangeCriticalStage
|
||||
public class LaserFocusEffect : Script, IScriptChangeCriticalStage, IScriptOnEndTurn
|
||||
{
|
||||
private bool _hasHadEndTurn;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage)
|
||||
{
|
||||
stage = 100;
|
||||
RemoveSelf();
|
||||
}
|
||||
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
if (_hasHadEndTurn)
|
||||
RemoveSelf();
|
||||
else
|
||||
_hasHadEndTurn = true;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "lock_on")]
|
||||
public class LockOnEffect : Script, IScriptOnEndTurn, IScriptChangeAccuracy
|
||||
public class LockOnEffect : Script, IScriptOnEndTurn, IScriptChangeIncomingAccuracy
|
||||
{
|
||||
private readonly IPokemon _placer;
|
||||
private bool _hasHadEndTurn;
|
||||
|
||||
public LockOnEffect(IPokemon placer)
|
||||
{
|
||||
_placer = placer;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref int modifiedAccuracy)
|
||||
public void ChangeIncomingAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex,
|
||||
ref int modifiedAccuracy)
|
||||
{
|
||||
if (_placer != target)
|
||||
if (_placer != executingMove.User)
|
||||
return;
|
||||
modifiedAccuracy = 255;
|
||||
}
|
||||
@@ -21,6 +22,9 @@ public class LockOnEffect : Script, IScriptOnEndTurn, IScriptChangeAccuracy
|
||||
/// <inheritdoc />
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
RemoveSelf();
|
||||
if (_hasHadEndTurn)
|
||||
RemoveSelf();
|
||||
else
|
||||
_hasHadEndTurn = true;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Pokemon, "lucky_chant")]
|
||||
public class LuckyChantEffect : Script, IScriptBlockCriticalHit, IScriptOnEndTurn
|
||||
{
|
||||
private int _turnsLeft = 5;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void BlockCriticalHit(IExecutingMove move, IPokemon target, byte hit, ref bool block)
|
||||
{
|
||||
block = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
_turnsLeft--;
|
||||
if (_turnsLeft > 0)
|
||||
return;
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
@@ -5,27 +5,45 @@ public class MagmaStormEffect : Script, IScriptOnEndTurn, IScriptPreventSelfRunA
|
||||
IAIInfoScriptExpectedEndOfTurnDamage
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
private int _turns;
|
||||
private readonly float _percentOfMaxHealth;
|
||||
|
||||
public MagmaStormEffect(IPokemon owner)
|
||||
public MagmaStormEffect(IPokemon owner, int turns, float percentOfMaxHealth)
|
||||
{
|
||||
_owner = owner;
|
||||
_turns = turns;
|
||||
_percentOfMaxHealth = percentOfMaxHealth;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
_owner.Damage(_owner.BoostedStats.Hp / 16, DamageSource.Misc);
|
||||
if (_turns > 0)
|
||||
{
|
||||
_turns--;
|
||||
_owner.Damage((uint)(_owner.MaxHealth * _percentOfMaxHealth), DamageSource.Misc);
|
||||
}
|
||||
if (_turns <= 0)
|
||||
RemoveSelf();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PreventSelfRunAway(IFleeChoice choice, ref bool prevent) => prevent = true;
|
||||
public void PreventSelfRunAway(IFleeChoice choice, ref bool prevent)
|
||||
{
|
||||
if (choice.User.Types.All(x => x.Name != "ghost"))
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent) => prevent = true;
|
||||
public void PreventSelfSwitch(ISwitchChoice choice, ref bool prevent)
|
||||
{
|
||||
if (choice.User.Types.All(x => x.Name != "ghost"))
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ExpectedEndOfTurnDamage(IPokemon pokemon, ref int damage)
|
||||
{
|
||||
damage += (int)(pokemon.MaxHealth / 16f);
|
||||
damage += (int)(pokemon.MaxHealth * _percentOfMaxHealth);
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,14 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
[Script(ScriptCategory.Pokemon, "magnet_rise")]
|
||||
public class MagnetRiseEffect : Script, IScriptChangeEffectiveness, IScriptOnEndTurn
|
||||
{
|
||||
private int _turnsRemaining = 5;
|
||||
private int _turnsRemaining = 4;
|
||||
private static readonly StringKey IronBallName = "iron_ball";
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeEffectiveness(IExecutingMove move, IPokemon target, byte hit, ref float effectiveness)
|
||||
{
|
||||
if (move.User.HasHeldItem(IronBallName))
|
||||
return;
|
||||
if (move.UseMove.MoveType.Name == "ground")
|
||||
{
|
||||
effectiveness = 0.0f;
|
||||
|
||||
@@ -7,6 +7,9 @@ public class PhantomForceCharge : Script, IScriptForceTurnSelection, IScriptBloc
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsSemiInvulnerableTurn => true;
|
||||
|
||||
public PhantomForceCharge(IPokemon owner)
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
@@ -7,6 +7,9 @@ public class ShadowForceCharge : Script, IScriptForceTurnSelection, IScriptBlock
|
||||
{
|
||||
private readonly IPokemon _owner;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsSemiInvulnerableTurn => true;
|
||||
|
||||
public ShadowForceCharge(IPokemon owner)
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
42
Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/FollowMeEffect.cs
Normal file
42
Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/FollowMeEffect.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
[Script(ScriptCategory.Side, "follow_me")]
|
||||
public class FollowMeEffect : Script, IScriptChangeIncomingTargets, IScriptOnEndTurn, IScriptOnFaint, IScriptOnSwitchOut
|
||||
{
|
||||
private readonly IPokemon _user;
|
||||
|
||||
public FollowMeEffect(IPokemon user)
|
||||
{
|
||||
_user = user;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeIncomingTargets(IMoveChoice moveChoice, ref IReadOnlyList<IPokemon?> targets)
|
||||
{
|
||||
if (targets.Count != 1)
|
||||
return;
|
||||
if (moveChoice.ChosenMove.MoveData.HasFlag(MoveFlags.NoRedirection))
|
||||
return;
|
||||
|
||||
if (moveChoice.User.BattleData?.SideIndex == _user.BattleData?.SideIndex)
|
||||
return;
|
||||
targets = [_user];
|
||||
}
|
||||
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
|
||||
public void OnFaint(IPokemon pokemon, DamageSource source)
|
||||
{
|
||||
if (pokemon == _user)
|
||||
RemoveSelf();
|
||||
}
|
||||
|
||||
public void OnSwitchOut(IPokemon oldPokemon, byte position)
|
||||
{
|
||||
if (oldPokemon == _user)
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
21
Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/LuckyChantEffect.cs
Normal file
21
Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/LuckyChantEffect.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
[Script(ScriptCategory.Side, "lucky_chant")]
|
||||
public class LuckyChantEffect : Script, IScriptBlockIncomingCriticalHit, IScriptOnEndTurn
|
||||
{
|
||||
private int _turnsLeft = 5;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
_turnsLeft--;
|
||||
if (_turnsLeft > 0)
|
||||
return;
|
||||
RemoveSelf();
|
||||
}
|
||||
|
||||
public void BlockIncomingCriticalHit(IExecutingMove move, IPokemon target, byte hit, ref bool block)
|
||||
{
|
||||
block = true;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,24 @@
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
|
||||
[Script(ScriptCategory.Side, "mist")]
|
||||
public class MistEffect : Script, IScriptPreventStatBoostChange
|
||||
public class MistEffect : Script, IScriptPreventStatBoostChange, IScriptOnEndTurn
|
||||
{
|
||||
private int _turnsRemaining = 5;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PreventStatBoostChange(IPokemon target, Statistic stat, sbyte amount, bool selfInflicted,
|
||||
ref bool prevent)
|
||||
{
|
||||
if (selfInflicted)
|
||||
return;
|
||||
if (amount < 0)
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
public void OnEndTurn(IScriptSource owner, IBattle battle)
|
||||
{
|
||||
_turnsRemaining--;
|
||||
if (_turnsRemaining == 0)
|
||||
RemoveSelf();
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Side;
|
||||
[Script(ScriptCategory.Side, "rainbow_effect")]
|
||||
public class RainbowEffect : Script, IScriptChangeEffectChance, IScriptOnEndTurn
|
||||
{
|
||||
private int _turns = 5;
|
||||
private int _turns = 4;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ChangeEffectChance(IExecutingMove move, IPokemon target, byte hit, ref float chance)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Scripts.Terrain;
|
||||
|
||||
[Script(ScriptCategory.Terrain, "misty_terrain")]
|
||||
public class MistyTerrain : Script, IScriptChangeBasePower, IScriptPreventStatusChange
|
||||
public class MistyTerrain : Script, IScriptChangeBasePower, IScriptPreventStatusChange, IScriptPreventVolatileAdd
|
||||
{
|
||||
private static bool IsAffectedByTerrain(IPokemon pokemon) =>
|
||||
!pokemon.IsFloating;
|
||||
@@ -24,4 +26,10 @@ public class MistyTerrain : Script, IScriptChangeBasePower, IScriptPreventStatus
|
||||
return;
|
||||
preventStatus = true;
|
||||
}
|
||||
|
||||
public void PreventVolatileAdd(IScriptSource parent, Script script, ref bool preventVolatileAdd)
|
||||
{
|
||||
if (script.Name == ScriptUtils.ResolveName<Confusion>())
|
||||
preventVolatileAdd = true;
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ public static class CopyableMoves
|
||||
"follow_me",
|
||||
"helping_hand",
|
||||
"hold_hands",
|
||||
"instruct",
|
||||
"kings_shield",
|
||||
"mat_block",
|
||||
"me_first",
|
||||
@@ -65,4 +66,66 @@ public static class CopyableMoves
|
||||
"trick",
|
||||
"whirlwind",
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Validates if a move can be selected by Metronome.
|
||||
/// </summary>
|
||||
public static bool CanBeSelectedByMetronome(this IMoveData move) =>
|
||||
!MetronomeUnselectable.Contains(move.Name);
|
||||
|
||||
/// <summary>
|
||||
/// The names of moves that cannot be selected by Metronome. This differs from <see cref="NonCopyable"/>:
|
||||
/// Metronome additionally cannot select moves such as After You and Wide Guard, while moves that are only
|
||||
/// excluded for other copying moves (such as semi-invulnerable and forced-switch moves) remain selectable.
|
||||
/// </summary>
|
||||
private static readonly HashSet<StringKey> MetronomeUnselectable =
|
||||
[
|
||||
"after_you",
|
||||
"assist",
|
||||
"baneful_bunker",
|
||||
"beak_blast",
|
||||
"belch",
|
||||
"bestow",
|
||||
"celebrate",
|
||||
"chatter",
|
||||
"copycat",
|
||||
"counter",
|
||||
"covet",
|
||||
"crafty_shield",
|
||||
"destiny_bond",
|
||||
"detect",
|
||||
"endure",
|
||||
"feint",
|
||||
"focus_punch",
|
||||
"follow_me",
|
||||
"helping_hand",
|
||||
"hold_hands",
|
||||
"instruct",
|
||||
"kings_shield",
|
||||
"mat_block",
|
||||
"me_first",
|
||||
"metronome",
|
||||
"mimic",
|
||||
"mirror_coat",
|
||||
"mirror_move",
|
||||
"nature_power",
|
||||
"protect",
|
||||
"quash",
|
||||
"quick_guard",
|
||||
"rage_powder",
|
||||
"shell_trap",
|
||||
"sketch",
|
||||
"sleep_talk",
|
||||
"snatch",
|
||||
"snore",
|
||||
"spectral_thief",
|
||||
"spiky_shield",
|
||||
"spotlight",
|
||||
"struggle",
|
||||
"switcheroo",
|
||||
"thief",
|
||||
"transform",
|
||||
"trick",
|
||||
"wide_guard",
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user